www.gmapsapi.com

Kompleksowy kurs podstaw API, po którym mapowianie nie będzie miało przed Tobą żadnych tajemnic!

Setki przykładów, kursów i poradników z kodem gotowym do skopiowania i korzystania.

Największa strona o Google Maps API w Polsce, największe źródło informacji w języku polskim.

Edycja danych na mapie i zapis do bazy

Ten artykuł dotyczy API w wersji 2

« powrót do listy poradników

Często poszukiwanym przez internautów rozwiązaniem jest umożliwienie użytkownikowi edycji pewneych markerów, a następnie trwałe zapisanie efektów pracy z powrotem do bazy. Ta funkcjonalność może być wykorzystywana, gdy np. mamy bazę lokalizacji, ale chcemy dać użytkownikom możliwość nanoszenia poprawek bezpośrednio na mapie, a następnie ich zapisu bez konieczności ingerencji administratora.

Ten przykład pokazuje realizację głównego zadania, tj. wczytania markerów z bazy, ich edycji oraz zapisu. Sam będziesz musiał natomiast zadbać o weryfikację użytkownika (najczęściej będzie to logowanie).

Przygotowanie

Wykorzystam bazę, użytą w tutorialu Pobieranie danych z bazy MySql. Struktura oraz przykładowe dane:

ID będzie jednoznacznie identyfikować wpis, a pozostałe atrybuty odpowiadają za (kolejno): nazwę markera, długość, szerokość geograficzną oraz URL ikony.

Mapa i formularz edycji

Obok mapy wyświetlimy formularz edycji:

<table style="border: 0 collapse;">
	<tr>
		<td style="width: 410px;">
			<div id='mapka' style='width: 400px; height: 350px; border: 1px solid black; background: gray;'></div>
		</td>
		<td style="width: 300px; border: 1px solid black; overflow: auto;">
			<form id="marker_edycja" style="display: none;" onsubmit="alert('zapisano!'); return false;">
				<p><input onkeyup="uaktualnijMarker()" id="lat" type="text" class="wspolrzedna" value="" /><br />długość geograficzna</p>
				<p><input onkeyup="uaktualnijMarker()" id="lng" type="text" class="wspolrzedna" value="" /><br />szerokość geograficzna</p>
				<p><textarea onkeyup="uaktualnijMarker()" id="opis" rows="9"></textarea><br />opis markera</p>
			</form>
		</td>
	</tr>
</table>
    Formularz na starcie ma być niewidoczny, jako że nie został jeszcze wybrany żadny marker do edycji. Odpowiada za to ustawienie atrybutu display na none.

  • Każde z pól ze współrzędnymi oraz z opisem posiada atrybut onkeyup="uaktualnijMarker()". Oznacza to, że po wpisaniu dowolnego znaku do któregoś z tych pól zostanie wywołana funkcja uaktualnijMarker(). Napiszemy ją później.

Wczytanie zestawu markerów

Wczytanie odbędzie się analogicznie jak w tutorialu 21, przy czym w tym wypadku do danych w XML musimy dodać również ID wpisu, które wykorzystamy później przy zapisie powrotnym do bazy. Taki skrypt w pliku wczytaj.php może wyglądać następująco:

<?
header('Content-Type: text/xml; charset=utf-8');
echo '<?xml version="1.0" encoding="UTF-8"?>';

$user="nazwa_uzytkownika";
$pass="haslo_uzytkownika";
$baza="nazwa_bazy";

mysql_connect ("localhost", $user, $pass) or 
	die ("Nie nawiązano połączenie z bazą MySQL");
mysql_select_db ($baza) or 
	die ("Nie nawiązano połączenia z bazą serwisu.");
	
mysql_query("SET NAMES 'UTF8';");

$zapytanie = "SELECT id,nazwa,lat,lng,flaga FROM PoznajGoogleMaps_panstwa ORDER BY id";
$pobierz = mysql_query($zapytanie);

