Dev (Back & Front)

19 mai, 2017

Fazendo mapas com React

Publicidade

Neste artigo, você pode encontrar uma visão geral sobre diferentes bibliotecas de mapeamento React. Muitos de nossos projetos são sobre visualizações de dados e mapas. Ao longo de dois anos, temos construído a maioria de nossos aplicativos com React. No início, apenas usamos Leaflet dentro de um componente React. Agora estamos usando principalmente React Leaflet que é um wrapper React para Leaflet.

Todos os exemplos incluídos neste artigo podem ser encontrados no repositório do GitHub make-maps-with-react se você quiser brincar um pouco.

Suporte para WebGL

Antes de você começar a desenvolver o seu mapa, você deve decidir se precisa ou não de suporte WebGL. Você deve considerar o uso de WebGL se você tem um monte de recursos a serem exibidos ao mesmo tempo, digamos marcadores de 10k, por exemplo. Para um mapa normal, uma biblioteca sem suporte WebGL será suficiente.

React Leaflet

Como mencionado anteriormente, essa é a biblioteca que mais usamos. Leaflet é uma biblioteca de mapeamento muito sólida e esse é o wrapper do React. Ele é ativamente mantido e já usa a versão Leaflet 1.0.0.

A API é bastante simples se você estiver acostumado com a API Leaflet. Os componentes diferentes estão bem documentados e já existem alguns bons plugins de terceiros. Para ver o mapa, você também precisa adicionar Leaflet.css. Estamos importando isso em nosso arquivo main.styl.

Instalação:

yarn add react-leaflet leaflet react react-dom

Componente React:

Você pode encontrar o exemplo completo no repositório GitHub.

class ReactLeafletMap extends PureComponent {

  render() {
    // create an array with marker components
    const LeafletMarkers = markers.map(marker => (
      <Marker position={marker.latlng} key={`marker_${marker.name}`}>
        <Popup>
          <span>{marker.name}</span>
        </Popup>
      </Marker>
    ));

    return (
      <div className="map">
        <Map center={mapConfig.center} zoom={mapConfig.zoom} className="map__reactleaflet">
          <TileLayer
            url="https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_all/{z}/{x}/{y}.png"
            attribution='© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>, © <a href="https://carto.com/attribution">CARTO</a>'
          />
          {LeafletMarkers}
        </Map>
      </div>
    );
  }
}

Mapas Pigeon

Essa é uma biblioteca de mapeamento muito especial porque ela não tem dependências externas. Por enquanto, você pode apenas exibir sobreposições e marcadores em um mapa base, então isso pode ser útil para um mapa de localizador muito simples. Até agora, não há pop-ups para os marcadores implementados, então você precisaria encontrar sua própria lógica. Para obter mais informações, consulte a página do projeto.

Instalação

yarn add pigeon-maps pigeon-marker

Componente React

Você pode encontrar o exemplo completo no repositório GitHub.

class PigeonMaps extends PureComponent {

  onMarkerClick(evt) {
    console.log(evt.payload);
  }

  render() {
    // create an array with marker components
    const PigeonMarkers = markers.map(marker => (
      <Marker key={`marker_${marker.name}`} anchor={marker.latlng} payload={marker.name} onClick={this.onMarkerClick} />
    ));

    return (
      <div className="map">
        <Map
          width={window.innerWidth}
          height={600}
          defaultCenter={mapConfig.center}
          defaultZoom={mapConfig.zoom}
          provider={getProvider}
        >
          {PigeonMarkers}
        </Map>
      </div>
    );
  }
}

Google Map React

A biblioteca do Google Map React envolve o seu mapa do Google como um componente React. O legal sobre isso é que ele permite que você renderize qualquer componente React no mapa, o que lhe dá a oportunidade de criar facilmente marcadores personalizados. Outra característica útil é que os componentes fornecidos pela API do Google Maps (por exemplo, Camadas de Tráfego ou Trânsito ou uma caixa de pesquisa) também podem ser incluídos no mapa.

Certifique-se de incluir a chave da API do Google Maps. A configuração é muito simples:

Instalação:

yarn add google-map-react react

Componente React:

O exemplo completo está disponível no repositório GitHub.

const CustomMarker = ({ text }) => <div className="custom-marker"><p>{text}</p></div>;

class GoogleMapReactComponent extends PureComponent {
  render() {
    const GoogleMapsMarkers = markers.map(marker => (
      <CustomMarker
        key={`marker_${marker.name}`}
        lat={marker.latlng[0]}
        lng={marker.latlng[1]}
        text={marker.name}
      />
    ));

    return (
      <GoogleMapReact
        defaultCenter={mapConfig.center}
        defaultZoom={mapConfig.zoom}
        layerTypes={['TrafficLayer', 'TransitLayer']}
        bootstrapURLKeys={{
          key: CONFIG.GOOGLE_MAPS_API_KEY,
          language: 'de'
        }}
      >
        {GoogleMapsMarkers}
      </GoogleMapReact>
    );
  }
}

