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.

Zaznaczanie obszaru przez użytkownika

Ten artykuł dotyczy API w wersji 2

« powrót do listy poradników

W Twojej aplikacji możesz niejednokrotnie wymagać od użytkownika zaznaczenia pewnego obszaru mapy za pomocą myszy. Dzięki temu możesz wyświetlić jedynie markery z określonego przedziału lub wykonać inne akcje. W tej części kursu poznasz sposób realizacji zaznaczania prostokątnego obszaru za pomocą myszy, wraz z przykładowym zastosowaniem.

Założenia

Zaznaczanie obszaru prostokątnego można stworzyć przy użyciu polilinii, ale rozwiązanie to jest wolne, szczególnie w przeglądarce Internet Explorer. W tym tutorialu zostanie to rozwiązane w prostszy i szybszy sposób. Sterowanie obszarem zaznaczania polegać będzie na kliknięciu w pierwszym punkcie w celu otwarcia zaznaczenia, i w punkcie drugim w celu jego zakończenia. W trakcie przesuwania myszy użytkownik będzie widział dynamicznie odświeżany prostokąt, pokazujący graficznie zaznaczony obszar.

Najprostsza w użyciu będzie klasa GOverlay(), której działanie przedstawiłem w tym poradniku. Dzięki niej wstawimy na mapę element blokowy div, odpowiednio go przestylujemy, a dzięki wewnętrzym funkcjon API zmiana jego rozmiaru i odświeżanie będzie banalnie proste. Założenia i sposób osiągnięcia celu będzie identyczny jak w tutorialu 19, diabeł jednak tkwi w szczegółach.

Inicjalizacja

Najpierw stworzymy nową klasę o nazwie ZaznaczObszar:

function ZaznaczObszar(punkt1,punkt2)  
{  
    this.punkt1 = punkt1;   
	this.punkt2 = punkt2 || punkt1;
}; 

Konstruktor posiada dwa argumenty - punkty typu GLatLng, które zapisujemy jako własności obiektu tej klasy. Jeśli punkt2 nie został podany, to wartość this.punkt2 ustawiamy na argument punkt1. Te dwa punkty będą określać dwa naprzeciwległe narożniki zaznaczanego prostokątnego obszaru.

Klasa ZaznaczObszar ma dziedziczyć po GOverlay, co też czynimy:

ZaznaczObszar.prototype = new GOverlay();  

ZaznaczObszar.prototype.pobierz = function()
{
	var granica = new GLatLngBounds();
	granica.extend(this.punkt1);
	granica.extend(this.punkt2);
	return granica;
};

Metoda pobierz() będzie zwracać prostokątny obszar GLatLngBounds, określony przez narożniki punkt1 i punkt2. W celu zagwarantowania poprawności obszaru, stopniowo rozszerzamy go (metoda extend() o dwa punkty), a następnie zwracamy.

Teraz należy napisać metodę, dzięki której współrzędne drugiego punktu obszaru będą odświeżane wraz z ruchami myszy:

ZaznaczObszar.prototype.ustawPunktKoncowy = function(punkt)
{
	this.punkt2 = punkt;
	this.redraw(true);
};

Metoda ustawPunktKoncowy() zmieni własność punkt2 na punkt, określony w jedynym przekazywanym argumencie. Następnie wymusi ona przerysowanie zaznaczenia tak, by współrzędne prostokąta na ekranie odpowiadały własnościom zaznaczenia.

Metody initialize, copy, remove

Kolejne kroki są bardzo podobne do tego, co zostało przedstawione w tym poradniku, w związku z czym nie będę ich komentował:

ZaznaczObszar.prototype.initialize = function(mapa)
{
	this.mapa = mapa;
	var kontener = document.createElement('div');
	kontener.style.border = '2px solid red';			
	kontener.style.position = 'absolute';
	this.mapa.getPane(G_MAP_MAP_PANE).appendChild(kontener);
	this.kontener = kontener;
};

ZaznaczObszar.prototype.remove = function()
{
	this.kontener.parentNode.removeChild(this.kontener);
};

ZaznaczObszar.prototype.copy = function()
{
	return new ZaznaczObszar(this.punkt1,this.punkt2);
};

Rysowanie prostokąta

Czas na narysowanie prostokąta - wpierw konwertujemy współrzędne geograficzne na pozycję XY względem górnego, lewego narożnika mapy, używając metody mapa.fromLatLngToDivPixel(). Wyciągamy z każdego z punktów współrzędne x i y, a następnie porównujemy odpowiadające sobie współrzędne i razie potrzeby je zamieniamy. Po tej operacji położenie prostokąta określone będzie współrzędnymi pierwszego punktu, a długość i szerokość prostokąta będzie różnicą odpowiadających sobie wartości. Oto kod:

ZaznaczObszar.prototype.redraw = function(wymus)
{
	if (!wymus || !this.punkt2 || !this.punkt1)
		return;
		
	var x1 = this.mapa.fromLatLngToDivPixel(this.punkt1).x;
	var y1 = this.mapa.fromLatLngToDivPixel(this.punkt1).y;
	var x2 = this.mapa.fromLatLngToDivPixel(this.punkt2).x;
	var y2 = this.mapa.fromLatLngToDivPixel(this.punkt2).y;
	
	if(x1>x2)
	{
		var x_temp = x1;
		x1 = x2;
		x2 = x_temp;
	}
	
	if(y1>y2)
	{
		var y_temp = y1;
		y1 = y2;
		y2 = y_temp;
	}
	
	this.kontener.style.left 	= x1+'px';
	this.kontener.style.top 	= y1+'px';
	this.kontener.style.width	= x2-x1+'px';
	this.kontener.style.height	= y2-y1+'px';
};

