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.

Własna mapa w Google Maps API

Ten artykuł dotyczy API w wersji 2

« powrót do listy poradników

Google Maps API może być stosowane niezależnie od warstw mapowych, dostarczanych przez Google. Przykładowo, często poszukiwanym poradnikiem przez graczy jest możliwość stworzenia mapy w API, która zamiast prawdziwych dróg i zdjęć wyświetlać będzie na przykład teren gry, i umożliwiać opisanie go za pomocą markerów i innych dobrodziejstw Google Maps. W tym poradniku pokażę, że jest to możliwe!

W tym poradniku będę używał do przycinania i przygotowania plików programu graficznego Corel Photo-Paint - równie dobrze można użyć innego edytora grafiki, zgodnego z własnymi upodobaniami.

Wstępne omówienie

Google Maps nie przechowuje zdjęć satelitarnych w jednym pliku - zamiast tego, wszystkie zdjęcia, na wszystkich poziomach przybliżenia są w rzeczywistości podzielone na małe obrazki o wymiarach 256 na 256 pikseli. Takie wymiary ma obraz, przedstawiający cały świat na najdalszym oddaleniu. Na przybliżeniu o jeden poziom więcej świat reprezentowany jest przez 4 małe fragmenty, każdy o tych samych wymiarach 256 x 256. Na kolejnym poziomie przybliżenia świat jest reprezentowany przez 16 małych fragmentów, również o tych samych wymiarach. Google Maps numeruje obrazki, by wiedzieć, który w danym momencie wyświetlić. Kiedy oglądasz obraz mapy, API przekazuje żądanie przesłania pliku graficznego z pewnego poziomu zoom, z określoną współrzędną X i Y. Współrzędne X i Y są w rzeczywistości liczbami całkowitymi, gdzie 0 to pierwszy element. Oznacza to, że na najdalszym oddaleniu, jedyny kawałek ma współrzędne (0,0). Na kolejnym oddaleniu kawałek w górnym lewym rogu ma współrzędne (0,0), w prawym górnym (1,0), w lewym dolnym (0,1) i w prawym dolnym (1,1). Analogicznie wygląda to na kolejnych poziomach. Oto wygląd podzielonej na kawałki mapy, który będziemy musieli uzyskać:

Sposób numeracji kawałków

Z tego co napisałem wynika jedna bardzo ważna rzecz: nie każdy obraz ma odpowiednie wymiary, by mógł zostać prawidłowo podzielony na kawałki. Przede wszystkim, powinien być kwadratem, a po drugie musi mieć wymiary (w pikselach) 256x256, 512x512, 1024x1024, 2048x2048, 4096x4096 lub dowolne, w których długość boku da się zapisać jako 256*2x, gdzie x to dowolna liczba naturalna. Po odpowiednich obróbkach, mapa którą wybrałem do tego przykładu (wymiary 1750 x 1550) będzie mogła być użyta.

Przygotowanie mapy

Ten krok jest potencjalnie najtrudniejszy ze wszystkich. Wymagany jest po pierwsze obrazek w wysokiej rozdzielczości z mapą, którą chcemy wyświetlać. Im więcej poziomów zoom chcemy udostępnić oraz im głębszy ma być to zoom, tym rozdzielczość mapy powinna być wyższa. Dla celów tego poradnika wybrałem mapę z gry Grand Theft Auto: Vice City:

http://realgta.net/realgta/files/libertycitymap.gif

Jak już wcześniej pisałem, specyfika działania API wymaga, by obraz z mapą był w odpowiedniej rozdzielczości. Mapa, którą wybrałem w tym przykładzie ma rozdzielczość 1750 x 1550. W takim wypadku są dwie opcje:

  1. Przeskalować mapę do jednej z pożądanych rozdzielczości
  2. Rozszerzyć mapę o puste pole, by obrazek miał ostatecznie jedną z pożądanych rozdzielczości

