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.

Pogoda na mapie

Ten artykuł dotyczy najnowszej wersji API

« powrót do listy poradników

API Google Maps oferuje nieskończenie wiele możliwości, których stosowanie jest ograniczone jedynie wyobraźnią twórcy. Są jednak elementy, których nie da się osiągnąć bez współpracy z innymi API. Serwisy, które korzystają z kilku różnych dostawców danych poprzez API nazywa się mash-upami. Najczęściej spotykane technologie w tego typu serwisach to Google Maps API, a także np. Amazon, Flickr, Youtube itp. Więcej informacji na temat mashupów pod tym adresem: https://googlepolska.blogspot.com/2007/02/co-to-jest-mashup.html.

Twój pierwszy mashup

W tej części kursu użyjemy API Google Maps oraz serwisu openweathermap.org. Na mapie Polski wyświetlimy ikonki, reprezentujące panujące w danym mieście warunki pogodowe (temperaturę, wilgotność, ikonkę itp.). W dalszej części kursu dodamy jeszcze warstwę, reprezentującą obecny układ chmur. Oto co zamierzamy osiągnąć:

Pierwszy mashup

Jak korzystać z pogody w openweathermap.org?

Jest kilka możliwych sposobów na wykorzystanie danych z serwisu pogodowego openweathermap.org. Na potrzeby tego poradnika skorzystamy z danych reprezentowanych w formacie JSON. Aby ominąć ograniczenia wynikające z pobierania plików z innego serwera (zobacz uwagi do poradnika ), skorzystam z pomocnej funkcjonalności oferowanej przez powyższy serwis.

API serwisu pogodowego udostępnia informacje poprzez generowany na żądanie plik JSON. Aby go wywołać, otwieramy plik https://api.openweathermap.org/data/2.5/weather?q=Warszawa,Poland&lang=pl&units=metric, gdzie parametrowi q odpowiada poszukiwana nazwa miasta lub jego współrzędne. Kliknij na powyższy adres, by przekonać się jak wygląda struktura pliku. Przykładowo, API może zwrócić:

{
   "coord":{
      "lon":21.01,
      "lat":52.23
   },
   "sys":{
      "message":0.0044,
      "country":"Poland",
      "sunrise":1384668077,
      "sunset":1384699250
   },
   "weather":[
      {
         "id":701,
         "main":"Mist",
         "description":"Mgiełka",
         "icon":"50d"
      }
   ],
   "base":"cmc stations",
   "main":{
      "temp":4.22,
      "humidity":91,
      "pressure":1018,
      "temp_min":3.33,
      "temp_max":5
   },
   "wind":{
      "speed":1.54,
      "gust":5.14,
      "deg":225
   },
   "clouds":{
      "all":88
   },
   "dt":1384679732,
   "id":756135,
   "name":"Warsaw",
   "cod":200
}

