socib / Leaflet.TimeDimension

Add time dimension capabilities on a Leaflet map.
MIT License
435 stars 138 forks source link

React and Leaflet.TimeDimension #153

Closed ErikaHD closed 6 years ago

ErikaHD commented 6 years ago

Hi, Has anyone try to use Leaflet.TimeDimension with React? I will really appreciate any help on how to do this. Thank you :-)

murb commented 6 years ago

Don't have time for a full explainer, but here is some code that I've written. Warning: it's not the most elegant code, but the problem with leaflet & react is that react wants to manage all that is DOM, and Leaflet interferes with that.

https://github.com/murb/exons-client/blob/master/src/components/MapComponent.jsx

Note, there are some quite project specific things going on here, but maybe you can follow what is happening.

ErikaHD commented 6 years ago

Thank you murb for your answer. I will look into it now. Did you try this library with Angular? I am thinking to use it for mobile app development.

Thanks again

murb commented 6 years ago

Nope, not used with angular or any other (semi-)native solution

Op 18 jul. 2018 om 11:16 heeft ErikaHD notifications@github.com het volgende geschreven:

Thank you murb for your answer. I will look into it now. Did you try this library with Angular? I am thinking to use it for mobile app development.

Thanks again

— You are receiving this because you commented. Reply to this email directly, view it on GitHub, or mute the thread.

ErikaHD commented 6 years ago

Thank you for your help again. I really appreciate it.

benjamineac commented 1 year ago

@murb would I be able to take a look at the code you linked earlier? Seems the link is broken

murb commented 1 year ago

@benjamineac this is the code at the time it was still in the open:

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import Leaflet from 'leaflet';
import 'leaflet.nontiledlayer';
import 'leaflet-timedimension';

class MapComponent extends Component {
  constructor (props) {
    super(props);
    this.initializeMap = this.initializeMap.bind(this);
    this.dimensionSummary = '';
  }

  initializeMap (props) {
    if (this.currentMap) { this.currentMap.remove(); }

    const { timeInterval, zoom, centerLatLon, timeDimensionControl, geovisActions, dispatch } = props;

    this.currentMap = Leaflet.map('map-8d7b0efd-96bb-4b8f-8ccd-936edbeac503', {
      buffer: 20,
      crs: Leaflet.CRS.EPSG3857,
      timeDimension: true,
      timeDimensionOptions: {
        timeInterval: timeInterval,
        period: 'PT5M'
      },
      timeDimensionControlOptions: {
        position: 'bottomleft',
        playerOptions: {
          minBufferReady: 4,
          transitionTime: 500
        }
      },
      timeDimensionControl: timeDimensionControl
    }).setView(centerLatLon, zoom);

    Leaflet.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
      attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
    }).addTo(this.currentMap);
    this.currentMapLayers = Leaflet.layerGroup();
    this.currentMapLayers.addTo(this.currentMap);
    this.currentMapHotSpotLayers = Leaflet.layerGroup();
    this.currentMapHotSpotLayers.addTo(this.currentMap);

    this.currentLinesLayers = Leaflet.layerGroup();
    this.currentLinesLayers.addTo(this.currentMap);

    window.leafletMap = this.currentMap;
  }

  componentDidMount () {
   this.componentWillUpdate(this.props);
  }

  componentWillUpdate (nextProps) {
    const { layers, hotSpots, geovisActions, dispatch, wmsDimensions, timeDimensionControl, focus } = nextProps;
    if (
      nextProps.viewName !== this.props.viewName ||
      nextProps.timeDimensionControl !== this.props.timeDimensionControl ||
      typeof this.currentMap === 'undefined'
    ) {
      this.initializeMap({
        timeDimensionControl: timeDimensionControl,
        timeInterval: nextProps.timeInterval,
        zoom: nextProps.zoom,
        centerLatLon: nextProps.centerLatLon
      });
      this.lastUpdatedMapLayersAt = 0;
    }

    if (this.currentMapHotSpotLayers) this.currentMapHotSpotLayers.clearLayers();

    const defaultIconOptions = {
      iconUrl: '/marker-blue.png',
      iconSize: [25, 43],
      iconAnchor: [12, 43],
      popupAnchor: [-3, -76],
      shadowUrl: '/marker-shadow.png',
      shadowSize: [31, 21],
      shadowAnchor: [0, 21]
    };

    const defaultIcon = Leaflet.icon(
      defaultIconOptions
    );

    const selectedIcon = Leaflet.icon(
      Object.assign(defaultIconOptions, { iconUrl: '/marker-lightblue.png' })
    );

    hotSpots.forEach((hotspot) => {
      const selected = focus && hotspot.dataUrl === focus.dataUrl;
      const marker = Leaflet.marker(hotspot.centerLatLon, {
        icon: (selected ? selectedIcon : defaultIcon), alt: hotspot.name, title: hotspot.name
      });
      marker.on('click', () => {
        dispatch(geovisActions.setFocus(hotspot));
        dispatch(geovisActions.fetchTimeseries({ url: hotspot.dataUrl }));
      });
      marker.addTo(this.currentMapHotSpotLayers);
    });

    let newDimensionSummary = '';
    for (const dimension in wmsDimensions) {
      newDimensionSummary = newDimensionSummary + dimension + wmsDimensions[dimension];
    }
    // every 5 min is ok for maplayers
    const mapLayerRefreshNeeded = (new Date()).getTime() - this.lastUpdatedMapLayersAt > 1000 * 60 * 1;
    if (mapLayerRefreshNeeded || newDimensionSummary !== this.dimensionSummary) {
      this.lastUpdatedMapLayersAt = (new Date()).getTime();
      this.dimensionSummary = newDimensionSummary;
    } else {
      return false;
    }

    if (this.currentMapLayers) this.currentMapLayers.clearLayers();

    layers.forEach((layer) => {
      const variableWMSLayer = layer.wmsLayer.match(/\{(.*)\}/);

      const wmsLayer =  (variableWMSLayer) ? layer.wmsLayer.replace(variableWMSLayer[0], wmsDimensions[variableWMSLayer[1]]) : layer.wmsLayer;

      let layerOptions = {
        layers: wmsLayer,
        styles: layer.styles,
        format: 'image/png',
        opacity: (layer.opacity ? layer.opacity : 1),
        transparent: true,
        tileSize: 1500,
        updateWhenIdle: false,
        updateWhenZooming: false,
        updateInterval: 600
      };
      for (const dimension in wmsDimensions) {
        layerOptions[`DIM_${dimension}`] = wmsDimensions[dimension];
      }

      const llayer = (layer.timeLayer) ? Leaflet.timeDimension.layer.wms(Leaflet.tileLayer.wms(layer.wms, layerOptions)) : Leaflet.nonTiledLayer.wms(layer.wms, layerOptions);

      llayer.addTo(this.currentMapLayers);
    });

    dispatch(geovisActions.fetchMapMetaData(layers[0], nextProps.focusLatLon, wmsDimensions));
  }

  render () {
    return (
      <div id='map-8d7b0efd-96bb-4b8f-8ccd-936edbeac503' className='leaflet-map' />
    );
  }
}

MapComponent.propTypes = {
  viewName: PropTypes.string,
  timeDimensionControl: PropTypes.bool,
  geovisActions: PropTypes.object,
  dispatch: PropTypes.func
};

export default MapComponent;