import React from 'react' import style from '../../libs/style.js' import isEqual from 'lodash.isequal' import { loadJSON } from '../../libs/urlopen' import 'openlayers/dist/ol.css' function suitableVectorSource(mapStyle) { const sources = Object.keys(mapStyle.sources) .map(sourceId => { return { id: sourceId, source: mapStyle.sources[sourceId] } }) .filter(({source}) => (source.type === 'vector' || source.type === 'geojson')) return sources[0] } function toVectorLayer(source, tilegrid, cb) { function newMVTLayer(tileUrl) { const ol = require('openlayers') return new ol.layer.VectorTile({ source: new ol.source.VectorTile({ format: new ol.format.MVT(), tileGrid: tilegrid, tilePixelRatio: 8, url: tileUrl }) }) } function newGeoJSONLayer(sourceUrl) { const ol = require('openlayers') return new ol.layer.Vector({ source: new ol.source.Vector({ format: new ol.format.GeoJSON(), url: sourceUrl }) }) } if (source.type === 'vector') { if(!source.tiles) { sourceFromTileJSON(source.url, tileSource => { cb(newMVTLayer(tileSource.tiles[0])) }) } else { cb(newMVTLayer(source.tiles[0])) } } else if (source.type === 'geojson') { cb(newGeoJSONLayer(source.data)) } } function sourceFromTileJSON(url, cb) { loadJSON(url, null, tilejson => { if(!tilejson) return cb({ type: 'vector', tiles: tilejson.tiles, minzoom: tilejson.minzoom, maxzoom: tilejson.maxzoom, }) }) } class OpenLayers3Map extends React.Component { static propTypes = { onDataChange: React.PropTypes.func, mapStyle: React.PropTypes.object.isRequired, accessToken: React.PropTypes.string, } static defaultProps = { onMapLoaded: () => {}, onDataChange: () => {}, } constructor(props) { super(props) this.tilegrid = null this.resolutions = null this.layer = null this.map = null } updateStyle(newMapStyle) { const oldSource = suitableVectorSource(this.props.mapStyle) const newSource = suitableVectorSource(newMapStyle) const resolutions = this.resolutions function setStyleFunc(map, layer) { const olms = require('ol-mapbox-style') const styleFunc = olms.getStyleFunction(newMapStyle, newSource.id, resolutions) layer.setStyle(styleFunc) //NOTE: We need to mark the source as changed in order //to trigger a rerender layer.getSource().changed() map.render() } if(newSource) { if(this.layer && !isEqual(oldSource, newSource)) { this.map.removeLayer(this.layer) this.layer = null } if(!this.layer) { var self = this toVectorLayer(newSource.source, this.tilegrid, vectorLayer => { self.layer = vectorLayer self.map.addLayer(self.layer) setStyleFunc(self.map, self.layer) }) } else { setStyleFunc(this.map, this.layer) } } } componentWillReceiveProps(nextProps) { require.ensure(["openlayers", "ol-mapbox-style"], () => { if(!this.map || !this.resolutions) return this.updateStyle(nextProps.mapStyle) }) } componentDidMount() { //Load OpenLayers dynamically once we need it //TODO: Make this more convenient require.ensure(["openlayers", "ol-mapbox-style"], ()=> { console.log('Loaded OpenLayers3 renderer') const ol = require('openlayers') const olms = require('ol-mapbox-style') this.tilegrid = ol.tilegrid.createXYZ({tileSize: 512, maxZoom: 22}) this.resolutions = this.tilegrid.getResolutions() const map = new ol.Map({ target: this.container, layers: [], view: new ol.View({ zoom: 2, center: [52.5, -78.4] }) }) map.addControl(new ol.control.Zoom()) this.map = map this.updateStyle(this.props.mapStyle) }) } render() { return
this.container = x} style={{ position: "fixed", top: 40, right: 0, bottom: 0, height: 'calc(100% - 40px)', width: "75%", backgroundColor: '#fff', ...this.props.style, }}>
} } export default OpenLayers3Map