Mamy tu cały zestaw interesujących nas informacji, takich jak temperatura, wilgotność, ciśnienie a nawet obrazek! Jego nazwa jest zapisana w obiekcie weather.icon (w powyższym przykładzie 50d. Wystarczy dodać rozszerzenie .png i prefix https://openweathermap.org/img/w/ aby uzyskać jego pełny adres. Przykładowo, obrazek odpowiadający nazwie 50d to:

Ikonka pogody

Podanie atrybutu lang=pl w zapytaniu pozwoliło zwrócić polski opis pogody (w powyższym przykładzie: Mgiełka). Z kolei określenie parametru units=metric nakazuje API zwrócenie temperatur w stopniach celsjusza, prędkości wiatru w km/h itp. W przeciwnym wypadku, użyte zostaną jednostki imperialne (stopnie Fahrenheita, mile na godzinę itp.)

Ominięcie zabezpieczeń

Bezpośrednio nie jest możliwe pobranie tego pliku za pomocą funkcji Ajaxa, gdyż znajduje się w innej domenie. Aby ominąć te zabezpieczenie, wykorzystam mały trick oferowany przez producenta API. Dodanie do adresu parametru callback=nazwa_funkcji spowoduje, że dane zostaną zwrapowane jako argument funkcji o nazwie nazwa_funkcji, np.:

wyswietlPogode({
   "coord":{
      "lon":-0.13,
      "lat":51.51
   },
   "sys":{
      "message":0.2266,
      "country":"GB",
      "sunrise":1384672987,
      "sunset":1384704484
   },
   "weather":[
      {
         "id":804,
         "main":"Clouds",
         "description":"Pochmurno",
         "icon":"04d"
      }
   ],
   "base":"gdps stations",
   "main":{
      "temp":8.13,
      "pressure":1026,
      "humidity":87,
      "temp_min":7.22,
      "temp_max":8.89
   },
   "wind":{
      "speed":1,
      "deg":0
   },
   "clouds":{
      "all":92
   },
   "dt":1384679403,
   "id":2643743,
   "name":"London",
   "cod":200
});

Jest to zupełnie prawidłowy skrypt JavaScript! Jeśli więc w naszym kodzie napiszemy jednoargumentową funkcję wyswietlPogode i dołączymy powyższy skrypt, zostanie ona wywołana z pobranymi danymi pogodowymi. Sprytny sposób na ominięcie konieczności pisania własnego proxy do ajaxa.

Dane

W globalnej przestrzeni zadeklaruję stały adres, pod którym dostępne są ikonki z serwisu pogodowego:

var BAZOWY_ADRES_IKON = "https://openweathermap.org/img/w/";

W tworzonym przykładzie wyświetlimy ikonki pogodowe dla kilku predefiniowanych miejsc, tak jak w każdej prognozie pogody. Dobre rezultaty daje następujący zestaw miast (zapisany w postaci javascriptowej tablicy:

var miejsca = ['Warszawa', 'Rzeszow', 'Koszalin', 'Zielona Gora', 'Lodz', 'Krakow', 'Wroclaw', 'Poznan', 'Gdansk', 'Szczecin', 'Torun', 'Lublin', 'Bialystok'];

/* pobieramy pogodę dla wszystkich miejsc */
for(var i=0; i<miejsca.length; i++)
{
	zaladujPogode(miejsca[i]+',Poland');
}

Funkcja zaladujPogode będzie musiała wykonać wspomniany wcześniej trick - dynamicznie dołączyć skrypt z danymi z API tak, by została wykonana funkcja parsująca.

var skryptPogody = document.createElement("script");
skryptPogody.setAttribute("src", "https://api.openweathermap.org/data/2.5/weather?lang=pl&units=metric&callback=wyswietlPogode&q=" + encodeURI(adres));
skryptPogody.setAttribute("type", "text/javascript");
document.head.appendChild(skryptPogody);

Musimy oczywiście napisać funkcję do obsługi danych - jej nazwa to wyswietlPogode. Musi być dokładnie taka sama, jak parametr callback z którym wywołaliśmy skrypt powyżej.

function wyswietlPogode(danePogody) {
	var punkt = new google.maps.LatLng(danePogody.coord.lat, danePogody.coord.lon);

	var opis = null;
	if (danePogody.weather && danePogody.weather.length > 0) {

		opis = "";
		for (i = 0; i < danePogody.weather.length; i++) {

			if (i != 0)
				opis += ", ";

			opis += danePogody.weather[i].description;
		}
	}

	var ikona = BAZOWY_ADRES_IKON + danePogody.weather[0].icon + ".png";
	var temperatura = danePogody.main ? danePogody.main.temp : null;
	var cisnienie = danePogody.main ? danePogody.main.pressure : null;
	var wilgotnosc = danePogody.main ? danePogody.main.humidity : null;
	var predkosc_wiatru = danePogody.wind ? danePogody.wind.speed : null;
	var zachmurzenie = danePogody.clouds ? danePogody.clouds.all : null;

	dodajMarker(punkt, ikona, (temperatura ? temperatura + "°C" : "-"), generujZawartoscDymka(opis, temperatura, wilgotnosc, predkosc_wiatru, zachmurzenie, cisnienie));
}

Wizualizacja

Funkcja generująca opis wyświetlany w dymku wygląda następująco:

function generujZawartoscDymka(opis, temperatura, wilgotnosc, predkosc_wiatru, zachmurzenie, cisnienie) {
var opis_okno = ''+
'<div class="pogoda_dymek" style="width: 220px;">'+
	'<h3>'+opis+'</h3>'+
	'<ul>' +
		'<li><strong>temperatura</strong>: ' + (temperatura ? temperatura + '°C' : 'brak danych') + '</li>' +
		'<li><strong>ciśnienie</strong>: ' + (cisnienie ? cisnienie + 'hPa' : 'brak danych') + '</li>' +
		'<li><strong>wilgotność</strong>: ' + (wilgotnosc ? wilgotnosc +'%' : 'brak danych') + '</li>' +
		'<li><strong>zachmurzenie</strong>: ' + (zachmurzenie ? zachmurzenie + '%' : 'brak danych') + '</li>' +
		'<li><strong>prędkośc wiatru</strong>: ' + (predkosc_wiatru ? predkosc_wiatru + 'km/h' : 'brak danych') + '</li>' +
	'</ul>'+
'</div>';

return opis_okno;
}

Wygenerowane okno wyglądać będzie następująco:

Markery

Teraz trzeba zadbać o wstawienie markerów na mapę i podpięcie odpowiednich zdarzeń, by całość działała zgodnie z oczekiwaniami. Domyślne markery w Google Maps API nie posiadają jednak jednej istotnej funkcjonalności, która by się w tym momencie przydała - podpisu. Chcemy bowiem, by ikonką markera była reprezentacja obecnego stanu pogodowego, a pod nim ma się znajdować na przykład temperatura.

Problem ten można rozwiązać rozszerzając standardową klasę Marker o opisaną wcześniej funkcjonalność. Na szczęście, nie musimy wymyślać tego od nowa, gdyż istnieją gotowe, przetestowane i co najważniejsze darmowe rozwiązania. Jednym z nich jest MarkerWithLabel, dostępne pod adresem https://google-maps-utility-library-v3.googlecode.com/svn/tags/markerwithlabel/. Wystarczy dołączyć do kodu strony skrypt:

<script src="https://gmapsapi.com/examples/127/markerWithLabel.js" type="text/javascript"></script>  

by móc korzystać z jej dobrodziejstw.

Funkcja, dzięki której dane zostaną wyświetlone na mapie będzie wówczas wyglądać następująco:

function wyswietlPogode(danePogody) {
	var punkt = new google.maps.LatLng(danePogody.coord.lat, danePogody.coord.lon);

	var opis = null;
	if (danePogody.weather && danePogody.weather.length > 0) {

		opis = "";
		for (i = 0; i < danePogody.weather.length; i++) {

			if (i != 0)
				opis += ", ";

			opis += danePogody.weather[i].description;
		}
	}

	var ikona = BAZOWY_ADRES_IKON + danePogody.weather[0].icon + ".png";
	var temperatura = danePogody.main ? danePogody.main.temp : null;
	var cisnienie = danePogody.main ? danePogody.main.pressure : null;
	var wilgotnosc = danePogody.main ? danePogody.main.humidity : null;
	var predkosc_wiatru = danePogody.wind ? danePogody.wind.speed : null;
	var zachmurzenie = danePogody.clouds ? danePogody.clouds.all : null;

	dodajMarker(punkt, ikona, (temperatura ? temperatura + "°C" : "-"), generujZawartoscDymka(opis, temperatura, wilgotnosc, predkosc_wiatru, zachmurzenie, cisnienie));
}

function dodajMarker(punkt, adresObrazka, podpis, tresc) {
            
	var marker = new MarkerWithLabel({
		title: podpis,
		position: punkt,
		icon: new google.maps.MarkerImage(adresObrazka),
		labelContent: podpis,
		labelAnchor: new google.maps.Point(22, 0),
		labelClass: "temperatura",
		labelStyle: {opacity: 0.75}
	});
	
	marker.tresc = tresc;
	marker.setMap(mapa);

	google.maps.event.addListener(marker,"click",function()
	{
		dymek.setContent(marker.tresc);
		dymek.open(mapa, marker);
	});
}

Pozostały drobnostki - odrobina CSS, by całość się ładnie prezentowała. W szczególności potrzebne jest zdefiniowane stylu dla klasy temperatura (zwróć uwagę na podświetloną powyżej linię kodu):

.temperatura
{
	text-align: center;
	padding: 2px !important;
	border: 1px solid black;			    
	background: black;
	color: white;
}

.temperatura span
{
	font-family: Verdana;
	font-size: 12px !important;
	font-weight: bold;
	color: white;
}

.pogoda_dymek
{
	font-size: 10px;
	font-family: Arial;
	color: #333;
}

.pogoda_dymek h3
{
	font-size: 14px;
	padding: 1px;
	margin: 1px;
	border-bottom: 1px dotted gray;
	color: black;
}

I to tyle! Czas na przetestowanie: