Mapbox a tipy na nástroje v React.js

Při procházení populárních příspěvků jsem se inspiroval touto mapou COVID-19, abych se mohl naučit Mapbox. Projekt pokrývá hodně z toho, co zde dělám, a doufám, že se nebudu snažit ukrást někomu hrom. Toto není příspěvek o mé kreativitě. Jsem začátečník/student bootcampu a měl jsem pocit, že bych mohl ještě více zjednodušit proces pouhého používání Mapboxu, natož jeho připojení k zajímavým datům COVID a formátování.

Základní nastavení Mapboxu

Mapbox GL JS je JavaScriptová knihovna, která využívá WebGL k vykreslování interaktivních map z vektorových dlaždic a stylů Mapbox. Tento tutoriál o základním nastavení v Reactu je velmi dobrý a užitečný! Tento příspěvek většinou projde/zkombinuje několik již velmi dobrých tutoriálů. Ještě jednou se zde nesnažíme znovu vynalézt kolo, ale doufáme, že zkombinujeme několik dobrých stávajících kol.

Základní nastavení Reactu:

npx create-react-app your-app-name
cd your-app-name
npm install mapbox-gl

nebo přidejte mapbox-gl do package.json ručně a poté spusťte npm install . Zdá se, že oba dosáhnou stejné věci - vytvoření package-lock.json a má package.json který obsahuje mapbox-gl v dependencies .

Nyní je to pravděpodobně triviální rozdíl, ale tutoriál Mapbox obsahuje vše v index.js , Učil jsem se Reagovat se zachováním index.js krátké - takto:

// src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';

ReactDOM.render(<App />, document.getElementById("root"));

A pak ponechám většinu kódu v App.js pro teď.

// src/App.js

import React, { Component } from 'react'
import "./App.css";
import mapboxgl from 'mapbox-gl';

mapboxgl.accessToken = process.env.REACT_APP_MAPBOX_ACCESS_TOKEN;

export class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      lng: -90,
      lat: 45,
      zoom: 3
    };}

  componentDidMount() {
    const map = new mapboxgl.Map({
      container: this.mapContainer,
      style: 'mapbox://styles/mapbox/streets-v11',
      center: [this.state.lng, this.state.lat],
      zoom: this.state.zoom
    });}

  render() {
    return (
      <div className="App">
        <div ref={element => this.mapContainer = element} className="mapContainer" />
      </div>
    )}}

export default App

a nyní máme základní Mapbox! Pro přístupový token si jednoduše zaregistrujete bezplatný a snadný účet na Mapboxu a pak malá vedlejší poznámka, která není příliš důležitá, protože je nepravděpodobné, že by někdo chtěl váš bezplatný token ukrást, ale je dobré používat .env a .gitignore :

// in project main directory
touch .env

// .env
REACT_APP_MAPBOX_ACCESS_TOKEN=<mytoken>

// App.js
mapboxgl.accessToken = process.env.REACT_APP_MAPBOX_ACCESS_TOKEN;

// .gitignore
.env

Zábavné upozornění! ⚠️ Pokud se zobrazí chyba Invalid LngLat latitude value: must be between -90 and 90 - pravděpodobně máte zaměněnou zeměpisnou délku a šířku! Kdybyste věděli, kolik věcí jsem se to pokusil opravit, aniž bych tu chybu jednoduše vygoogloval, protože jsem si nemyslel, že bych mohl dělat tak jednoduchou záměnu...

Každopádně v tuto chvíli mám souřadnice nastavené na SF. Můžete si pohrát s console.logs a nástroji React dev for state, abyste mohli experimentovat s různými počátečními souřadnicemi a přiblížením.

    this.state = {
      lat: 37.7524,
      lng: -122.4343,
      zoom: 11.43
    };
  }

Stále se řídíte výukovým programem Mapbox – zde je návod, jak přidat lištu, která zobrazuje vaše souřadnice a přibližuje při pohybu po mapě.

// added to existing componentDidMount() function 

componentDidMount() {
...
    map.on('move', () => {
      this.setState({
      lng: map.getCenter().lng.toFixed(4),
      lat: map.getCenter().lat.toFixed(4),
      zoom: map.getZoom().toFixed(2)
      });
      });
    }