print "\n<dane>\n";
$i=0;
while($dane = mysql_fetch_array($pobierz))
{
	$i++;
	printf("\t<marker id=\"%d\" szerokosc=\"%f\" dlugosc=\"%f\" ikona=\"%s\" nazwa=\"%s\" />\n",$dane['id'],$dane['lat'],$dane['lng'],$dane['flaga'],$dane['nazwa']);
}
print "</dane>";
?>

Taki skrypt wygeneruje XML o następującej zawartości:

<dane>
<marker id="1" szerokosc="62.021528" dlugosc="16.699219" ikona="http://gmapsapi.com/ikony/szwecja.png" nazwa="Szwecja"/>
<marker id="2" szerokosc="61.438767" dlugosc="7.910156" ikona="http://gmapsapi.com/ikony/norwegia.png" nazwa="Norwegia"/>
<marker id="3" szerokosc="63.233627" dlugosc="26.894531" ikona="http://gmapsapi.com/ikony/finlandia.png" nazwa="Finlandia"/>
<marker id="4" szerokosc="52.052490" dlugosc="19.863281" ikona="http://gmapsapi.com/ikony/polska.png" nazwa="Polska"/>
<marker id="5" szerokosc="50.847573" dlugosc="10.019531" ikona="http://gmapsapi.com/ikony/niemcy.png" nazwa="Niemcy"/>
<marker id="6" szerokosc="55.012934" dlugosc="-3.446868" ikona="http://gmapsapi.com/ikony/uk.png" nazwa="Wielka Brytania"/>
<marker id="7" szerokosc="47.754098" dlugosc="2.109375" ikona="http://gmapsapi.com/ikony/francja.png" nazwa="Francja"/>
<marker id="8" szerokosc="39.707187" dlugosc="-6.064453" ikona="http://gmapsapi.com/ikony/hiszpania.png" nazwa="Hiszpania"/>
<marker id="9" szerokosc="41.902277" dlugosc="14.765625" ikona="http://gmapsapi.com/ikony/wlochy.png" nazwa="Włochy"/>
<marker id="10" szerokosc="48.382802" dlugosc="31.174610" ikona="http://gmapsapi.com/ikony/ukraina.png" nazwa="Ukraina"/>
<marker id="11" szerokosc="57.704147" dlugosc="37.792969" ikona="http://gmapsapi.com/ikony/rosja.png" nazwa="Rosja"/>
</dane>

Teraz przechodzimy do skryptu JavaScript, parsującego te dane i wstawiającego markery na mapę:

function wczytajMarkery()
{
	GDownloadUrl('/examples/051/wczytaj.php', function(dane,kodOdpowiedzi)
	{
		if(kodOdpowiedzi==200)
		{
			var xml = GXml.parse(dane);
			var markery = xml.documentElement.getElementsByTagName("marker");
			for(var i=0; i<markery.length; i++)
			{
				var id			=	parseInt(markery[i].getAttribute("id"));
				var dlugosc		=	parseFloat(markery[i].getAttribute("dlugosc"));
				var szerokosc	=	parseFloat(markery[i].getAttribute("szerokosc"));
				var ikona_url	=	markery[i].getAttribute("ikona");
				var nazwa		=	markery[i].getAttribute("nazwa");
				var marker		=	dodajMarker(szerokosc,dlugosc,ikona_url,nazwa,id);
			}
			alert('Wczytano '+markery.length+' markerów z bazy');
		}
		else
		{
			alert('Nie mogłem otworzyć pliku dane.php');
		}
	});
}

Funkcja dodajMarker() wstawia markery na mapie na podstawie przekazanych danych:

function dodajMarker(lat,lng,ikona_url,nazwa,id)
{

	var ikona = new GIcon();
	ikona.image = ikona_url;
	ikona.iconSize = new GSize(30, 23);
	ikona.iconAnchor = new GPoint(15, 12);
	ikona.shadow = "";
	ikona.infoWindowAnchor = new GPoint(15,12);
	
	var marker	=	new GMarker(new GLatLng(lat,lng),{icon: ikona,draggable:true});
	marker.txt	=	nazwa;
	marker.id	=	id;
	marker.zmiana = false;
	return marker;
}