Wadą pierwszego rozwiązania jest utrata jakości, a także niemożność stosowania, gdy obraz nie jest kwadratem (bo wówczas przeskalowanie go spowoduje zniekształcenia). Dla celów omówienia w tym poradniku wybrałem więc opcję mieszaną. Stworzyłem obraz o wymiarach 2048 x 2048, wypełniłem kolorem niebieskim (kolor wody) a następnie wstawiłem na niego oryginalny obraz mapy i rozciągnąłem tak, by zajmował całą szerokość 2048 pikseli. Dzięki temu zachowałem proporcje oryginału, a jednocześnie mam plik o pożądanych wymiarach:

Rozciągnięty obrazek

Kawałkowanie

W tym kroku stworzymy kawałki, by API mogło szybko ładować mapę.

Przygotowany w poprzednim kroku obrazek zapisałem jako Z3.cpt, a jego kopie jako Z2.cpt, Z1.cpt i Z0.cpt. Kopie wykorzystamy później, najpierw dokończymy edycję obrazka Z3.cpt. Będzie on odpowiadał za największe przybliżenie. Obrazek ma wymiary 2048 x 2048, co oznacza, że zmieści się w nim 8 x 8 mniejszych obrazków o wymiarach 256 x 256 px. Wybieramy narzędzie kawałkowania w Corel Photo-Paint, i klikamy na ikonkę "jednakowe kawałki". Następnie podajemy, na ile częsci ma zostać podzielony obrazek - wpisujemy 8 w pionie i 8 w poziomie.

Obrazek zostanie podzielony, musimy go jeszcze tylko wyeksportować. Klikamy w menu na Plik > Eksport dla internetu. Zaznaczamy, że mają być eksportowane jedynie obrazki, i wybieramy katalog eksportu. Po kliknięciu ZAPISZ w wybranym katalogu powinny się pojawić 64 pliki, każdy zawierający wycinek dużej mapy.

Otwieramy plik Z2.cpt. Z menu wybieramy Obrazek > Zmień rozmiar / rozdzielczość i wpisujemy skalę 50%, a następnie klikamy OK. Obrazek zostanie przeskalowany do rozdzielczości 1024 x 1024. Łatwe dzielenie mówi, że teraz w obrazku zmieszczą się już tylko 4 x 4 kawałki o wymiarach 256 x 256 pikseli. Kawałkujemy więc obrazek na 4 w pionie i 4 w poziomie, a następnie eksportujemy, analogicznie jak w poprzednim kroku.

Obrazek w pliku Z1.cpt przeskalowujemy do 25%, to jest do wymiaru 512 x 512. Analogicznie jak poprzednio, kawałkujemy go (2x2) a następnie eksportujemy.

Obrazek w pliku Z0.cpt przeskalowujemy do rozmiaru 256 x 256. Będzie to najdalsze oddalenie, jakie można będzie uzyskać na mapie. Zapisujemy obrazek pod nazwą Z0R1C1.jpg (ponieważ ma on już wymiary 256 x 256, nie ma sensu go kawałkować).

W tym momencie powinniśmy mieć 85 plików, każdy o wymiarach 256 x 256. Pliki posiadają automatyczne nazwy, nadawane przez program Corel Photo-Paint. Przykładowo, nazwa pliku Z1R3C2.jpg oznacza, że dany obrazek jest wyświetlany na poziomie przybliżenia 1, w rzędzie 3 (R - skrót od angielskiego ROW) i w kolumnie 2 (C z angielskiego COLUMN). Numerowanie kolumn i wierszy programu Corel Photo Paint zaczyna się zawsze od 1. Tę małą niezgodność z API (gdzie numeracja jest od zera) skorygujemy później.

Z http://gmapsapi.com/examples/062/mapa-vc-kawalki.zip możesz pobrać spakowane archiwum ze wszystkimi kawałkami, przygotowanymi według powyższego opisu dla wybranej przeze mnie mapy.

Skrypt

