diff --git a/package-lock.json b/package-lock.json index c90af1f3..7aea5cf1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8356,6 +8356,11 @@ "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", "dev": true }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" + }, "lodash.isequal": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", diff --git a/package.json b/package.json index 6fe3fd18..71bad716 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "lodash.capitalize": "^4.2.1", "lodash.clamp": "^4.0.3", "lodash.clonedeep": "^4.5.0", + "lodash.get": "^4.4.2", "lodash.isequal": "^4.5.0", "lodash.throttle": "^4.1.1", "mapbox-gl": "^0.53.1", diff --git a/src/components/App.jsx b/src/components/App.jsx index b24cb1b7..cc598f5c 100644 --- a/src/components/App.jsx +++ b/src/components/App.jsx @@ -2,6 +2,7 @@ import autoBind from 'react-autobind'; import React from 'react' import cloneDeep from 'lodash.clonedeep' import clamp from 'lodash.clamp' +import get from 'lodash.get' import {arrayMove} from 'react-sortable-hoc' import url from 'url' @@ -218,6 +219,9 @@ export default class App extends React.Component { showCollisionBoxes: false, showOverdrawInspector: false, }, + openlayersDebugOptions: { + debugToolbox: false, + }, } this.layerWatcher = new LayerWatcher({ @@ -276,6 +280,27 @@ export default class App extends React.Component { }) } + onChangeMetadataProperty = (property, value) => { + // If we're changing renderer reset the map state. + if ( + property === 'maputnik:renderer' && + value !== get(this.state.mapStyle, ['metadata', 'maputnik:renderer'], 'mbgljs') + ) { + this.setState({ + mapState: 'map' + }); + } + + const changedStyle = { + ...this.state.mapStyle, + metadata: { + ...this.state.mapStyle.metadata, + [property]: value + } + } + this.onStyleChanged(changedStyle) + } + onStyleChanged = (newStyle, save=true) => { const errors = validate(newStyle, latest) @@ -479,9 +504,10 @@ export default class App extends React.Component { } mapRenderer() { + const metadata = this.state.mapStyle.metadata || {}; + const mapProps = { mapStyle: style.replaceAccessTokens(this.state.mapStyle, {allowFallback: true}), - options: this.state.mapboxGlDebugOptions, onDataChange: (e) => { this.layerWatcher.analyzeMap(e.map) this.fetchSources(); @@ -496,9 +522,12 @@ export default class App extends React.Component { if(renderer === 'ol') { mapElement = } else { mapElement = @@ -540,6 +569,15 @@ export default class App extends React.Component { this.setModal(modalName, !this.state.isOpen[modalName]); } + onChangeOpenlayersDebug = (key, value) => { + this.setState({ + openlayersDebugOptions: { + ...this.state.openlayersDebugOptions, + [key]: value, + } + }); + } + onChangeMaboxGlDebug = (key, value) => { this.setState({ mapboxGlDebugOptions: { @@ -555,6 +593,7 @@ export default class App extends React.Component { const metadata = this.state.mapStyle.metadata || {} const toolbar = @@ -615,8 +656,10 @@ export default class App extends React.Component { - + {feature.layer.type && + + } {feature.layer.id} {feature.counter && × {feature.counter}} diff --git a/src/components/map/OpenLayersMap.jsx b/src/components/map/OpenLayersMap.jsx index 9cb5ee24..b1ee83a7 100644 --- a/src/components/map/OpenLayersMap.jsx +++ b/src/components/map/OpenLayersMap.jsx @@ -3,10 +3,26 @@ import {throttle} from 'lodash'; import PropTypes from 'prop-types' import { loadJSON } from '../../libs/urlopen' +import FeatureLayerPopup from './FeatureLayerPopup'; + import 'ol/ol.css' import {apply} from 'ol-mapbox-style'; -import {Map, View} from 'ol'; +import {Map, View, Proj, Overlay} from 'ol'; +import {toLonLat} from 'ol/proj'; +import {toStringHDMS} from 'ol/coordinate'; + + +function renderCoords (coords) { + if (!coords || coords.length < 2) { + return null; + } + else { + return + {coords.map((coord) => String(coord).padStart(7, "\u00A0")).join(', ')} + + } +} export default class OpenLayersMap extends React.Component { static propTypes = { @@ -14,15 +30,24 @@ export default class OpenLayersMap extends React.Component { mapStyle: PropTypes.object.isRequired, accessToken: PropTypes.string, style: PropTypes.object, + onLayerSelect: PropTypes.func.isRequired, + debugToolbox: PropTypes.bool.isRequired, } static defaultProps = { onMapLoaded: () => {}, onDataChange: () => {}, + onLayerSelect: () => {}, } constructor(props) { super(props); + this.state = { + zoom: 0, + rotation: 0, + cursor: [], + center: [], + }; this.updateStyle = throttle(this._updateStyle.bind(this), 200); } @@ -34,33 +59,108 @@ export default class OpenLayersMap extends React.Component { apply(this.map, newMapStyle); } - componentDidUpdate() { - this.updateStyle(this.props.mapStyle); + componentDidUpdate(prevProps) { + if (this.props.mapStyle !== prevProps.mapStyle) { + this.updateStyle(this.props.mapStyle); + } } componentDidMount() { - this.updateStyle(this.props.mapStyle); + this.overlay = new Overlay({ + element: this.popupContainer, + autoPan: true, + autoPanAnimation: { + duration: 250 + } + }); const map = new Map({ target: this.container, - layers: [], + overlays: [this.overlay], view: new View({ - zoom: 2, - center: [52.5, -78.4] + zoom: 1, + center: [180, -90], + }) + }); + + map.on('pointermove', (evt) => { + var coords = toLonLat(evt.coordinate); + this.setState({ + cursor: [ + coords[0].toFixed(2), + coords[1].toFixed(2) + ] }) }) + + map.on('postrender', (evt) => { + const center = toLonLat(map.getView().getCenter()); + this.setState({ + center: [ + center[0].toFixed(2), + center[1].toFixed(2), + ], + rotation: map.getView().getRotation().toFixed(2), + zoom: map.getView().getZoom().toFixed(2) + }); + }); + + + this.map = map; + this.updateStyle(this.props.mapStyle); + } + + closeOverlay = (e) => { + e.target.blur(); + this.overlay.setPosition(undefined); } render() { - return
this.container = x} - style={{ - width: "100%", - height: "100%", - backgroundColor: '#fff', - ...this.props.style, - }}> + return
+
this.popupContainer = x} + style={{background: "black"}} + className="maputnik-popup" + > + + +
+
+ Zoom level: {this.state.zoom} +
+ {this.props.debugToolbox && +
+
+ + {renderCoords(this.state.cursor)} +
+
+ + {renderCoords(this.state.center)} +
+
+ + {this.state.rotation} +
+
+ } +
this.container = x} + style={{ + ...this.props.style, + }}> +
} } diff --git a/src/components/modals/DebugModal.js b/src/components/modals/DebugModal.js index bddb48f3..91bf47c7 100644 --- a/src/components/modals/DebugModal.js +++ b/src/components/modals/DebugModal.js @@ -9,8 +9,10 @@ class DebugModal extends React.Component { isOpen: PropTypes.bool.isRequired, renderer: PropTypes.string.isRequired, onChangeMaboxGlDebug: PropTypes.func.isRequired, + onChangeOpenlayersDebug: PropTypes.func.isRequired, onOpenToggle: PropTypes.func.isRequired, mapboxGlDebugOptions: PropTypes.object, + openlayersDebugOptions: PropTypes.object, } render() { @@ -33,9 +35,15 @@ class DebugModal extends React.Component { } {this.props.renderer === 'ol' && -
- No debug options available for the OpenLayers renderer -
+
    + {Object.entries(this.props.openlayersDebugOptions).map(([key, val]) => { + return
  • + +
  • + })} +
}
diff --git a/src/components/modals/SettingsModal.jsx b/src/components/modals/SettingsModal.jsx index 6d38b2dc..04ca51d1 100644 --- a/src/components/modals/SettingsModal.jsx +++ b/src/components/modals/SettingsModal.jsx @@ -11,6 +11,7 @@ class SettingsModal extends React.Component { static propTypes = { mapStyle: PropTypes.object.isRequired, onStyleChanged: PropTypes.func.isRequired, + onChangeMetadataProperty: PropTypes.func.isRequired, isOpen: PropTypes.bool.isRequired, onOpenToggle: PropTypes.func.isRequired, } @@ -23,19 +24,9 @@ class SettingsModal extends React.Component { this.props.onStyleChanged(changedStyle) } - changeMetadataProperty(property, value) { - const changedStyle = { - ...this.props.mapStyle, - metadata: { - ...this.props.mapStyle.metadata, - [property]: value - } - } - this.props.onStyleChanged(changedStyle) - } - render() { const metadata = this.props.mapStyle.metadata || {} + const {onChangeMetadataProperty} = this.props; const inputProps = { } return @@ -86,7 +77,7 @@ class SettingsModal extends React.Component { @@ -94,7 +85,7 @@ class SettingsModal extends React.Component { @@ -106,9 +97,10 @@ class SettingsModal extends React.Component { ['ol', 'Open Layers (experimental)'], ]} value={metadata['maputnik:renderer'] || 'mbgljs'} - onChange={this.changeMetadataProperty.bind(this, 'maputnik:renderer')} + onChange={onChangeMetadataProperty.bind(this, 'maputnik:renderer')} /> + } diff --git a/src/styles/_map.scss b/src/styles/_map.scss index 951c654a..c45eab3a 100644 --- a/src/styles/_map.scss +++ b/src/styles/_map.scss @@ -1,7 +1,13 @@ //OPENLAYERS .maputnik-layout { .ol-zoom { - top: 10px; + top: 40px; + right: 10px; + left: auto; + } + + .ol-rotate { + top: 94px; right: 10px; left: auto; } @@ -20,3 +26,57 @@ } } } + + +.maputnik-ol { + width: 100%; + height: 100%; +} + +.maputnik-ol-popup { + background: $color-black; + +} + +.maputnik-coords { + font-family: monospace; + &:before { + content: '['; + color: #888; + } + &:after { + content: ']'; + color: #888; + } +} + +.maputnik-ol-debug { + font-family: monospace; + font-size: smaller; + position: absolute; + bottom: 10px; + left: 10px; + background: rgb(28, 31, 36); + padding: 6px 8px; + border-radius: 2px; + z-index: 9999; +} + +.maputnik-ol-zoom { + position: absolute; + right: 10px; + top: 10px; + background: #1c1f24; + border-radius: 2px; + padding: 6px 8px; + color: $color-lowgray; + z-index: 9999; + font-size: 12px; + font-weight: bold; +} + +.maputnik-ol-container { + display: flex; + flex: 1; + position: relative; +} diff --git a/src/styles/_popup.scss b/src/styles/_popup.scss index dc6caf96..2338f899 100644 --- a/src/styles/_popup.scss +++ b/src/styles/_popup.scss @@ -22,7 +22,7 @@ .maputnik-popup-layer-id { padding-left: $margin-2; - padding-right: $margin-2; + padding-right: 1.6em; background-color: $color-midgray; color: $color-white; }