Każdemu markerowi nadajemy właściwosci:

  • id: id wpisu w bazie danych
  • txt: nazwa markera
  • zmiana: wartość logiczna (true/false) określająca, czy któryś z parametrów markera został zmieniony przez użytkownika. Przy tworzeniu markera ustawiamy ją na false

Warto zwrócić uwagę, że poprzez ustawienie wartości draggable: true marker będzie mógł być przeciągany za pomocą myszy.

Serce aplikacji

Nim przejdziemy do kodu, omówimy kilka założeń dalszego działania. Naraz będzie można edytować jeden marker. W celu edycji, użytkownik będzie musiał na niego kliknąć, lub go przesunąć. Aktualnie edytowany marker będzie pamiętany poprzez odwołanie w globalnej zmiennej aktywny. Po aktywowaniu markera, odpowiednie pola formularza zostaną uaktualnione rzeczywistymi danymi markera. W trakcie przesuwania markera za pomocą myszy pola ze współrzędnymi również będą dynamicznie odświeżane. Zmiana współrzędnych lub opisu z poziomu klawiatury będzie na bieżąco powodować uaktualnianie pozycji/nazwy markera. Każdy marker ma przypisaną własność zmiana, informującą skrypt o tym, czy był on zmieniany. W momencie wywołania funkcji wykonany zostanie po stronie serwera plik PHP, do którego przekazane zostaną parametry tylko zmienionych markerów, po czym nastąpi wywołanie zapytania uaktualniającego dane w bazie danych.

Kliknięcie na marker lub jego przesunięcie mają powodować aktywację markera do edycji. Trzeba więc nieco zmodyfikować funkcję dodajMarker(), dodając pod koniec (przed return marker;) taki kod:

GEvent.addListener(marker,"click",function()
{
	marker.openInfoWindowHtml(marker.txt);
	ustawAktywnyMarker(marker);
});

GEvent.addListener(marker,'dragstart',function()
{
	ustawAktywnyMarker(marker);
	marker.closeInfoWindow();
});

GEvent.addListener(marker,'drag',function()
{
	document.getElementById('lat').value = marker.getPoint().lat();
	document.getElementById('lng').value = marker.getPoint().lng();
	
	marker.zmiana = true; // marker był zmieniany, zapamiętujemy ten fakt, by wykonać odpowiednie zapytanie uaktualniające bazę
	
});
markery.push(marker);
  • W przedostatniej linii konieczne jest dodanie markera do pewnej globalnej tablicy z markerami, którą wcześniej trzeba utworzyć. Tablica ta będzie używana podczas zapisywania z powrotem do bazy.
  • Zwróć uwagę, że podczas przeciągania markera (zdarzenie drag) uaktualniamy na bieżąco wartości w polach ze współrzędnymi.
  • Funkcja ustawAktywnyMarker() odpowiada za zapisanie odwołania do markera w globalnej zmiennej aktywny, oraz uzupełnieniu formularza danymi markera. Jej kod:
function ustawAktywnyMarker(marker)
{
	if(marker) // jeżeli wybrano marker
	{
		aktywny = marker; // zapamiętujemy odwołanie
		
		// uaktualniamy pola w formularzu danymi markera
		document.getElementById('marker_edycja').style.display = 'block';
		document.getElementById('lat').value = marker.getPoint().lat();
		document.getElementById('lng').value = marker.getPoint().lng();
		document.getElementById('opis').value = marker.txt;
	}
	else
	{
		aktywny = null;
		document.getElementById('marker_edycja').style.display = 'none';
	}
}

W tym momencie nasza aplikacja powinna już poprawnie wyświetlać dane w formularzu, oraz odświeżać je w czasie przesuwania markera. Teraz czas na akcję w drugą stronę, to znaczy uaktualnienie formularza powinno skutkować przesunięciem/zmianą opisu markera.

