From 1538f2e17433bbe1ee9ec1af042f6e7263df5eac Mon Sep 17 00:00:00 2001 From: Lukas Martinelli Date: Sun, 8 Jan 2017 23:19:21 +0100 Subject: [PATCH] Get highlight working --- src/components/App.jsx | 5 +- src/components/map/InspectionMap.jsx | 126 ------------------------ src/components/map/MapboxGlMap.jsx | 38 +++++++- src/libs/highlight.js | 36 +++++++ src/libs/stylegen.js | 141 --------------------------- 5 files changed, 73 insertions(+), 273 deletions(-) delete mode 100644 src/components/map/InspectionMap.jsx create mode 100644 src/libs/highlight.js diff --git a/src/components/App.jsx b/src/components/App.jsx index 27a10cc..29d5305 100644 --- a/src/components/App.jsx +++ b/src/components/App.jsx @@ -2,7 +2,6 @@ import React from 'react' import { saveAs } from 'file-saver' import Mousetrap from 'mousetrap' -import InspectionMap from './map/InspectionMap' import MapboxGlMap from './map/MapboxGlMap' import OpenLayers3Map from './map/OpenLayers3Map' import LayerList from './layers/LayerList' @@ -177,7 +176,9 @@ export default class App extends React.Component { if(renderer === 'ol3') { return } else { - return + return } } diff --git a/src/components/map/InspectionMap.jsx b/src/components/map/InspectionMap.jsx deleted file mode 100644 index d672698..0000000 --- a/src/components/map/InspectionMap.jsx +++ /dev/null @@ -1,126 +0,0 @@ -import React from 'react' -import ReactDOM from 'react-dom' -import MapboxGl from 'mapbox-gl/dist/mapbox-gl.js' -import validateColor from 'mapbox-gl-style-spec/lib/validate/validate_color' -import colors from '../../config/colors' -import style from '../../libs/style' -import FeaturePropertyPopup from './FeaturePropertyPopup' -import { colorHighlightedLayer, generateColoredLayers } from '../../libs/stylegen' -import 'mapbox-gl/dist/mapbox-gl.css' -import '../../mapboxgl.css' - -function convertInspectStyle(mapStyle, sources, highlightedLayer) { - const coloredLayers = generateColoredLayers(sources) - - const layer = colorHighlightedLayer(highlightedLayer) - if(layer) { - coloredLayers.push(layer) - } - - const newStyle = { - ...mapStyle, - layers: [ - { - "id": "background", - "type": "background", - "paint": { - "background-color": colors.black, - } - }, - ...coloredLayers, - ] - } - return newStyle -} - -function renderPopup(features) { - var mountNode = document.createElement('div'); - ReactDOM.render(, mountNode) - return mountNode.innerHTML; -} - -export default class InspectionMap extends React.Component { - static propTypes = { - onDataChange: React.PropTypes.func, - sources: React.PropTypes.object, - originalStyle: React.PropTypes.object, - highlightedLayer: React.PropTypes.object, - style: React.PropTypes.object, - } - - static defaultProps = { - onMapLoaded: () => {}, - onTileLoaded: () => {}, - } - - constructor(props) { - super(props) - this.state = { map: null } - } - - componentWillReceiveProps(nextProps) { - if(!this.state.map) return - - this.state.map.setStyle(convertInspectStyle(nextProps.mapStyle, this.props.sources, nextProps.highlightedLayer), { diff: true}) - } - - componentDidMount() { - MapboxGl.accessToken = this.props.accessToken - - const map = new MapboxGl.Map({ - container: this.container, - style: convertInspectStyle(this.props.mapStyle, this.props.sources, this.props.highlightedLayer), - hash: true, - }) - - const nav = new MapboxGl.NavigationControl(); - map.addControl(nav, 'top-right'); - - map.on("style.load", () => { - this.setState({ map }); - }) - - map.on("data", e => { - if(e.dataType !== 'tile') return - this.props.onDataChange({ - map: this.state.map - }) - }) - - map.on('click', this.displayPopup.bind(this)) - map.on('mousemove', function(e) { - var features = map.queryRenderedFeatures(e.point, { layers: this.layers }) - map.getCanvas().style.cursor = (features.length) ? 'pointer' : '' - }) - } - - displayPopup(e) { - const features = this.state.map.queryRenderedFeatures(e.point, { - layers: this.layers - }); - - if (!features.length) { - return - } - - // Populate the popup and set its coordinates - // based on the feature found. - const popup = new MapboxGl.Popup() - .setLngLat(e.lngLat) - .setHTML(renderPopup(features)) - .addTo(this.state.map) - } - - render() { - return
this.container = x} - style={{ - position: "fixed", - top: 0, - bottom: 0, - height: "100%", - width: "100%", - ...this.props.style, - }}>
- } -} diff --git a/src/components/map/MapboxGlMap.jsx b/src/components/map/MapboxGlMap.jsx index 794ad28..2599eeb 100644 --- a/src/components/map/MapboxGlMap.jsx +++ b/src/components/map/MapboxGlMap.jsx @@ -5,7 +5,9 @@ import MapboxInspect from 'mapbox-gl-inspect' import FeatureLayerTable from './FeatureLayerTable' import FeaturePropertyPopup from './FeaturePropertyPopup' import validateColor from 'mapbox-gl-style-spec/lib/validate/validate_color' +import colors from '../../config/colors' import style from '../../libs/style.js' +import { colorHighlightedLayer } from '../../libs/highlight' import 'mapbox-gl/dist/mapbox-gl.css' import '../../mapboxgl.css' @@ -21,6 +23,27 @@ function renderPropertyPopup(features) { return mountNode.innerHTML; } +function buildInspectStyle(originalMapStyle, coloredLayers, highlightedLayer) { + const backgroundLayer = { + "id": "background", + "type": "background", + "paint": { + "background-color": colors.black, + } + } + + const layer = colorHighlightedLayer(highlightedLayer) + if(layer) { + coloredLayers.push(layer) + } + + const inspectStyle = { + ...originalMapStyle, + layers: [backgroundLayer].concat(coloredLayers) + } + return inspectStyle +} + export default class MapboxGlMap extends React.Component { static propTypes = { onDataChange: React.PropTypes.func, @@ -28,6 +51,7 @@ export default class MapboxGlMap extends React.Component { accessToken: React.PropTypes.string, style: React.PropTypes.object, inspectModeEnabled: React.PropTypes.bool.isRequired, + highlightedLayer: React.PropTypes.object, } static defaultProps = { @@ -52,10 +76,6 @@ export default class MapboxGlMap extends React.Component { if(!this.state.map) return - if(this.props.inspectModeEnabled !== nextProps.inspectModeEnabled) { - this.state.inspect.toggleInspector() - } - if(!nextProps.inspectModeEnabled) { //Mapbox GL now does diffing natively so we don't need to calculate //the necessary operations ourselves! @@ -63,6 +83,15 @@ export default class MapboxGlMap extends React.Component { } } + componentDidUpdate(prevProps) { + if(this.props.inspectModeEnabled !== prevProps.inspectModeEnabled) { + this.state.inspect.toggleInspector() + } + if(this.props.inspectModeEnabled) { + this.state.inspect._renderInspector() + } + } + componentDidMount() { const map = new MapboxGl.Map({ container: this.container, @@ -80,6 +109,7 @@ export default class MapboxGlMap extends React.Component { }), showMapPopup: true, showInspectButton: false, + buildInspectStyle: (originalMapStyle, coloredLayers) => buildInspectStyle(originalMapStyle, coloredLayers, this.props.highlightedLayer), renderPopup: features => { if(this.props.inspectModeEnabled) { return renderPropertyPopup(features) diff --git a/src/libs/highlight.js b/src/libs/highlight.js new file mode 100644 index 0000000..5e050ef --- /dev/null +++ b/src/libs/highlight.js @@ -0,0 +1,36 @@ +import randomColor from 'randomcolor' +import Color from 'color' + +import stylegen from 'mapbox-gl-inspect/lib/stylegen' +import colors from 'mapbox-gl-inspect/lib/colors' + +export function colorHighlightedLayer(layer) { + if(!layer || layer.type === 'background' || layer.type === 'raster') return null + + function changeLayer(l) { + if(layer.filter) { + l.filter = layer.filter + } else { + delete l['filter'] + } + l.id = l.id + '_highlight' + return l + } + + const color = colors.brightColor(layer.id, 1) + const layers = [] + + if(layer.type === "fill" || layer.type === 'fill-extrusion') { + return changeLayer(stylegen.polygonLayer(color, color, layer.source, layer['source-layer'])) + } + + if(layer.type === "symbol" || layer.type === 'circle') { + return changeLayer(stylegen.circleLayer(color, layer.source, layer['source-layer'])) + } + + if(layer.type === 'line') { + return changeLayer(stylegen.lineLayer(color, layer.source, layer['source-layer'])) + } + + return null +} diff --git a/src/libs/stylegen.js b/src/libs/stylegen.js index f4decc5..e69de29 100644 --- a/src/libs/stylegen.js +++ b/src/libs/stylegen.js @@ -1,141 +0,0 @@ -import randomColor from 'randomcolor' -import Color from 'color' - -function assignVectorLayerColor(layerId) { - let hue = null - if(/water|ocean|lake|sea|river/.test(layerId)) { - hue = 'blue' - } - - if(/road|highway|transport/.test(layerId)) { - hue = 'orange' - } - - if(/building/.test(layerId)) { - hue = 'yellow' - } - - if(/wood|forest|park|landcover|landuse/.test(layerId)) { - hue = 'green' - } - - return randomColor({ - luminosity: 'bright', - hue: hue, - seed: layerId, - }) -} - -function circleLayer(source, vectorLayer, color) { - const layer = { - id: `${source}_${vectorLayer}_circle`, - source: source, - type: 'circle', - paint: { - 'circle-color': color, - 'circle-radius': 2, - }, - filter: ["==", "$type", "Point"] - } - if(vectorLayer) { - layer['source-layer'] = vectorLayer - } - return layer -} - -function polygonLayer(source, vectorLayer, color, fillColor) { - const layer = { - id: `${source}_${vectorLayer}_polygon`, - source: source, - type: 'fill', - paint: { - 'fill-color': fillColor, - 'fill-antialias': true, - 'fill-outline-color': color, - }, - filter: ["==", "$type", "Polygon"] - } - if(vectorLayer) { - layer['source-layer'] = vectorLayer - } - return layer -} - -function lineLayer(source, vectorLayer, color) { - const layer = { - id: `${source}_${vectorLayer}_line`, - source: source, - layout: { - 'line-join': 'round', - 'line-cap': 'round' - }, - type: 'line', - paint: { - 'line-color': color, - }, - filter: ["==", "$type", "LineString"] - } - if(vectorLayer) { - layer['source-layer'] = vectorLayer - } - return layer -} - -export function colorHighlightedLayer(layer) { - if(!layer || layer.type === 'background' || layer.type === 'raster') return null - - function changeLayer(l) { - if(layer.filter) { - l.filter = layer.filter - } else { - delete l['filter'] - } - l.id = l.id + '_highlight' - return l - } - - const color = assignVectorLayerColor(layer.id) - const layers = [] - - if(layer.type === "fill" || layer.type === 'fill-extrusion') { - return changeLayer(polygonLayer(layer.source, layer['source-layer'], color, Color(color).alpha(0.2).string())) - } - - if(layer.type === "symbol" || layer.type === 'circle') { - return changeLayer(circleLayer(layer.source, layer['source-layer'], color)) - } - - if(layer.type === 'line') { - return changeLayer(lineLayer(layer.source, layer['source-layer'], color)) - } - - return null -} - -export function generateColoredLayers(sources) { - const polyLayers = [] - const circleLayers = [] - const lineLayers = [] - - Object.keys(sources).forEach(sourceId => { - const layers = sources[sourceId] - - // Deal with GeoJSON sources that do not have any source layers - if(!layers) { - const color = Color(assignVectorLayerColor(sourceId)) - circleLayers.push(circleLayer(sourceId, null, color.alpha(0.3).string())) - lineLayers.push(lineLayer(sourceId, null, color.alpha(0.3).string())) - polyLayers.push(polygonLayer(sourceId, null, color.alpha(0.2).string(), color.alpha(0.05).string())) - return - } - - layers.forEach(layerId => { - const color = Color(assignVectorLayerColor(layerId)) - circleLayers.push(circleLayer(sourceId, layerId, color.alpha(0.3).string())) - lineLayers.push(lineLayer(sourceId, layerId, color.alpha(0.3).string())) - polyLayers.push(polygonLayer(sourceId, layerId, color.alpha(0.2).string(), color.alpha(0.05).string())) - }) - }) - - return polyLayers.concat(lineLayers).concat(circleLayers) -}