mirror of
https://github.com/maputnik/editor.git
synced 2026-01-08 06:20:00 +00:00
Merge remote-tracking branch 'upstream/master' into fix/issue-509
This commit is contained in:
@@ -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'
|
||||
|
||||
@@ -19,6 +20,7 @@ import SourcesModal from './modals/SourcesModal'
|
||||
import OpenModal from './modals/OpenModal'
|
||||
import ShortcutsModal from './modals/ShortcutsModal'
|
||||
import SurveyModal from './modals/SurveyModal'
|
||||
import DebugModal from './modals/DebugModal'
|
||||
|
||||
import { downloadGlyphsMetadata, downloadSpriteMetadata } from '../libs/metadata'
|
||||
import {latest, validate} from '@mapbox/mapbox-gl-style-spec'
|
||||
@@ -139,6 +141,12 @@ export default class App extends React.Component {
|
||||
document.querySelector(".mapboxgl-canvas").focus();
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "!",
|
||||
handler: () => {
|
||||
this.toggleModal("debug");
|
||||
}
|
||||
},
|
||||
]
|
||||
|
||||
document.body.addEventListener("keyup", (e) => {
|
||||
@@ -203,12 +211,16 @@ export default class App extends React.Component {
|
||||
open: false,
|
||||
shortcuts: false,
|
||||
export: false,
|
||||
survey: localStorage.hasOwnProperty('survey') ? false : true
|
||||
survey: localStorage.hasOwnProperty('survey') ? false : true,
|
||||
debug: false,
|
||||
},
|
||||
mapOptions: {
|
||||
showTileBoundaries: queryUtil.asBool(queryObj, "show-tile-boundaries"),
|
||||
showCollisionBoxes: queryUtil.asBool(queryObj, "show-collision-boxes"),
|
||||
showOverdrawInspector: queryUtil.asBool(queryObj, "show-overdraw-inspector")
|
||||
mapboxGlDebugOptions: {
|
||||
showTileBoundaries: false,
|
||||
showCollisionBoxes: false,
|
||||
showOverdrawInspector: false,
|
||||
},
|
||||
openlayersDebugOptions: {
|
||||
debugToolbox: false,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -217,20 +229,24 @@ export default class App extends React.Component {
|
||||
})
|
||||
}
|
||||
|
||||
handleKeyPress(e) {
|
||||
handleKeyPress = (e) => {
|
||||
if(navigator.platform.toUpperCase().indexOf('MAC') >= 0) {
|
||||
if(e.metaKey && e.shiftKey && e.keyCode === 90) {
|
||||
e.preventDefault();
|
||||
this.onRedo(e);
|
||||
}
|
||||
else if(e.metaKey && e.keyCode === 90) {
|
||||
e.preventDefault();
|
||||
this.onUndo(e);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(e.ctrlKey && e.keyCode === 90) {
|
||||
e.preventDefault();
|
||||
this.onUndo(e);
|
||||
}
|
||||
else if(e.ctrlKey && e.keyCode === 89) {
|
||||
e.preventDefault();
|
||||
this.onRedo(e);
|
||||
}
|
||||
}
|
||||
@@ -264,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)
|
||||
@@ -397,6 +434,27 @@ export default class App extends React.Component {
|
||||
})
|
||||
}
|
||||
|
||||
setDefaultValues = (styleObj) => {
|
||||
const metadata = styleObj.metadata || {}
|
||||
if(metadata['maputnik:renderer'] === undefined) {
|
||||
const changedStyle = {
|
||||
...styleObj,
|
||||
metadata: {
|
||||
...styleObj.metadata,
|
||||
'maputnik:renderer': 'mbgljs'
|
||||
}
|
||||
}
|
||||
return changedStyle
|
||||
} else {
|
||||
return styleObj
|
||||
}
|
||||
}
|
||||
|
||||
openStyle = (styleObj) => {
|
||||
styleObj = this.setDefaultValues(styleObj)
|
||||
this.onStyleChanged(styleObj)
|
||||
}
|
||||
|
||||
fetchSources() {
|
||||
const sourceList = {...this.state.sources};
|
||||
|
||||
@@ -461,18 +519,23 @@ export default class App extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
_getRenderer () {
|
||||
const metadata = this.state.mapStyle.metadata || {};
|
||||
return metadata['maputnik:renderer'] || 'mbgljs';
|
||||
}
|
||||
|
||||
mapRenderer() {
|
||||
const metadata = this.state.mapStyle.metadata || {};
|
||||
|
||||
const mapProps = {
|
||||
mapStyle: style.replaceAccessTokens(this.state.mapStyle, {allowFallback: true}),
|
||||
options: this.state.mapOptions,
|
||||
onDataChange: (e) => {
|
||||
this.layerWatcher.analyzeMap(e.map)
|
||||
this.fetchSources();
|
||||
},
|
||||
}
|
||||
|
||||
const metadata = this.state.mapStyle.metadata || {}
|
||||
const renderer = metadata['maputnik:renderer'] || 'mbgljs'
|
||||
const renderer = this._getRenderer();
|
||||
|
||||
let mapElement;
|
||||
|
||||
@@ -480,9 +543,12 @@ export default class App extends React.Component {
|
||||
if(renderer === 'ol') {
|
||||
mapElement = <OpenLayersMap
|
||||
{...mapProps}
|
||||
debugToolbox={this.state.openlayersDebugOptions.debugToolbox}
|
||||
onLayerSelect={this.onLayerSelect}
|
||||
/>
|
||||
} else {
|
||||
mapElement = <MapboxGlMap {...mapProps}
|
||||
options={this.state.mapboxGlDebugOptions}
|
||||
inspectModeEnabled={this.state.mapState === "inspect"}
|
||||
highlightedLayer={this.state.mapStyle.layers[this.state.selectedLayerIndex]}
|
||||
onLayerSelect={this.onLayerSelect} />
|
||||
@@ -524,12 +590,31 @@ 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: {
|
||||
...this.state.mapboxGlDebugOptions,
|
||||
[key]: value,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const layers = this.state.mapStyle.layers || []
|
||||
const selectedLayer = layers.length > 0 ? layers[this.state.selectedLayerIndex] : null
|
||||
const metadata = this.state.mapStyle.metadata || {}
|
||||
|
||||
const toolbar = <Toolbar
|
||||
renderer={this._getRenderer()}
|
||||
mapState={this.state.mapState}
|
||||
mapStyle={this.state.mapStyle}
|
||||
inspectModeEnabled={this.state.mapState === "inspect"}
|
||||
@@ -575,6 +660,15 @@ export default class App extends React.Component {
|
||||
|
||||
|
||||
const modals = <div>
|
||||
<DebugModal
|
||||
renderer={this._getRenderer()}
|
||||
mapboxGlDebugOptions={this.state.mapboxGlDebugOptions}
|
||||
openlayersDebugOptions={this.state.openlayersDebugOptions}
|
||||
onChangeMaboxGlDebug={this.onChangeMaboxGlDebug}
|
||||
onChangeOpenlayersDebug={this.onChangeOpenlayersDebug}
|
||||
isOpen={this.state.isOpen.debug}
|
||||
onOpenToggle={this.toggleModal.bind(this, 'debug')}
|
||||
/>
|
||||
<ShortcutsModal
|
||||
ref={(el) => this.shortcutEl = el}
|
||||
isOpen={this.state.isOpen.shortcuts}
|
||||
@@ -583,8 +677,10 @@ export default class App extends React.Component {
|
||||
<SettingsModal
|
||||
mapStyle={this.state.mapStyle}
|
||||
onStyleChanged={this.onStyleChanged}
|
||||
onChangeMetadataProperty={this.onChangeMetadataProperty}
|
||||
isOpen={this.state.isOpen.settings}
|
||||
onOpenToggle={this.toggleModal.bind(this, 'settings')}
|
||||
openlayersDebugOptions={this.state.openlayersDebugOptions}
|
||||
/>
|
||||
<ExportModal
|
||||
mapStyle={this.state.mapStyle}
|
||||
@@ -594,7 +690,7 @@ export default class App extends React.Component {
|
||||
/>
|
||||
<OpenModal
|
||||
isOpen={this.state.isOpen.open}
|
||||
onStyleOpen={this.onStyleChanged}
|
||||
onStyleOpen={this.openStyle}
|
||||
onOpenToggle={this.toggleModal.bind(this, 'open')}
|
||||
/>
|
||||
<SourcesModal
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import classnames from 'classnames'
|
||||
import {detect} from 'detect-browser';
|
||||
|
||||
import {MdFileDownload, MdOpenInBrowser, MdSettings, MdLayers, MdHelpOutline, MdFindInPage, MdAssignmentTurnedIn} from 'react-icons/md'
|
||||
|
||||
@@ -9,6 +10,11 @@ import logoImage from 'maputnik-design/logos/logo-color.svg'
|
||||
import pkgJson from '../../package.json'
|
||||
|
||||
|
||||
// This is required because of <https://stackoverflow.com/a/49846426>, there isn't another way to detect support that I'm aware of.
|
||||
const browser = detect();
|
||||
const colorAccessibilityFiltersEnabled = ['chrome', 'firefox'].indexOf(browser.name) > -1;
|
||||
|
||||
|
||||
class IconText extends React.Component {
|
||||
static propTypes = {
|
||||
children: PropTypes.node,
|
||||
@@ -108,6 +114,7 @@ export default class Toolbar extends React.Component {
|
||||
onToggleModal: PropTypes.func,
|
||||
onSetMapState: PropTypes.func,
|
||||
mapState: PropTypes.string,
|
||||
renderer: PropTypes.string,
|
||||
}
|
||||
|
||||
state = {
|
||||
@@ -133,22 +140,27 @@ export default class Toolbar extends React.Component {
|
||||
{
|
||||
id: "inspect",
|
||||
title: "Inspect",
|
||||
disabled: this.props.renderer !== 'mbgljs',
|
||||
},
|
||||
{
|
||||
id: "filter-deuteranopia",
|
||||
title: "Map (deuteranopia)",
|
||||
disabled: !colorAccessibilityFiltersEnabled,
|
||||
},
|
||||
{
|
||||
id: "filter-protanopia",
|
||||
title: "Map (protanopia)",
|
||||
disabled: !colorAccessibilityFiltersEnabled,
|
||||
},
|
||||
{
|
||||
id: "filter-tritanopia",
|
||||
title: "Map (tritanopia)",
|
||||
disabled: !colorAccessibilityFiltersEnabled,
|
||||
},
|
||||
{
|
||||
id: "filter-achromatopsia",
|
||||
title: "Map (achromatopsia)",
|
||||
disabled: !colorAccessibilityFiltersEnabled,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -201,7 +213,7 @@ export default class Toolbar extends React.Component {
|
||||
<select onChange={(e) => this.handleSelection(e.target.value)} value={currentView.id}>
|
||||
{views.map((item) => {
|
||||
return (
|
||||
<option key={item.id} value={item.id}>
|
||||
<option key={item.id} value={item.id} disabled={item.disabled}>
|
||||
{item.title}
|
||||
</option>
|
||||
);
|
||||
|
||||
@@ -13,25 +13,28 @@ class NumberInput extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
value: props.value
|
||||
editing: false,
|
||||
value: props.value,
|
||||
}
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps(props, state) {
|
||||
return {
|
||||
value: props.value
|
||||
};
|
||||
if (!state.editing) {
|
||||
return {
|
||||
value: props.value
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
changeValue(newValue) {
|
||||
this.setState({editing: true});
|
||||
const value = parseFloat(newValue)
|
||||
|
||||
const hasChanged = this.state.value !== value
|
||||
if(this.isValid(value) && hasChanged) {
|
||||
this.props.onChange(value)
|
||||
} else {
|
||||
this.setState({ value: newValue })
|
||||
}
|
||||
this.setState({ value: newValue })
|
||||
}
|
||||
|
||||
isValid(v) {
|
||||
@@ -52,6 +55,7 @@ class NumberInput extends React.Component {
|
||||
}
|
||||
|
||||
resetValue = () => {
|
||||
this.setState({editing: false});
|
||||
// Reset explicitly to default value if value has been cleared
|
||||
if(this.state.value === "") {
|
||||
return this.changeValue(this.props.default)
|
||||
|
||||
@@ -14,13 +14,16 @@ class StringInput extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
editing: false,
|
||||
value: props.value || ''
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if(this.props.value !== prevProps.value) {
|
||||
this.setState({value: this.props.value})
|
||||
static getDerivedStateFromProps(props, state) {
|
||||
if (!state.editing) {
|
||||
return {
|
||||
value: props.value
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,11 +54,15 @@ class StringInput extends React.Component {
|
||||
placeholder: this.props.default,
|
||||
onChange: e => {
|
||||
this.setState({
|
||||
editing: true,
|
||||
value: e.target.value
|
||||
})
|
||||
},
|
||||
onBlur: () => {
|
||||
if(this.state.value!==this.props.value) this.props.onChange(this.state.value)
|
||||
if(this.state.value!==this.props.value) {
|
||||
this.setState({editing: false});
|
||||
this.props.onChange(this.state.value);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import LayerIcon from '../icons/LayerIcon'
|
||||
import {latest, expression, function as styleFunction} from '@mapbox/mapbox-gl-style-spec'
|
||||
|
||||
function groupFeaturesBySourceLayer(features) {
|
||||
const sources = {}
|
||||
@@ -28,7 +29,64 @@ function groupFeaturesBySourceLayer(features) {
|
||||
class FeatureLayerPopup extends React.Component {
|
||||
static propTypes = {
|
||||
onLayerSelect: PropTypes.func.isRequired,
|
||||
features: PropTypes.array
|
||||
features: PropTypes.array,
|
||||
zoom: PropTypes.number,
|
||||
}
|
||||
|
||||
_getFeatureColor(feature, zoom) {
|
||||
// Guard because openlayers won't have this
|
||||
if (!feature.layer.paint) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const paintProps = feature.layer.paint;
|
||||
let propName;
|
||||
|
||||
if(paintProps.hasOwnProperty("text-color") && paintProps["text-color"]) {
|
||||
propName = "text-color";
|
||||
}
|
||||
else if (paintProps.hasOwnProperty("fill-color") && paintProps["fill-color"]) {
|
||||
propName = "fill-color";
|
||||
}
|
||||
else if (paintProps.hasOwnProperty("line-color") && paintProps["line-color"]) {
|
||||
propName = "line-color";
|
||||
}
|
||||
else if (paintProps.hasOwnProperty("fill-extrusion-color") && paintProps["fill-extrusion-color"]) {
|
||||
propName = "fill-extrusion-color";
|
||||
}
|
||||
|
||||
if(propName) {
|
||||
const propertySpec = latest["paint_"+feature.layer.type][propName];
|
||||
|
||||
let color = feature.layer.paint[propName];
|
||||
|
||||
if(typeof(color) === "object") {
|
||||
if(color.stops) {
|
||||
color = styleFunction.convertFunction(color, propertySpec);
|
||||
}
|
||||
|
||||
const exprResult = expression.createExpression(color, propertySpec);
|
||||
const val = exprResult.value.evaluate({
|
||||
zoom: zoom
|
||||
}, feature);
|
||||
return val.toString();
|
||||
}
|
||||
else {
|
||||
return color;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Default color
|
||||
return "black";
|
||||
}
|
||||
}
|
||||
// This is quite complex, just incase there's an edgecase we're missing
|
||||
// always return black if we get an unexpected error.
|
||||
catch (err) {
|
||||
console.error("Unable to get feature color, error:", err);
|
||||
return "black";
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -36,21 +94,33 @@ class FeatureLayerPopup extends React.Component {
|
||||
|
||||
const items = Object.keys(sources).map(vectorLayerId => {
|
||||
const layers = sources[vectorLayerId].map((feature, idx) => {
|
||||
return <label
|
||||
key={idx}
|
||||
className="maputnik-popup-layer"
|
||||
onClick={() => {
|
||||
this.props.onLayerSelect(feature.layer.id)
|
||||
}}
|
||||
const featureColor = this._getFeatureColor(feature, this.props.zoom);
|
||||
|
||||
return <div
|
||||
key={idx}
|
||||
className="maputnik-popup-layer"
|
||||
>
|
||||
<LayerIcon type={feature.layer.type} style={{
|
||||
width: 14,
|
||||
height: 14,
|
||||
paddingRight: 3
|
||||
}}/>
|
||||
{feature.layer.id}
|
||||
{feature.counter && <span> × {feature.counter}</span>}
|
||||
</label>
|
||||
<div
|
||||
className="maputnik-popup-layer__swatch"
|
||||
style={{background: featureColor}}
|
||||
></div>
|
||||
<label
|
||||
className="maputnik-popup-layer__label"
|
||||
onClick={() => {
|
||||
this.props.onLayerSelect(feature.layer.id)
|
||||
}}
|
||||
>
|
||||
{feature.layer.type &&
|
||||
<LayerIcon type={feature.layer.type} style={{
|
||||
width: 14,
|
||||
height: 14,
|
||||
paddingRight: 3
|
||||
}}/>
|
||||
}
|
||||
{feature.layer.id}
|
||||
{feature.counter && <span> × {feature.counter}</span>}
|
||||
</label>
|
||||
</div>
|
||||
})
|
||||
return <div key={vectorLayerId}>
|
||||
<div className="maputnik-popup-layer-id">{vectorLayerId}</div>
|
||||
|
||||
@@ -166,14 +166,18 @@ export default class MapboxGlMap extends React.Component {
|
||||
if(this.props.inspectModeEnabled) {
|
||||
return renderPopup(<FeaturePropertyPopup features={features} />, tmpNode);
|
||||
} else {
|
||||
return renderPopup(<FeatureLayerPopup features={features} onLayerSelect={this.props.onLayerSelect} />, tmpNode);
|
||||
return renderPopup(<FeatureLayerPopup features={features} onLayerSelect={this.props.onLayerSelect} zoom={this.state.zoom} />, tmpNode);
|
||||
}
|
||||
}
|
||||
})
|
||||
map.addControl(inspect)
|
||||
|
||||
map.on("style.load", () => {
|
||||
this.setState({ map, inspect });
|
||||
this.setState({
|
||||
map,
|
||||
inspect,
|
||||
zoom: map.getZoom()
|
||||
});
|
||||
if(this.props.inspectModeEnabled) {
|
||||
inspect.toggleInspector();
|
||||
}
|
||||
@@ -185,6 +189,12 @@ export default class MapboxGlMap extends React.Component {
|
||||
map: this.state.map
|
||||
})
|
||||
})
|
||||
|
||||
map.on("zoom", e => {
|
||||
this.setState({
|
||||
zoom: map.getZoom()
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@@ -1,11 +1,28 @@
|
||||
import React from 'react'
|
||||
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 <span className="maputnik-coords">
|
||||
{coords.map((coord) => String(coord).padStart(7, "\u00A0")).join(', ')}
|
||||
</span>
|
||||
}
|
||||
}
|
||||
|
||||
export default class OpenLayersMap extends React.Component {
|
||||
static propTypes = {
|
||||
@@ -13,53 +30,137 @@ 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);
|
||||
}
|
||||
|
||||
updateStyle(newMapStyle) {
|
||||
_updateStyle(newMapStyle) {
|
||||
if(!this.map) return;
|
||||
|
||||
// See <https://github.com/openlayers/ol-mapbox-style/issues/215#issuecomment-493198815>
|
||||
this.map.getLayers().clear();
|
||||
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 <div
|
||||
ref={x => this.container = x}
|
||||
style={{
|
||||
position: "fixed",
|
||||
top: 40,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
height: 'calc(100% - 40px)',
|
||||
width: "75%",
|
||||
backgroundColor: '#fff',
|
||||
...this.props.style,
|
||||
}}>
|
||||
return <div className="maputnik-ol-container">
|
||||
<div
|
||||
ref={x => this.popupContainer = x}
|
||||
style={{background: "black"}}
|
||||
className="maputnik-popup"
|
||||
>
|
||||
<button
|
||||
className="mapboxgl-popup-close-button"
|
||||
onClick={this.closeOverlay}
|
||||
aria-label="Close popup"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
<FeatureLayerPopup
|
||||
features={this.state.selectedFeatures || []}
|
||||
onLayerSelect={this.props.onLayerSelect}
|
||||
/>
|
||||
</div>
|
||||
<div className="maputnik-ol-zoom">
|
||||
Zoom level: {this.state.zoom}
|
||||
</div>
|
||||
{this.props.debugToolbox &&
|
||||
<div className="maputnik-ol-debug">
|
||||
<div>
|
||||
<label>cursor: </label>
|
||||
<span>{renderCoords(this.state.cursor)}</span>
|
||||
</div>
|
||||
<div>
|
||||
<label>center: </label>
|
||||
<span>{renderCoords(this.state.center)}</span>
|
||||
</div>
|
||||
<div>
|
||||
<label>rotation: </label>
|
||||
<span>{this.state.rotation}</span>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div
|
||||
className="maputnik-ol"
|
||||
ref={x => this.container = x}
|
||||
style={{
|
||||
...this.props.style,
|
||||
}}>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
53
src/components/modals/DebugModal.js
Normal file
53
src/components/modals/DebugModal.js
Normal file
@@ -0,0 +1,53 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import Modal from './Modal'
|
||||
|
||||
|
||||
class DebugModal extends React.Component {
|
||||
static propTypes = {
|
||||
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() {
|
||||
return <Modal
|
||||
data-wd-key="debug-modal"
|
||||
isOpen={this.props.isOpen}
|
||||
onOpenToggle={this.props.onOpenToggle}
|
||||
title={'Debug'}
|
||||
>
|
||||
<div className="maputnik-modal-section maputnik-modal-shortcuts">
|
||||
{this.props.renderer === 'mbgljs' &&
|
||||
<ul>
|
||||
{Object.entries(this.props.mapboxGlDebugOptions).map(([key, val]) => {
|
||||
return <li key={key}>
|
||||
<label>
|
||||
<input type="checkbox" checked={val} onClick={(e) => this.props.onChangeMaboxGlDebug(key, e.target.checked)} /> {key}
|
||||
</label>
|
||||
</li>
|
||||
})}
|
||||
</ul>
|
||||
}
|
||||
{this.props.renderer === 'ol' &&
|
||||
<ul>
|
||||
{Object.entries(this.props.openlayersDebugOptions).map(([key, val]) => {
|
||||
return <li key={key}>
|
||||
<label>
|
||||
<input type="checkbox" checked={val} onClick={(e) => this.props.onChangeOpenlayersDebug(key, e.target.checked)} /> {key}
|
||||
</label>
|
||||
</li>
|
||||
})}
|
||||
</ul>
|
||||
}
|
||||
</div>
|
||||
</Modal>
|
||||
}
|
||||
}
|
||||
|
||||
export default DebugModal;
|
||||
@@ -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 <Modal
|
||||
data-wd-key="modal-settings"
|
||||
@@ -78,7 +69,7 @@ class SettingsModal extends React.Component {
|
||||
<StringInput {...inputProps}
|
||||
data-wd-key="modal-settings.maputnik:mapbox_access_token"
|
||||
value={metadata['maputnik:mapbox_access_token']}
|
||||
onChange={this.changeMetadataProperty.bind(this, "maputnik:mapbox_access_token")}
|
||||
onChange={onChangeMetadataProperty.bind(this, "maputnik:mapbox_access_token")}
|
||||
/>
|
||||
</InputBlock>
|
||||
|
||||
@@ -86,7 +77,7 @@ class SettingsModal extends React.Component {
|
||||
<StringInput {...inputProps}
|
||||
data-wd-key="modal-settings.maputnik:openmaptiles_access_token"
|
||||
value={metadata['maputnik:openmaptiles_access_token']}
|
||||
onChange={this.changeMetadataProperty.bind(this, "maputnik:openmaptiles_access_token")}
|
||||
onChange={onChangeMetadataProperty.bind(this, "maputnik:openmaptiles_access_token")}
|
||||
/>
|
||||
</InputBlock>
|
||||
|
||||
@@ -94,7 +85,7 @@ class SettingsModal extends React.Component {
|
||||
<StringInput {...inputProps}
|
||||
data-wd-key="modal-settings.maputnik:thunderforest_access_token"
|
||||
value={metadata['maputnik:thunderforest_access_token']}
|
||||
onChange={this.changeMetadataProperty.bind(this, "maputnik:thunderforest_access_token")}
|
||||
onChange={onChangeMetadataProperty.bind(this, "maputnik:thunderforest_access_token")}
|
||||
/>
|
||||
</InputBlock>
|
||||
|
||||
@@ -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')}
|
||||
/>
|
||||
</InputBlock>
|
||||
|
||||
</div>
|
||||
</Modal>
|
||||
}
|
||||
|
||||
@@ -40,6 +40,10 @@ class ShortcutsModal extends React.Component {
|
||||
key: "m",
|
||||
text: "Focus map"
|
||||
},
|
||||
{
|
||||
key: "!",
|
||||
text: "Debug modal"
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user