mirror of
https://github.com/a-nyx/maputnik-with-pmtiles.git
synced 2024-12-28 17:11: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 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 <OpenLayers3Map {...mapProps} />
|
||||
} 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 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)
|
||||
|
|
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