Desenvolvimento

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/.