mirror of
https://github.com/a-nyx/maputnik-with-pmtiles.git
synced 2024-12-28 16:41:17 +01:00
Get highlight working
This commit is contained in:
parent
580068bf63
commit
1538f2e174
5 changed files with 73 additions and 273 deletions
|
@ -2,7 +2,6 @@ import React from 'react'
|
||||||
import { saveAs } from 'file-saver'
|
import { saveAs } from 'file-saver'
|
||||||
import Mousetrap from 'mousetrap'
|
import Mousetrap from 'mousetrap'
|
||||||
|
|
||||||
import InspectionMap from './map/InspectionMap'
|
|
||||||
import MapboxGlMap from './map/MapboxGlMap'
|
import MapboxGlMap from './map/MapboxGlMap'
|
||||||
import OpenLayers3Map from './map/OpenLayers3Map'
|
import OpenLayers3Map from './map/OpenLayers3Map'
|
||||||
import LayerList from './layers/LayerList'
|
import LayerList from './layers/LayerList'
|
||||||
|
@ -177,7 +176,9 @@ export default class App extends React.Component {
|
||||||
if(renderer === 'ol3') {
|
if(renderer === 'ol3') {
|
||||||
return <OpenLayers3Map {...mapProps} />
|
return <OpenLayers3Map {...mapProps} />
|
||||||
} else {
|
} else {
|
||||||
return <MapboxGlMap {...mapProps} inspectModeEnabled={this.state.inspectModeEnabled} />
|
return <MapboxGlMap {...mapProps}
|
||||||
|
inspectModeEnabled={this.state.inspectModeEnabled}
|
||||||
|
highlightedLayer={this.state.mapStyle.layers[this.state.selectedLayerIndex]} />
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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(<FeaturePropertyPopup features={features} />, 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 <div
|
|
||||||
ref={x => this.container = x}
|
|
||||||
style={{
|
|
||||||
position: "fixed",
|
|
||||||
top: 0,
|
|
||||||
bottom: 0,
|
|
||||||
height: "100%",
|
|
||||||
width: "100%",
|
|
||||||
...this.props.style,
|
|
||||||
}}></div>
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -5,7 +5,9 @@ import MapboxInspect from 'mapbox-gl-inspect'
|
||||||
import FeatureLayerTable from './FeatureLayerTable'
|
import FeatureLayerTable from './FeatureLayerTable'
|
||||||
import FeaturePropertyPopup from './FeaturePropertyPopup'
|
import FeaturePropertyPopup from './FeaturePropertyPopup'
|
||||||
import validateColor from 'mapbox-gl-style-spec/lib/validate/validate_color'
|
import validateColor from 'mapbox-gl-style-spec/lib/validate/validate_color'
|
||||||
|
import colors from '../../config/colors'
|
||||||
import style from '../../libs/style.js'
|
import style from '../../libs/style.js'
|
||||||
|
import { colorHighlightedLayer } from '../../libs/highlight'
|
||||||
import 'mapbox-gl/dist/mapbox-gl.css'
|
import 'mapbox-gl/dist/mapbox-gl.css'
|
||||||
import '../../mapboxgl.css'
|
import '../../mapboxgl.css'
|
||||||
|
|
||||||
|
@ -21,6 +23,27 @@ function renderPropertyPopup(features) {
|
||||||
return mountNode.innerHTML;
|
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 {
|
export default class MapboxGlMap extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
onDataChange: React.PropTypes.func,
|
onDataChange: React.PropTypes.func,
|
||||||
|
@ -28,6 +51,7 @@ export default class MapboxGlMap extends React.Component {
|
||||||
accessToken: React.PropTypes.string,
|
accessToken: React.PropTypes.string,
|
||||||
style: React.PropTypes.object,
|
style: React.PropTypes.object,
|
||||||
inspectModeEnabled: React.PropTypes.bool.isRequired,
|
inspectModeEnabled: React.PropTypes.bool.isRequired,
|
||||||
|
highlightedLayer: React.PropTypes.object,
|
||||||
}
|
}
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
|
@ -52,10 +76,6 @@ export default class MapboxGlMap extends React.Component {
|
||||||
|
|
||||||
if(!this.state.map) return
|
if(!this.state.map) return
|
||||||
|
|
||||||
if(this.props.inspectModeEnabled !== nextProps.inspectModeEnabled) {
|
|
||||||
this.state.inspect.toggleInspector()
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!nextProps.inspectModeEnabled) {
|
if(!nextProps.inspectModeEnabled) {
|
||||||
//Mapbox GL now does diffing natively so we don't need to calculate
|
//Mapbox GL now does diffing natively so we don't need to calculate
|
||||||
//the necessary operations ourselves!
|
//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() {
|
componentDidMount() {
|
||||||
const map = new MapboxGl.Map({
|
const map = new MapboxGl.Map({
|
||||||
container: this.container,
|
container: this.container,
|
||||||
|
@ -80,6 +109,7 @@ export default class MapboxGlMap extends React.Component {
|
||||||
}),
|
}),
|
||||||
showMapPopup: true,
|
showMapPopup: true,
|
||||||
showInspectButton: false,
|
showInspectButton: false,
|
||||||
|
buildInspectStyle: (originalMapStyle, coloredLayers) => buildInspectStyle(originalMapStyle, coloredLayers, this.props.highlightedLayer),
|
||||||
renderPopup: features => {
|
renderPopup: features => {
|
||||||
if(this.props.inspectModeEnabled) {
|
if(this.props.inspectModeEnabled) {
|
||||||
return renderPropertyPopup(features)
|
return renderPropertyPopup(features)
|
||||||
|
|
36
src/libs/highlight.js
Normal file
36
src/libs/highlight.js
Normal file
|
@ -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
|
||||||
|
}
|
|
@ -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)
|
|
||||||
}
|
|
Loading…
Reference in a new issue