a v render() , přidejte následující <div> těsně pod <div className="App"> :

// added to existing render() 
...
   <div className="App">
     <div className="sidebarStyle">
        Longitude: {this.state.lng} | Latitude: {this.state.lat} | Zoom: {this.state.zoom}
     </div>

V tomto okamžiku byste také měli mít něco takového v src/App.css . Všimněte si, že pokud něco nefunguje, ale neobjevují se žádné chyby, může to být problém s CSS – hodně z toho zahrnuje stylování z Mapboxu.

.mapContainer {
 position: absolute;
 top: 0;
 right: 0;
 left: 0;
 bottom: 0;
}

.sidebarStyle {
 display: inline-block;
 position: absolute;
 top: 0;
 left: 0;
 margin: 12px;
 background-color: #404040;
 color: #ffffff;
 z-index: 1 !important;
 padding: 6px;
 font-weight: bold;
 }

Malá tečna, kterou jsem považoval za zajímavou, ale snadno se najde – pokud chcete změnit ikonu, která se zobrazuje na kartě prohlížeče vedle názvu, uložte obrázek do své veřejné složky a přidejte jej do index.html kde je výchozí odkaz na ikonu již nastaven:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/map.png" />

a stačí změnit část za %PUBLIC_URL%/ . Uložil jsem svůj jako 'map.png', jak můžete vidět zde.

Zde výukový program Mapbox téměř končí a poté odkazuje na příklady, jak rozšířit. Stejně jako u všeho v programování existuje tolik dobrých možností! A různé způsoby provedení každé z těchto možností. Z nějakého důvodu mi tipy na nástroje vynikly. Doposud jsem ani nevěděl, že 'tooltips' je oficiální termín pro tato malá vyskakovací okna.

Také jsem narazil na tento skvělý blogový příspěvek o knihovnách komponent React a zajímalo mě použití react-portal-tooltip . Zjistil jsem však, že oficiální příklad Mapboxu na nápovědách je o něco snazší sledovat přímo po tomto nastavení. react-portal-tooltip je obecnější a užitečná pro všechny druhy aplikací, což je skvělé, ale pomohlo mi to začít s konkrétním příkladem Mapboxu, abych zjistil, co se zde děje.

Popisky

Popisek (nebo infotip nebo hint ) je běžný prvek grafického uživatelského rozhraní — malý „hover box“ s informacemi o položce. Opět docela základní věci, ale jsem studentem kódovacího bootcampu a právě jsme dokončili vanilla JS/začali React, takže to vypadalo jako skvělá věc, která by bez Reactu byla těžší! Vždy rád myslím na jasný příklad, proč se něco učím, místo abych to jen přijímal, protože je to módní slovo. Každopádně!

Toto je repo pro příklad popisku specifického pro Mapbox, se kterým začínám.

Nejprve vytvořte components adresář v rámci src a ToolTipBox.js Component (nebo jej můžete pojmenovat jakkoli chcete, něco kratšího, například ToolTip.js , ale pokud později použiji knihovnu popisků, nemusí to být dostatečně konkrétní). Importujte komponentu a také ReactDOM které nyní potřebujeme v App.js a přidejte následující kód:

...
import ReactDOM from 'react-dom';
import ToolTipBox from './components/ToolTipBox'
...

export class App extends Component {
 mapRef = React.createRef();
 tooltipContainer;

 componentDidMount() {
   // Container to put generated content in
   this.tooltipContainer = document.createElement('div');

   const map = new mapboxgl.Map({
     container: this.mapRef.current,
 ...
   });
 ...
   const tooltip = new mapboxgl.Marker(this.tooltipContainer).setLngLat([0,0]).addTo(map);

   map.on('mousemove', (e) => {
     const features = map.queryRenderedFeatures(e.point);
     tooltip.setLngLat(e.lngLat);
     map.getCanvas().style.cursor = features.length ? 'pointer' : '';
     this.setTooltip(features);
   }
   );
 }

 render() {
   return (
     <div className="App">
      ...
       <div ref={this.mapRef} className="absolute top right left bottom"/>
     </div>)}}
...

Upozornění v map.on('mousemove') Mám this.setTooltip(features) . Definuji to mimo componentDidMount() a připojí se k mému ToolTipBox komponent.

export class App extends Component {
...
 setTooltip(features) {
   if (features.length) {
     ReactDOM.render(
       React.createElement(
         ToolTipBox, {
           features
         }
       ),
       this.tooltipContainer
     );
   } else {
     ReactDOM.unmountComponentAtNode(this.tooltipContainer);
   }
 }
...
}

Zde použité důležité věci - React.createRef() , což je dobré pro:

Ale měli byste se vyhnout všemu, co lze udělat deklarativně.

queryRenderedFeatures pochází z Mapbox API a takto získáváme 'funkce', které nám poskytnou informace o nápovědách/vyskakovacích oknech!

React.createElement() - to se nezdá běžné/standardní a obvykle by se to dělalo s JSX. Dokumenty React doporučují používat JSX a ne React.createElement() , ale tady to vypadá dobře.

Nyní více o ToolTipBox komponenta, která používá Static PropTypes pro ověření, že se 'funkce' vrátily z queryRenderedFeatures je pole.

// src/components/ToolTipBox.js

import React from 'react'
import PropTypes from 'prop-types'

export default class Tooltip extends React.Component {

 static propTypes = {
   features: PropTypes.array.isRequired
 };

 render() {
   const { features } = this.props;

   const renderFeature = (feature, i) => {
     return (
       <div key={i}>
         <strong className='mr3'>{feature.layer['source-layer']}:</strong>
         <span className='color-gray-light'>{feature.layer.id}</span>
       </div>
     )
   };

    return (
      <div className="flex-parent-inline absolute bottom">
        <div className="flex-child">
          {features.map(renderFeature)}
        </div>
      </div>
    );}}

S CSS se toho tady hodně děje a všimnete si, že skutečný příklad, ze kterého kopíruji, měl více stylů, ale odstranil jsem ho a některé přidal do svého vlastního App.css pro jednoduchost bloků kódu zde. Zde je to, co jsem přidal do svého CSS po tomto kroku:

.flex-parent {
  flex-direction: column;
  position: absolute;
}
.flex-child {
  color: white;
  background: gray;
  text-overflow: clip;
  padding: 1rem;
}

Docela jednoduché, stačí styl, abyste viděli základní krabici. Ne tak estetické, ale mohu se k tomu vrátit později, a vy také!

Ať tak či onak, pokud nechcete kompletně definovat všechny své vlastní CSS, což jsem neudělal, pravděpodobně byste měli mít svůj index.html vypadá to také jako příklad, protože sem importují šablony stylů z mapboxu:

<!DOCTYPE html>
<html lang="en">
 <head>
   <meta charset="utf-8" />
   <link rel="icon" href="%PUBLIC_URL%/map.png" />
   <meta name="viewport" content="width=device-width, initial-scale=1" />
   <meta name="theme-color" content="#000000" />
   <meta
     name="description"
     content="Web site created using create-react-app"
   />
   <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
   <link href='https://api.mapbox.com/mapbox-assembly/mbx/v0.18.0/assembly.min.css' rel='stylesheet'>
   <link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.39.1/mapbox-gl.css' rel='stylesheet' />
   <title>MapBox React Example</title>
 </head>
   <div id="root"></div>
   <script src='https://api.mapbox.com/mapbox-assembly/mbx/v0.18.0/assembly.js'></script>
 </body>
</html>

React Tooltip Library

Tento příspěvek je již trochu dlouhý, takže se nebudu zabývat react-portal-tooltip . Ale jednu velmi nepříjemnou věc, kterou jsem překonal, když jsem to prozkoumával, a myslel jsem si, že stojí za to se o ni podělit – pokud máte toho chlapa:




Na StackOverflow je mnoho řešení. Tento pro mě fungoval:

touch src/declare_modules.d.ts

// in declare_modules.d.ts
declare module "react-portal-tooltip";

// if it still doesn't work, add import in `App.js`
// App.js
...
import './declare_modules.d.ts'

Děkujeme za přečtení!

Zdroje:

  • Mapbox API
  • Výukový program Mapbox React
  • Příklady Mapbox React
  • Mapbox COVID-19
  • Knihovna komponent React Tooltip
  • Můj repo pro tento příklad :)