Cvičení: React mapy
Dokumentace react-map-gl: visgl.github.io/react-map-gl/docs
Zadání
-
Založ nový projekt pomocí create-czechitas-app.
-
Vytvoř v
src
složkucomponents
a v ní přichystej komponentuMapa
.import React from 'react' export const Mapa = () => { return <>Mapa</> }
-
Přidej mapu na stránku.
-
Koukni do prohlížeče a zkontroluj, že se ti na stránce vypisuje text
Mapa
. -
Nainstaluj závislost pro mapy přes příkaz
npm install react-map-gl@5
. -
Mapbox vyžaduje import vlastních css. Přidej do komponenty
import 'mapbox-gl/dist/mapbox-gl.css'
. -
Zobraz základní mapu.
-
Přidej do komponenty stav, který bude reprezentovat střed mapy a přiblížení. Nezapomeň
useState
importovat.const [viewport, setViewport] = useState({ latitude: 50.087543262674856, longitude: 14.421045443793917, zoom: 15, })
-
Naimportuj
ReactMapGL
pomocíimport ReactMapGL from 'react-map-gl'
. -
Komponentu
ReactMapGL
vykresli.return ( <ReactMapGL {...viewport} width="100%" height={400} onViewportChange={(nextViewport) => setViewport(nextViewport)} ></ReactMapGL> )
-
Komponentě
ReactMapGL
přidejprop
mapStyle
s následujícím objektem nastavujícím mapové podklady ze Seznam map.{ version: 8, sources: { 'raster-tiles': { type: 'raster', tiles: ['https://mapserver.mapy.cz/base-m/{z}-{x}-{y}'], tileSize: 256, attribution: 'Mapové podklady od <a target="_top" rel="noopener" href="https://mapy.cz/">Seznam.cz</a> a <a target="_top" rel="noopener" href="http://openstreetmap.org">OpenStreetMap</a>.', }, }, layers: [ { id: 'simple-tiles', type: 'raster', source: 'raster-tiles', minzoom: 0, maxzoom: 18, }, ], }
-
Vyzkoušej, že se ti mapa zobrazí a ukazuje Staroměstské náměstí v Praze.
-
-
Najdi gps souřadnice nejbližší Czechitas pobočky a nastav je jako výchozí střed mapy. Adresu zjistíš třeba ze stránek czechitas.cz. Z webu gps-coordinates.net pak vytáhni konkrétní souřadnice
latitude
alongitude
(zeměpisnou šířku a délku). -
Přidej na mapu Marker, který bude značit polohu této pobočky.
-
Z webu iconmonstr.com stáhni ikonku špendlíku a ulož ji do složky
src/img
pod názvemspendlik.svg
. -
Přidej do komponenty
Mapa
import Markeru.import ReactMapGL, { Marker } from 'react-map-gl'
-
Marker přidej jako potomka
<ReactMapGL>
. Nastav mu vlastnostilatitude
alongitude
. Použij hodnoty z gps-coordinates.net.<Marker latitude={…} longitude={…}> Czechitas </Marker>
-
Místo textu
Czechitas
vlož do Markeru obrázek špendlíku.import spendlikUrl from '../img/spendlik.svg'
<img src={spendlikUrl} width={50} height={50} alt="Czechitas" />
-
Všimni si, že špendlík je zapíchnutý o kousek vedle a jeho poloha se při přibližování a oddalování mapy (třeba kolečkem myši) lehce mění. Je to tím, že Mapbox neví, kde má ikonka hrot. Nastav Markeru vlastnosti
offsetLeft
aoffsetTop
.offsetLeft={-25}
říká, že hrot je po ose x posunutý o25
pixelů od levého horního rohu.offsetTop={-50}
pak o50
pixelů po ose y. Zkontroluj, že se špendlík už drží na správném místě.
-
-
Označ Marker Popupem.
-
Doplň do importů
{ Popup }
zreact-map-gl
. -
Přidej Popup bublinu vedle Markeru.
<Marker>…</Marker> <Popup latitude={50.0833715} longitude={14.4252452} > Czechitas </Popup>
-
Gps souřadnice dej stejné jako u Markeru.
-
Popup má také vlastnost
offsetTop
. Nastav ji tak, aby bublina nepřekrývala špendlík, byla nad ním.
-
-
Zobraz Popup až po kliknutí na Marker.
-
Přidej stavovou proměnnou.
const [popupOtevren, setPopupOtevren] = useState(false)
-
Vykresli Popup jen v případě, že
popupOtevren
jetrue
.{popupOtevren && <Popup … />}
-
Obal obrázek v Markeru tlačítkem, které na klik upraví stav.
<button onClick={() => setPopupOtevren(true)}><img … /></button>
-
Vyzkoušej, že kliknutí opravdu otevírá Popup.
-
Po kliknutí na křížek v pravém horním rohu Popup zavři. Přidej Popupu posluchač
onClose
.<Popup … onClose={() => setPopupOtevren(false)} >
-
Vyzkoušej.
-
Dostyluj
button
v Markeru, aby byl nenápadný.-
Dej mu
className="marker-button"
. -
Vytvoř v
components
soubormarker-button.css
. -
Přidej do něj styly a soubor v komponentě naimportuj.
.marker-button { padding: 0; border: none; background-color: transparent; display: flex; outline: none; cursor: pointer; }
-
-
-
Zobraz na mapě více Markerů.
-
Přidej do složky
src/img
obrázek bagru jakobagr.svg
a informační ikonky jakoinfo.svg
. -
Importuj je v komponentě
Mapa
.import bagrUrl from '../img/bagr.svg' import infoUrl from '../img/info.svg'
-
Přichystej si na začátku komponenty pole objektů.
const mista = [ { id: 1, ikonaUrl: bagrUrl, latitude: 50.08415631476569, longitude: 14.423472469019359, }, { id: 2, ikonaUrl: infoUrl, latitude: 50.08140252219053, longitude: 14.425123690476866, }, { id: 3, ikonaUrl: infoUrl, latitude: 50.08315119880879, longitude: 14.42713937555392, }, { id: 4, ikonaUrl: bagrUrl, latitude: 50.08147136893371, longitude: 14.427310912961879, }, ]
-
Představme si, že třeba ikonky bargu budou symbolizovat neprůchodná místa, kde se zrovna staví a ikonky informací budou místa s info stánky. Uprav gps souřadnice tak, aby odpovídali místům v okolí pobočky.
-
Pomocí
mista.map
vykresli všechny nové špendlíky na mapě.{mista.map((misto) => ( <Marker key={misto.id} latitude={misto.latitude} longitude={misto.longitude} offsetLeft={-15} offsetTop={-15} > <img src={misto.ikonaUrl} width={30} height={30} alt="" /> </Marker> ))}
-
-
Přidej filtrování pro zvýraznění informačních stánků, skrytí bagrů.
-
Přidej stavovou proměnnou.
const [bargrySkryty, setBargrySkryty] = useState(false)
-
Pod
</ReactMapGL>
přidej tlačítko, které bude měnit (togglovat) stavbargrySkryty
zfalse
natrue
a obráceně. Return může vracet jen jeden prvek. Budeš muset<ReactMapGL>
a<button>
obalit React fragmentemreturn <>…</>
.<button onClick={() => setBargrySkryty(!bargrySkryty)}> {bargrySkryty ? 'zobrazit' : 'skrýt'} bagry </button>
-
K výpisu
mista
přidej filtr, který bude skrývat bagry.mista .filter((misto) => bargrySkryty === false || misto.ikonaUrl !== bagrUrl) .map(…)
-
-
Přidej na stránku tlačítka pro ovládání přiblížení.
-
Importuj
{ NavigationControl }
zreact-map-gl
. -
Mezi potomky
<ReactMapGL>
přidejdiv
a v němNavigationControl
.<div className="ovladani"> <NavigationControl /> </div>
-
Všimni si třídy
ovladani
. Vytvoř souborovladani.css
, importuj ho a napiš do něj styly pro ovládací panel..ovladani { position: absolute; top: 10px; right: 10px; } .ovladani > * { position: static !important; }
-
-
Přidej tlačítko pro zaměření aktuální polohy uživatele.
-
Importuj
{ GeolocateControl }
zreact-map-gl
. -
Vedle vykreslení
<NavigationControl />
přidej<GeolocateControl />
. -
Vyzkoušej, co tlačítko po kliknutí dělá. Poprvé by se tě mělo zeptat na přístup k polozek a po chvíli pak zaměřit mapu na místo, kde zrovna jsi. Na počítači může být poloha značně nepřesná.
-
Bonus
-
Zkus z mapy.cz použít jiný mapový podklad.
-
Může to být trochu detektivní práce. Na Seznam mapách si přepni zobrazení třeba na zeměpisné zobrazení. Koukni do vývojářských nástrojů, co se stahuje za obrázky.
-
Pravděpodobně najdeš nějakou adresu v podobě
https://mapserver.mapy.cz/zemepis-m/11-1106-696
, kde poslední tři čísla jsou parametry konkrétní dlaždice. Nahraď je za{z}-{x}-{y}
a použij v propmapStyle
.
-
-
Umožni uživateli přesouvat Marker kliknutím na mapu.
-
Stáhni si obrázek kotvy a ulož ho do složky
src/img
s názvemkotva.svg
. -
Přidej do komponenty
Mapa
stav, který bude reprezentovat polohu kotvy.const [polohaKotvy, setPolohaKotvy] = useState({ latitude: 50.082027979423236, longitude: 14.426295971100695, })
-
Gps souřadnice opět uprav na něco v okolí.
-
Vykresli do mapy nový Marker s ikonou kotvy a polohou podle stavu
polohaKotvy
.<Marker {...polohaKotvy} offsetLeft={-25} offsetTop={-50}> <img src={kotvaUrl} width={50} height={50} alt="kotva" /> </Marker>
-
Do
<ReactMapGL>
přidej poslouchač událostionClick
, který bude přenastavovat stavpolohaKotvy
. Všimni si, že událost (event
) má vlastnostevent.lngLat
, pole o dvou prvcích v pořadílongitude
,latitude
.onClick={(event) => setPolohaKotvy({ latitude: event.lngLat[1], longitude: event.lngLat[0], }) }
-
-
Umožni uživateli přesouvat kotvu tahem myši.
-
Protože obrázky mají na tah myši speciální chování, budeš ho muset potlačit stylem
pointer-events: none
. Ideálně by takový styl mohl být ve vlastním souborucss
, ale pro teď si práci můžeš zjednodušit inline stylováním přímo vjsx
.<img src={kotvaUrl} width={50} height={50} alt="kotva" style= />
-
Nastav Markeru s kotvou vlastnou
draggable
.<Marker … draggable>
-
Vyzkoušej si kotvu tahem myši na mapě přemístit. Všimni si, že po puštění tlačítka Marker vždy skočí do původní polohy.
-
Na událost
onDragEnd
ulož dopolohaKotvy
nové umístění.<Marker … draggable onDragEnd={(event) => setPolohaKotvy({ latitude: event.lngLat[1], longitude: event.lngLat[0], }) } >
-
Nyní by kotva po přesunutí tahem myši měla zůstat na novém místě. Vyzkoušej si to. Hodnotu
polohaKotvy
můžeš na nějakém projektu případně využít pro zadávání polohy uživatelem při ukládání formuláře s mapou.
-