React MapGL

Se você precisar de suporte para WebGL em seu projeto, deve considerar o uso de React MapGL. Ele é um wrapper react para Mapbox GL que usa o WebGL para fazer mapas a partir de tiles vetoriais.

A biblioteca tem uma API pequena e algumas sobreposições de amostra como scatterplot de dispersão. Outras sobreposições úteis são fornecidas, por exemplo, por deck.gl. Como exemplo, criamos um mapa simples usando a sobreposição scatterplot. Na próxima seção, você pode encontrar uma versão que usa deck.gl.

A configuração é um pouco complicada e os documentos parecem ser um pouco confusos porque o readme do repositório e os documentos são diferentes. Observe que é necessário instalar immutable e fornecer uma chave Mapbox API para começar.

Instalação:

yarn add react-map-gl immutable react react-dom

Componente React:

Você pode encontrar o exemplo completo no repositório GitHub.

class ReactMapGL extends PureComponent {
  constructor(props) {
    super(props);

    this.state = {
      viewport: {
        width: window.innerWidth,
        height: 600,
        latitude: mapConfig.center[0],
        longitude: mapConfig.center[1],
        zoom: mapConfig.zoom,
        isDragging: false,
        startDragLngLat: mapConfig.center,
        pitch: 50,
        bearing: 0
      }
    };

    this.onChangeViewport = this.onChangeViewport.bind(this);
  }

  onChangeViewport(viewport) {
    this.setState({
      viewport: { ...this.state.viewport, ...viewport }
    });
  }

  render() {
    const { viewport } = this.state;
    return (
      <div className="reactmapgl">
        <MapGL
          {...viewport}
          mapboxApiAccessToken={CONFIG.MAPBOX_ACCESS_TOKEN}
          perspectiveEnabled
          onChangeViewport={this.onChangeViewport}
        >
          <ScatterplotOverlay
            {...viewport}
            locations={locations}
            dotRadius={2}
            globalOpacity={1}
            compositeOperation="screen"
            dotFill="#1FBAD6"
            renderWhileDragging
          />
        </MapGL>
      </div>
    );
  }
}

React MapGL com Deck.GL

Deck.GL foi desenvolvido pela equipe do Uber e é um framework que fornece belas sobreposições para mapas renderizados com Mapbox GL. No site do projeto, você encontrará um tutorial que mostra como implementar diferentes camadas em seu mapa. O exemplo abaixo usa o GeoJsonLayer e o ScreenGridLayer (com 50.000 pontos).

Instalação:

yarn add react-map-gl immutable react react-dom deck.gl luma.gl

Componente React:

Você pode encontrar o exemplo completo no repositório GitHub.

class ReactMapGLDeckGL extends PureComponent {
  constructor(props) {
    super(props);

    this.state = {
      viewport: {
        width: window.innerWidth,
        height: 600,
        latitude: mapConfig.center[0],
        longitude: mapConfig.center[1],
        zoom: mapConfig.zoom,
        isDragging: false,
        startDragLngLat: mapConfig.center,
        pitch: 50,
        bearing: 0
      },
      geojson: null
    };

    requestJson('data/berlin_bezirke.json', (error, response) => {
      if (!error) {
        this.setState({ geojson: response });
      }
    });


    this.onChangeViewport = this.onChangeViewport.bind(this);
  }

  onChangeViewport(viewport) {
    this.setState({
      viewport: { ...this.state.viewport, ...viewport }
    });
  }

  initialize(gl) {
    gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE, gl.ONE_MINUS_DST_ALPHA, gl.ONE);
    gl.blendEquation(gl.FUNC_ADD);
  }

  render() {
    const { viewport, geojson } = this.state;

    const geosjsonLayer = new GeoJsonLayer({
      id: 'geojson-layer',
      data: geojson,
      filled: false,
      stroked: true,
      lineWidthMinPixels: 1,
      lineWidthScale: 1,
      getLineColor: d => [175, 175, 175]
    });

    const screenGridLayer = new ScreenGridLayer({
      id: 'screen-grid-layer',
      data: scatterPlotData,
      cellSizePixels: 10,
      minColor: [43, 140, 190, 0],
      maxColor: [43, 140, 190, 255]
    });

    return (
      <div className="reactmapgldeckgl">
        <MapGL
          {...viewport}
          mapboxApiAccessToken={CONFIG.MAPBOX_ACCESS_TOKEN}
          mapStyle="mapbox://styles/mapbox/dark-v9"
          perspectiveEnabled
          onChangeViewport={this.onChangeViewport}
        >
          <DeckGL
            {...viewport}
            layers={[geosjsonLayer, screenGridLayer]}
            onWebGLInitialized={this.initialize}
          />
        </MapGL>
      </div>
    );
  }
}

***

Christine Wiederer faz parte do time de colunistas internacionais do iMasters. A tradução do artigo é feita pela redação iMasters, com autorização do autor, e você pode acompanhar o artigo em inglês no link: https://blog.webkid.io/making-maps-with-react/.