Mapy 1: Google Maps

... Petr Blahoš, 30. 5. 2020 JavaScript

Dostal jsem se k projektu na vizualizaci množství zboží na prodejních místech v čase. Prodejní místa jsou rozprostřena po celé republice a je potřeba zobrazit je na mapě, a každé z nich označit ikonkou, která bude vyjadřovat, jestli je na tom místě zboží dost, nebo jestli je potřeba doplnit. A k tomu jako bonus udělat animaci v čase.

Dnes se podíváme na to, jak to udělat s mapovými podklady od Google.

Přípravy

Zrovna do Google Maps podkladů se mi moc nechtělo, protože jsou placené. Zjistil jsem ale, že je tam měsíční kredit 200$ (nebo EURO?), a dají se nastavit kvóty. Pro aplikaci, která není veřejně přístupná ten kredit musí bohatě stačit.

Aplikace poběží v prohlížeči. Budeme používat javascriptové API. Nejprve musíme získat API KEY. Najdete spoustu video návodů jak na to. Např. tento. Varování: Ztište si reproduktory. Až budete mít API KEY, můžeme začít. Pozn.: Kdekoliv najdete ve zdrojovém kódu text YOUR_API_KEY, nahraďte jej vaším API KEY.

Nejbanálnější příklad

Na vyzkoušení toho, že Váš API KEY funguje, si uložte příklad od Google jako index.html, a změňte v něm API_KEY. Podle mých pokusů nepotřebujete ani web server, stačí přímo otevřít soubor v prohlížeči. Až tohle rozchodíte, můžeme pokračovat něčím trošku složitějším. Jestli si to budete zkoušet a nevíte úplně jak na to, tak prostě otevřte ten index.html v textovém editoru, a upravte část mezi <script> a </script>.

Pozn.: V tomto textu jsou nějaké živé ukázky. Pokud místo nich vidíte ''Jejda.. Něco se pokazilo.'', asi to znamená, že jsme už vyčerpali kvótu.

Marker

Marker, neboli značku, jste viděli už v tom prvním příkladu. Tady si jich umístíme více.

<div id="map" class="map"></div>
<script>
function initMap() {
  var center = {lat: 49.7477, lng: 14.4025};
  var map = new google.maps.Map(document.getElementById('map'), {zoom: 14, center: center});
  var m1 = new google.maps.Marker({position: center, map: map});
  var m2 = new google.maps.Marker({position: {lat: 49.7400, lng: 14.4025}, map: map});
  var m3 = new google.maps.Marker({position: {lat: 49.7550, lng: 14.4125}, map: map});
}
</script>
<script async defer src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap">
</script>

Zkusíme s tou značkou pohybovat.

Tohle je spíš taková hračka, ani Vás nebudu urážet tím, že bych tady ukazoval kód. Raději si zkusíme měnit vzhled té značky.

Jiná značka

Google tam umožňuje vrazit ikonu, ale zdá se mi, že oficiálně žádní ikony kromě té jedné červené značky s tečkou nezveřejnil. Po troše hledání najdete např. seznam ikon použitých v různých Google aplikacích. Ty jsou pravděpodobně dost bezpečné, ale jistota, že někdy nezmizí, není. Já osobně mám raději vektorové obrázky, než ikony, a po delší chvíli hledání jsem našel tohle. Tak si to zkombinujeme, a přidáme pár dalších parametrů.

<div id="map" class="map"></div>
<script>
function initMap() {
  var center = {lat: 49.7477, lng: 14.4025};
  var map = new google.maps.Map(document.getElementById('map'), {zoom: 14, center: center});
  var m1 = new google.maps.Marker({
    position: {lat: 49.7400, lng: 14.4000},
    title: "Original marker",
    map: map
  });
  var m2 = new google.maps.Marker({
    position: {lat: 49.7477, lng: 14.4025},
    title: "Blue Dot Icon",
    map: map,
    icon: {url: "http://maps.google.com/mapfiles/ms/icons/blue-dot.png"}
  });
  var pinSVGHole = "M12,11.5A2.5,2.5 0 0,1 9.5,9A2.5,2.5 0 0,1 12,6.5A2.5,2.5 0 0,1 14.5,9A2.5,2.5 0 0,1 12,11.5M12,2A7,7 0 0,0 5,9C5,14.25 12,22 12,22C12,22 19,14.25 19,9A7,7 0 0,0 12,2Z";
  var icon = {
    path: pinSVGHole,
    anchor: new google.maps.Point(12,17),
    fillOpacity: 1,
    fillColor: "green",
    strokeWeight: 2,
    strokeColor: "white",
    scale: 1.5,
    labelOrigin: new google.maps.Point(12,9)
  };
  var m3 = new google.maps.Marker({
    position: {lat: 49.7477, lng: 14.41},
    title: "Vectors",
    map: map,
    icon: icon
  });
}
</script>
<script async defer src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap">
</script>