Czas zabrać się za napisanie skryptu, który wyświetli mapę. Najpierw należy stworzyć obiekt, reprezentujący zbiór praw autorskich do zdefiniowanych obszarów mapy, oraz za notę, która będzie wyświetlana:

var prawaAutorskie = new GCopyrightCollection('Mapa');
var nota = new GCopyright(1,new GLatLngBounds(new GLatLng(-90, -180),new GLatLng(90, 180)),0,"© Rockstar Games");

Pierwszym argumentem w konstruktorze GCopyrightCollection() jest wyświetlany przed prawami autorskimi prefix. Warto zwrócić uwagę na drugi i trzeci argument w konstruktorze obiektu GCopyright - odpowiadają one za zakres współrzędnych mapy. W większości przypadków najlepiej pozostawić to tak, jak w powyższej linijce, zmieniając jedynie zawartość ostatniego ciągu tekstowego.

Teraz dodajemy notę do obiektu, odpowiadającego za prawa autorskie:

prawaAutorskie.addCopyright(nota);

Najważniejszym zadaniem jest poinstruowanie API o istnieniu naszych obrazków i o ich adresie URL:

var kawalki = [new GTileLayer(prawaAutorskie,0,3)];
kawalki[0].getTileUrl = function (p,z)
{
	var zoom 	= z;
	var rzad 	= p.y + 1;
	var kolumna	= p.x + 1;
	if(zoom >= 0 & zoom <4 && rzad >0 && rzad <= Math.pow(2,zoom) && kolumna > 0 && kolumna <= Math.pow(2,zoom))
		return "http://gmapsapi.com/examples/062/mapa/Z"+zoom+"R"+rzad+"C"+kolumna+".jpg";
	else
		return '';
}

W pierwszej linijce liczba 0 oznacza minimalny, a 3 maksymalny poziom przybliżenia. Funkcja getTileUrl() będzie wywoływana przez API za każdym razem, gdy zajdzie konieczność ustalenia adresu obrazka do wyświetlenia. Pierwszy przekazywany argument mówi o współrzędnych (odpowiednio we własnościach x i y), a drugi o poziomie zoom. Mając dane te informacje, możemy skonstruować adres obrazka. Nazewnictwo kawałków, które przygotowaliśmy wygląda tak: Z{K}R{L}C{M}, gdzie {K} to poziom zoom, {L} to współrzędna Y, ale numerowana od jedynki, a {M} to współrzędna X, również numerowana od jedynki. Skorygujemy to niedociągnięcie, dodając jeden do współrzędnej przekazywanej przez API, i zwrócimy ciąg, określający URL żądanej grafiki.

Ostatni etap pracy polega na stworzeniu nowego trybu mapy:

var mapaGTA = new GMapType(kawalki, G_NORMAL_MAP.getProjection(), "Mapa", {errorMessage:"Brak obrazka"});

Ten kod tworzy nowy tryb mapy, korzystającej z projekcji domyślnej. W razie braku obrazka, zostanie wyświetlony komunikat "brak obrazka". Wszystkie powyższe rzeczy powinny znaleźć się przed inicjalizacją mapy, która powinna wyglądać mniej więcej tak:

mapa = new GMap2(document.getElementById("mapka"),{mapTypes: [mapaGTA]});
mapa.addControl(new GLargeMapControl());
mapa.setCenter(new GLatLng(5.965753671065536,61.171875),2);	

Tak stworzona aplikacja powinna elegancko wyświetlać kawałki obrazka, stwarzając pozory "ciągłości". Gratuluję, jedno z najtrudniejszych zagadnień w dziedzinie Google Maps API rozgryzione! przykład 1pokaż kod przykładu

Trochę dodatków

Sama mapa to nie wszystko - warto ją również opisać - ciekawe miejsca można przykładowo zaznaczyć markerami z okienkami infoWindow. Dodawanie markerów wygląda zwyczajnie, natomiast samemu trzeba się zatroszczyć o współrzędne. W przykładzie 2 dodałem na mapę kilka markerów z okienkami po kliknięciu: przykład 2pokaż kod przykładu

Minimapa

Stworzona z naszych kawałków mapa jest gotowa do wyświetlania w trybie minimapy - proste dodanie linjki:

mapa.addControl(new GOverviewMapControl()); 

spowoduje, że w rogu mapy API wyświetli minimapę (szczególnie użyteczną na najbliższym przybliżeniu) przykład 3pokaż kod przykładu

Wyłączenie zawijania

Mapa wykazuje jedną niepożądaną cechę, odziedziczoną po sposobie wyświetlania mapy świata - zamiast jednej "mapy", w poziomie wyświetlane jest wiele kopii, a markery przesuwają się na tą, która jest w polu widzenia. Można to zmienić, modyfikując nieco domyślną projekcję:

GMercatorProjection.prototype.getWrapWidth = function () { return 10000; };

Dzięki tej modyfikacji, markery nie będą się "zawijać". Wartość 10 000 jest dowolna, ważne, by była wielokrotnie większa od rozmiaru samej mapy.

GMercatorProjection.prototype.tileCheckRange = function (a,z,r) { 
	if(a.x<0 || a.y<0)
		return false;
	else if(a.x > Math.pow(2,z)-1 || a.y > Math.pow(2,z)-1)
		return false;
	else
		return true;
};

Powyższy kod jest również prosty w działaniu - sprawdza, czy żądany przez API fragment należy do zamierzonego zakresu. Ponieważ siatka kawałków ma wymiary:

  • 1x1 na poziomie 0
  • 2x2 na poziomie 1
  • 4x4 na poziomie 2
  • 8x8 na poziomie 3

to warunek sprawdzenia można sprowadzić do postaci, w której porównujemy, czy współrzędna X i Y kawałka jest nie większa niż 2z-1, gdzie z to obecny poziom zoomu. Dodatkowo, należy wykluczyć ujemne współrzędne (patrz linijka 2 w powyższym listingu).

Kod z powyższych listingów musi się znaleźć przed inicjacją mapy (miejsce, w którym wybiera się rodzaj projekcji GMercatorProjection:

var mapaGTA = new GMapType(kawalki, new GMercatorProjection(19), "Mapa", {errorMessage:"Brak obrazka"});

Liczba 19 oznacza maksymalny stopień zbliżenia, ale nie ma ona wpływu na długość suwaka - ten ustalany jest wyłącznie na podstawie dostępności kawałków. Poziom ten należy bardziej traktować w kategoriach wewnętrznych obliczeń API - np. polilinie będą poprawnie rysowane tylko wtedy, gdy poziom zoom podany w argumencie będzie nie mniejszy niż 17. W przykładzie 4 dodatkowo zmieniłem tło mapy na niebieskie, imitujące wodę: przykład 4pokaż kod przykładu

Potencjalne problemy

Tak sporządzona mapa stwarza dwa zasadnicze problemy. Pierwszym z nich jest problem w wyznaczeniu współrzędnych - nie mają one wiele wspólnego ze współrzędnymi ekranowymi - aby znaleźć współrzędne, należy napisać dodatkową funkcję, która np. pokazuje współrzędne myszy, pomocne przy ustalaniu położenia markera.

Problemem okazuje się użycie projekcji, która nie jest zbytnio przystosowana do obsługi naszej płaskiej mapy. Konieczne będzie stworzenie mechanizmu własnej projekcji. Wkrótce na stronie pojawi się poradnik, pokazujący poprawione o własną projekcję przykłady z tego tutoriala.

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 programu MS Excel

API v2

W tym przykładzie pokazane zostało, w jaki sposób wyświetlić markery z arkusza kalkulacyjnego


Warstwa zdjęć z Panoramio

API v2

Dodaj na swoją mapę eleganckie miniaturki zdjęć okolicy z serwisu Panoramio


Wczytanie najbliżej położonych punktów

API v2

Jak wczytać 20 najbliższych miejsc w promieniu 20km od zadanego punktu?