Jak pamiętasz, atrybuty onkeyup w polach formularza posiadały wartość mówiącą o tym, że po zmianie zawartości pola ma zostać wykonana funkcja uaktualnijMarker(). Oto i ona:

function uaktualnijMarker()
{
	if(!aktywny) // jeżeli żaden marker nie jest aktywny, to nie robimy nic
		return;
		
	// nowe parametry, pobrane z formularza:
	var nowaLat = parseFloat(document.getElementById('lat').value);
	var nowaLng = parseFloat(document.getElementById('lng').value);
	var nowyPunkt = new GLatLng(nowaLat,nowaLng);	
	var nowyOpis = document.getElementById('opis').value;
	
	// sprawdzamy, co się zmieniło
	if(!aktywny.getPoint().equals(nowyPunkt))
		aktywny.setPoint(nowyPunkt);
	if(aktywny.txt!=nowyOpis)
		aktywny.txt = nowyOpis;

	// jeżeli było otwarte okno infoWindow, to otwieramy je ponownie, by uwzględnić wprowadzone zmiany
	if(!mapa.getInfoWindow().isHidden())
		aktywny.openInfoWindowHtml(aktywny.txt);
	
	aktywny.zmiana = true; // marker był zmieniany, zapamiętujemy ten fakt, by wykonać odpowiednie zapytanie uaktualniające bazę
}

W liniach 7-11 pobierane są nowe dane z formularza. W liniach 14 oraz 16 są one porównywane do obecnych wartości, a jeśli zachodzi potrzeba (wartości są różne) to są uaktualniane. Na koniec sprawdzamy, czy otwarte jest okienko infoWindow, i jeśli tak to otwieramy je ponownie, by uwzględnić wprowadzone zmiany. Na końcu oczywiście ustawiamy też wartość własności zmiana na true, dzięki temu wiadomo, że marker był zmieniany.

Teraz aplikacja działa już poprawnie, użytkownik może edytować pozycje i opisy markerów, a efekty pracy widoczne są na bieżąco.

Zapisanie danych z powrotem do bazy

Ostatnim zadaniem jest zapisanie z powrotem markerów do bazy. Udostępnimy użytkownikowi specjalny link, wywołujący funkcją zapiszMarkery():

function zapiszMarkery()
{
	var t = '';
	for(var i=0; i<markery.length; i++)
	{
		if(markery[i].zmiana == true)
		{
			if(t.length!=0)
				t+='@@'; // znak oddzielenia markerów
				
			// zakładamy, że parametry markera oddzielone są separatorem |
			t+=markery[i].id+'|'+markery[i].txt.replace("|",' ')+'|'+markery[i].getPoint().lat()+'|'+markery[i].getPoint().lng();
		}
	}
	
	// 
	GDownloadUrl('/examples/051/zapisz.php?dane='+t,function(dane,kodOdpowiedzi)
	{
		GLog.writeHtml(dane);
	},true);
}

W pętli przechodzimy przez wszystkie markery na mapie, i dla każdego ze zmienianych (własność zmiana = true) tworzymy specjalny ciąg, składający się z:

  • id wpisu
  • opisu
  • długości
  • szerokości geograficznej

oddzielonych znakiem |. Zwróć uwagę, że w przypadku opisu zamieniamy każde wystąpienie | na spację, by nie otrzymywać błędów, gdy opis zawiera taki znak. Tak stworzony ciąg odpowiada za dane jednego markera, a kilka ciągów oddzielamy znakiem @@. Dane markera zapisane w postaci jednego stringa są potrzebne, ponieważ w takiej postaci zostaną przekazane do skryptu PHP w postaci np:

/examples/051/zapisz.php?dane=1|Szwecja|62.02152819100765|16.69921875@@3|Finlandia|63.23362741232568|26.89453125

Taki plik pobieramy funkcją GDownloadURL(). Należy zadbać o to, by skrypt PHP odczytał dane, zapisane w postaci takiego stringa, a następnie wykonał odpowiednie zapytanie do bazy danych. W przykładzie tym zapytanie nie zostaje faktycznie wywołane w celach bezpieczeństwa, ale poniższy skrypt może Ci pomóc w opracowaniu własnego:

<?
header('Content-Type: text/html; charset=utf-8');

$dane = $_GET['dane'];
if(!$dane)
	exit;
$markery = split('@@',$dane);

$zapytanie = "UPDATE PoznajGoogleMaps_panstwa SET nazwa = '%s', lat = %f, lng = %f WHERE id = %d;<br />";
$html = sprintf('Dane zapisanych markerów (%d) zostały prawidłowo przechwycone przez skrypt. Oto zapytanie SQL, jakie powinno zostać wykonane:<br /><br />',count($markery));

for($i=0; $i<count($markery); $i++)
{
	$dane_markera = split("\|",$markery[$i]);
	$id = (int) $dane_markera[0];
	$opis = addslashes(strip_tags($dane_markera[1]));
	$lat = (float) $dane_markera[2];
	$lng = (float) $dane_markera[3];
	$html.= sprintf($zapytanie,$opis,$lat,$lng,$id);
}

print $html;
?>

Skrypt, zapisany w pliku zapisz.php generuje treść zapytania SQL, ale nie wykonuje go lecz drukuje. W funkcji zapiszMarkery() przedostatni listing zawiera kod, odpowiedzialny za wyświetlenie generowanej przez plik zawartości, w celu udowodnienia, że dane zostały przekazane do skryptu, i że możliwe jest ich zapisanie. Oto przykładowy skrypt, który faktycznie wykonuje zapytanie SQL:

<?
header('Content-Type: text/html; charset=utf-8');

$user="nazwa_uzytkownika";
$pass="haslo_uzytkownika";
$baza="nazwa_bazy";

mysql_connect ("localhost", $user, $pass) or 
	die ("Nie nawiązano połączenie z bazą MySQL");
mysql_select_db ($baza) or 
	die ("Nie nawiązano połączenia z bazą serwisu.");
	
mysql_query("SET NAMES 'UTF8';");

$dane = $_GET['dane'];
if(!$dane)
	exit;
$markery = split('@@',$dane);

$zapytanie = "UPDATE PoznajGoogleMaps_panstwa SET nazwa = '%s', lat = %f, lng = %f WHERE id = %d; ";
$query = '';

for($i=0; $i<count($markery); $i++)
{
	$dane_markera = split("\|",$markery[$i]);
	$id = (int) $dane_markera[0];
	$opis = addslashes(strip_tags($dane_markera[1]));
	$lat = (float) $dane_markera[2];
	$lng = (float) $dane_markera[3];
	$query.= sprintf($zapytanie,$opis,$lat,$lng,$id);
}

mysql_query($query);
?>

Zakończenie

I to koniec tego względnie skomplikowanego zagadnienia. Oprócz kodu JS, który możesz pobrać w całości z przykładu 1 potrzebny Ci są jeszcze dwa pliki PHP - zapisz.php i wczytaj.php (ich kod znajdziesz na łamach tego poradnika), oraz odpowiednia tabela w bazie MySql. Efekt jednak jest wart zachodu - sprawdź sam: przykład 1pokaż kod przykładu

Polecane artykuły

Dodaj stronę do ulubionego serwisu społecznościowego

Oto, co najczęściej czytają internauci, którzy przeczytali ten artykuł:

Dodawanie markerów przez użytkownika

API v2

Poradnik pokazuje, w jaki sposób stworzyć formularz, pozwalający na dodawanie markerów


Wczytywanie danych z pliku XML

API v2

Kurs podstaw cz. X: Omówienie wczytywania danych z pliku XML za pomocą AJAXa


Pobieranie danych z bazy MySql

API v2

Zobacz, jak pobrać dane markerów, zawarte w bazie MySql i wyświetlić je na mapie


Wyszukiwanie markerów as-you-type

API v2

Jak stworzyć wyszukiwanie i filtrowanie markerów as-you-type przy użyciu bazy danych