Ostatnie szlify

Ostatnie co pozostało to zaprogramowanie odpowiednich zdarzeń przy kliknięciu i przesuwaniu myszy. Oto moja propozycja:

GEvent.addListener(mapa,'mousemove',function(p)
{
	if(!obszar || !obszar.punkt1)
		return;
	obszar.ustawPunktKoncowy(p);
});

Ogólnie mówiąc, gdy pozycja kursora myszy zmieni się to sprawdzamy, czy istnieje obiekt obszar oraz czy posiada odpowiednią właściwość punkt1. Jeśli tak jest, to ustawiamy wartość drugiego punktu za pomocą metody ustawPunktKoncowy().

GEvent.addListener(mapa,'click',function(o,p)
{
	if(!p)
		return;
	if(!obszar)
	{
		obszar = new ZaznaczObszar(p);
		mapa.addOverlay(obszar);
	}
	else
	{
		GLog.write('Obszar: '+obszar.pobierz());
		obszar.remove(); 
		obszar = null;
	}
});

Gdy zostanie kliknięty lewy przycisk myszy sprawdzamy, czy istnieje obiekt obszar. Jeśli nie, to tworzymy go, przekazując jako argument kliknięty punkt (jest on klasy GLatLng), a następnie dodajemy stworzony obiekt na mapę. Jeśli obiekt już istnieje, to wyświetlamy współrzędne zaznaczonego obszaru (GLog.write() zasadniczo służy do debugowania aplikacji, ale jest mniej uciążliwe niż alert(), stąd pozwoliłem sobie użyć tej funkcji w celu zaprezentowania, że zaznaczony obszar jest znany). Następnie usuwamy zaznaczenie i ustawiamy wartość obiekt na null.

Tak napisany przykład da następujący efekt: przykład 1pokaż kod przykładu

Praktyczne zastosowanie

W przykładzie 28.2 pokazałem praktyczne zastosowanie zaznaczania. Do mapy dodane zostało 50 markerów o losowych współrzędnych. Kliknij raz, przesuń mysz w celu przeciągnięcia obiektu i kliknij drugi raz, by zaznaczyć wszystkie markery w danym obszarze.

Kluczowa jest funkcja zaznaczMarkery(), która wygląda następująco:

function zaznaczMarkery(ograniczenie)
{
	var licznik = 0;
	for(var i=0; i<markery.length; i++)
	{
		if(ograniczenie.contains(markery[i].getPoint()))
		{
			licznik ++;
			if(markery[i].ikonka!='/examples/028/on.png')
				markery[i].zmienObraz('/examples/028/on.png');
		}
		else
		{
			if(markery[i].ikonka!='/examples/028/off.png')
				markery[i].zmienObraz('/examples/028/off.png');
		}
	}
	GLog.write('Zaznaczonych jest '+licznik+' markerów');
}

Ta funkcja zmienia obrazki zaznaczonych markerów na aktywne i niezaznaczonych na nieaktywne. Wykorzystuje dwie prototypowane wcześniej metody - najpierw sprawdza, czy prostokątny obszar zawiera punkt zaczepienia markera i jeśli tak, to zmienia jego obrazek na 'on.png' (pod warunkiem, że obecny obrazek jest inny - przydaje się własność ikonka). Analogicznie, jeśli marker jest poza obszarem to też zmieniamy ikonkę, gdy jest to konieczne. Na końcu w celach testowych wyświetlam liczbę zaznaczonych markerów.

Nie omawiam tutaj części kodu, która powinna być po wszystkich poprzednich lekcjach i kursie podstaw jasna i przejrzysta. W razie wątpliwości pełny kod przykładu 2 powinien je rozwiać: przykład 2pokaż kod przykładu

Inne propozycje zastosowań

  • pobieranie współrzędnych zaznaczonego obszaru i ładowanie markerów w nim obecnych za pomocą asynchronicznego zapytania Ajax
  • przybliżanie do zaznaczonego obszaru (na podobnej zasadzie jak w tym poradniku)
  • zliczanie ilości obiektów w danym obszarze
  • inne

Pytania czytelników

  • Chciałbym zaprogramować zaznaczanie obszaru mapy przez użytkownika, reprezentowane przez polilinię. Jak wygląda kwestia wydajności takiego rozwiązania?
    Nie zalecam dynamicznego generowania polilinii - w Internet Explorerze będzie to bardzo powolne, a i w nowoczesnych przeglądarkach wydajność nie będzie tak dobra, jak w przypadku użycia elementu DIV.
  • Jak powinno wyglądać zapytanie do bazy aby pobrać markery na podstawie tych dwóch punktów będacymi kątami prostokąta?
    Mając dane dwa narożniki, należałoby wysłać współrzędne AJAX-em w postaci na przykład ajax.php?lat1=53.4333&lat2=52.3413&lng1=14.25223&lng2=16.12311. Następnie w zapytaniu SELECT należałoby dodać ograniczenia, na przykład: WHERE (lat BETWEEN lat2 AND lat1) AND (lng BETWEEN lng1 AND lng2).

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


Kategorie markerów i polilinii

API v2

Stwórz markery/polilinie w kilku kategoriach, a następnie ukrywaj i pokazuj wybrane kategorie


jQuery i Google Maps

API v2

Przyjemne dla oka animacje za pomocą bilbioteki jQuery uatrakcyjnią każdą mapę!


Wyszukiwanie markerów as-you-type

API v2

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