Co s takovou vektorovou značkou můžeme dělat si ukážeme záhy.

Animace

Teď se vrátím k původnímu zadání, to je vizualizovat množství zboží na prodejních místech v čase. Všimněte si, že jsem umístil dvě značky skoro na sebe. To je z jednoduchého důvodu. Když jsou dvě prodejní místa blízko sebe, tak dokud mapu dostatečně nepřiblížíme, uvidíme je přes sebe. V tom případě chceme, aby ten vážnější případ byl nahoře.

<div id="map" class="map"></div>
<script>
function initMap() {
  var center = {lat: 49, lng: 14};
  var map = new google.maps.Map(document.getElementById('map'), {zoom: 10, center: center});
  var pinSVGHole = "M12,11.5A2.5,2.5 0 0,1 9.5,9A2.5,2.5 0 0,1 12,6.5A2.5,2.5 0 0,1 14.5,9A2.5,2.5 0 0,1 12,11.5M12,2A7,7 0 0,0 5,9C5,14.25 12,22 12,22C12,22 19,14.25 19,9A7,7 0 0,0 12,2Z";
  var icon = {
    path: pinSVGHole,
    anchor: new google.maps.Point(12,17),
    fillOpacity: 1,
    fillColor: "green",
    strokeWeight: 2,
    strokeColor: "black",
    scale: 1.5,
    labelOrigin: new google.maps.Point(12,9)
  };
  var markers = {};
  for (var i = 1; i < 6; i++) {
    var mul = 2 == i ? 1.1 : i;
    var m = new google.maps.Marker({
      position: {lat: 49, lng: 13.8 + mul*0.1},
      optimized: false,
      value: 160,
      map: map,
      icon: icon
    });
    m.addListener("click", function (e) {
      console.log("Value on the clicked marker: ", this.value);
    });
    markers[i] = m;
  }
  function updateMarkers() {
    var m = markers[Math.floor(Math.random() * 5) + 1];
    var icon = m.getIcon();
    m.value = Math.floor(Math.random() * 300);
    icon.rotation = 0;
    var zindex = 1;
    if (m.value < 75) {
      icon.fillColor = "red";
      icon.rotation = 90;
      zindex = 5;
    } else if (m.value < 150) {
      icon.fillColor = "orange";
      zindex = 4;
    } else if (m.value > 200) {
      icon.fillColor = "blue";
      zindex = 3;
    } else {
      icon.fillColor = "green";
    }
    m.setIcon(icon);
    m.setZIndex(zindex);
    m.setTitle("Value: " + m.value);
  }
  setInterval(updateMarkers, 1000);
}
</script>
<script async defer src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap">
</script>

Tak pěkně od začátku. Po inicializaci mapy si vyrobíme pár značek, všechny se stejnou ikonou. Potom každou sekundu jednu ze značek vezmeme, a náhodně upravíme. K té vlastní úpravě asi ani není třeba mnoho říkat, snad jen:

  • Při výrobě značky jsem nastavil optimized = 0. Podle některých zdrojů by jinak nefungovalo nastavení z-indexu. Mě ale fungovalo, ikdyž bylo optimized ponecháno výchozí.
  • Na značku jsem pověsil obsluhu události click, je abyste věděli, že to jde.
  • Z-index, jak chápete, se nastavuje funkcí setZIndex.
  • setTitle nastaví text, který se zobrazí jako tooltip.
  • Ve skutečné aplikaci bych si připravil několik ikon a ty střídal. Nedělal bych getIcon .. update .. setIcon.
  • Můžete si to otevřít na jsfiddle, ale tam opravdu budete muset použít Váš API KEY.

Co dál

Příště zkusíme totéž, ale trochu jinak. Případné dotazy pište do komentářů.

Dokumentace od Google