mirror of
https://github.com/maputnik/editor.git
synced 2025-12-26 08:00:01 +00:00
Add globe support in Maputnik UI (#1379)
## Launch Checklist Add a small drop down to select mercator or globe. This isn't a fully covered field as one can set an expression there, but I believe this is good enough for most cases. Before: <img width="645" height="254" alt="image" src="https://github.com/user-attachments/assets/19a7ec50-a0bb-4ea3-b9fc-3abc5572c47e" /> After: <img width="770" height="462" alt="image" src="https://github.com/user-attachments/assets/f4774020-1cc8-45fe-88f9-f77ad7c53140" /> - [x] Briefly describe the changes in this PR. - [x] Include before/after visuals or gifs if this PR includes visual changes. - [x] Write tests for all new functionality. - [x] Add an entry to `CHANGELOG.md` under the `## main` section. --------- Co-authored-by: Birk Skyum <birk.skyum@pm.me> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import React from "react";
|
||||
import latest from "@maplibre/maplibre-gl-style-spec/dist/latest.json";
|
||||
import type {LightSpecification, StyleSpecification, TerrainSpecification, TransitionSpecification} from "maplibre-gl";
|
||||
import type {LightSpecification, ProjectionSpecification, StyleSpecification, TerrainSpecification, TransitionSpecification} from "maplibre-gl";
|
||||
import { type WithTranslation, withTranslation } from "react-i18next";
|
||||
|
||||
import FieldArray from "../FieldArray";
|
||||
@@ -79,6 +79,24 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
||||
});
|
||||
}
|
||||
|
||||
changeProjectionType(value: any) {
|
||||
const projection = {
|
||||
...this.props.mapStyle.projection,
|
||||
} as ProjectionSpecification;
|
||||
|
||||
if (value === undefined) {
|
||||
delete projection.type;
|
||||
}
|
||||
else {
|
||||
projection.type = value;
|
||||
}
|
||||
|
||||
this.props.onStyleChanged({
|
||||
...this.props.mapStyle,
|
||||
projection,
|
||||
});
|
||||
}
|
||||
|
||||
changeStyleProperty(property: keyof StyleSpecification | "owner", value: any) {
|
||||
const changedStyle = {
|
||||
...this.props.mapStyle,
|
||||
@@ -103,6 +121,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
||||
const light = this.props.mapStyle.light || {};
|
||||
const transition = this.props.mapStyle.transition || {};
|
||||
const terrain = this.props.mapStyle.terrain || {} as TerrainSpecification;
|
||||
const projection = this.props.mapStyle.projection || {} as ProjectionSpecification;
|
||||
|
||||
return <Modal
|
||||
data-wd-key="modal:settings"
|
||||
@@ -116,21 +135,21 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
||||
fieldSpec={latest.$root.name}
|
||||
data-wd-key="modal:settings.name"
|
||||
value={this.props.mapStyle.name}
|
||||
onChange={this.changeStyleProperty.bind(this, "name")}
|
||||
onChange={(value) => this.changeStyleProperty("name", value)}
|
||||
/>
|
||||
<FieldString
|
||||
label={t("Owner")}
|
||||
fieldSpec={{doc: t("Owner ID of the style. Used by Mapbox or future style APIs.")}}
|
||||
data-wd-key="modal:settings.owner"
|
||||
value={(this.props.mapStyle as any).owner}
|
||||
onChange={this.changeStyleProperty.bind(this, "owner")}
|
||||
onChange={(value) => this.changeStyleProperty("owner", value)}
|
||||
/>
|
||||
<FieldUrl
|
||||
fieldSpec={latest.$root.sprite}
|
||||
label={t("Sprite URL")}
|
||||
data-wd-key="modal:settings.sprite"
|
||||
value={this.props.mapStyle.sprite as string}
|
||||
onChange={this.changeStyleProperty.bind(this, "sprite")}
|
||||
onChange={(value) => this.changeStyleProperty("sprite", value)}
|
||||
/>
|
||||
|
||||
<FieldUrl
|
||||
@@ -138,7 +157,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
||||
fieldSpec={latest.$root.glyphs}
|
||||
data-wd-key="modal:settings.glyphs"
|
||||
value={this.props.mapStyle.glyphs as string}
|
||||
onChange={this.changeStyleProperty.bind(this, "glyphs")}
|
||||
onChange={(value) => this.changeStyleProperty("glyphs", value)}
|
||||
/>
|
||||
|
||||
<FieldString
|
||||
@@ -146,7 +165,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
||||
fieldSpec={fsa.maputnik.maptiler_access_token}
|
||||
data-wd-key="modal:settings.maputnik:openmaptiles_access_token"
|
||||
value={metadata["maputnik:openmaptiles_access_token"]}
|
||||
onChange={onChangeMetadataProperty.bind(this, "maputnik:openmaptiles_access_token")}
|
||||
onChange={(value) => onChangeMetadataProperty("maputnik:openmaptiles_access_token", value)}
|
||||
/>
|
||||
|
||||
<FieldString
|
||||
@@ -154,7 +173,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
||||
fieldSpec={fsa.maputnik.thunderforest_access_token}
|
||||
data-wd-key="modal:settings.maputnik:thunderforest_access_token"
|
||||
value={metadata["maputnik:thunderforest_access_token"]}
|
||||
onChange={onChangeMetadataProperty.bind(this, "maputnik:thunderforest_access_token")}
|
||||
onChange={(value) => onChangeMetadataProperty("maputnik:thunderforest_access_token", value)}
|
||||
/>
|
||||
|
||||
<FieldString
|
||||
@@ -162,7 +181,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
||||
fieldSpec={fsa.maputnik.stadia_access_token}
|
||||
data-wd-key="modal:settings.maputnik:stadia_access_token"
|
||||
value={metadata["maputnik:stadia_access_token"]}
|
||||
onChange={onChangeMetadataProperty.bind(this, "maputnik:stadia_access_token")}
|
||||
onChange={(value) => onChangeMetadataProperty("maputnik:stadia_access_token", value)}
|
||||
/>
|
||||
|
||||
<FieldString
|
||||
@@ -170,7 +189,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
||||
fieldSpec={fsa.maputnik.locationiq_access_token}
|
||||
data-wd-key="modal:settings.maputnik:locationiq_access_token"
|
||||
value={metadata["maputnik:locationiq_access_token"]}
|
||||
onChange={onChangeMetadataProperty.bind(this, "maputnik:locationiq_access_token")}
|
||||
onChange={(value) => onChangeMetadataProperty("maputnik:locationiq_access_token", value)}
|
||||
/>
|
||||
|
||||
<FieldArray
|
||||
@@ -180,7 +199,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
||||
type="number"
|
||||
value={mapStyle.center || []}
|
||||
default={[0, 0]}
|
||||
onChange={this.changeStyleProperty.bind(this, "center")}
|
||||
onChange={(value) => this.changeStyleProperty("center", value)}
|
||||
/>
|
||||
|
||||
<FieldNumber
|
||||
@@ -188,7 +207,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
||||
fieldSpec={latest.$root.zoom}
|
||||
value={mapStyle.zoom}
|
||||
default={0}
|
||||
onChange={this.changeStyleProperty.bind(this, "zoom")}
|
||||
onChange={(value) => this.changeStyleProperty("zoom", value)}
|
||||
/>
|
||||
|
||||
<FieldNumber
|
||||
@@ -196,7 +215,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
||||
fieldSpec={latest.$root.bearing}
|
||||
value={mapStyle.bearing}
|
||||
default={latest.$root.bearing.default}
|
||||
onChange={this.changeStyleProperty.bind(this, "bearing")}
|
||||
onChange={(value) => this.changeStyleProperty("bearing", value)}
|
||||
/>
|
||||
|
||||
<FieldNumber
|
||||
@@ -204,7 +223,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
||||
fieldSpec={latest.$root.pitch}
|
||||
value={mapStyle.pitch}
|
||||
default={latest.$root.pitch.default}
|
||||
onChange={this.changeStyleProperty.bind(this, "pitch")}
|
||||
onChange={(value) => this.changeStyleProperty("pitch", value)}
|
||||
/>
|
||||
|
||||
<FieldEnum
|
||||
@@ -214,7 +233,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
||||
value={light.anchor as string}
|
||||
options={Object.keys(latest.light.anchor.values)}
|
||||
default={latest.light.anchor.default}
|
||||
onChange={this.changeLightProperty.bind(this, "anchor")}
|
||||
onChange={(value) => this.changeLightProperty("anchor", value)}
|
||||
/>
|
||||
|
||||
<FieldColor
|
||||
@@ -222,7 +241,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
||||
fieldSpec={latest.light.color}
|
||||
value={light.color as string}
|
||||
default={latest.light.color.default}
|
||||
onChange={this.changeLightProperty.bind(this, "color")}
|
||||
onChange={(value) => this.changeLightProperty("color", value)}
|
||||
/>
|
||||
|
||||
<FieldNumber
|
||||
@@ -230,7 +249,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
||||
fieldSpec={latest.light.intensity}
|
||||
value={light.intensity as number}
|
||||
default={latest.light.intensity.default}
|
||||
onChange={this.changeLightProperty.bind(this, "intensity")}
|
||||
onChange={(value) => this.changeLightProperty("intensity", value)}
|
||||
/>
|
||||
|
||||
<FieldArray
|
||||
@@ -240,7 +259,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
||||
length={latest.light.position.length}
|
||||
value={light.position as number[]}
|
||||
default={latest.light.position.default}
|
||||
onChange={this.changeLightProperty.bind(this, "position")}
|
||||
onChange={(value) => this.changeLightProperty("position", value)}
|
||||
/>
|
||||
|
||||
<FieldString
|
||||
@@ -248,7 +267,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
||||
fieldSpec={latest.terrain.source}
|
||||
data-wd-key="modal:settings.maputnik:terrain_source"
|
||||
value={terrain.source}
|
||||
onChange={this.changeTerrainProperty.bind(this, "source")}
|
||||
onChange={(value) => this.changeTerrainProperty("source", value)}
|
||||
/>
|
||||
|
||||
<FieldNumber
|
||||
@@ -256,7 +275,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
||||
fieldSpec={latest.terrain.exaggeration}
|
||||
value={terrain.exaggeration}
|
||||
default={latest.terrain.exaggeration.default}
|
||||
onChange={this.changeTerrainProperty.bind(this, "exaggeration")}
|
||||
onChange={(value) => this.changeTerrainProperty("exaggeration", value)}
|
||||
/>
|
||||
|
||||
<FieldNumber
|
||||
@@ -264,7 +283,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
||||
fieldSpec={latest.transition.delay}
|
||||
value={transition.delay}
|
||||
default={latest.transition.delay.default}
|
||||
onChange={this.changeTransitionProperty.bind(this, "delay")}
|
||||
onChange={(value) => this.changeTransitionProperty("delay", value)}
|
||||
/>
|
||||
|
||||
<FieldNumber
|
||||
@@ -272,7 +291,20 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
||||
fieldSpec={latest.transition.duration}
|
||||
value={transition.duration}
|
||||
default={latest.transition.duration.default}
|
||||
onChange={this.changeTransitionProperty.bind(this, "duration")}
|
||||
onChange={(value) => this.changeTransitionProperty("duration", value)}
|
||||
/>
|
||||
|
||||
<FieldSelect
|
||||
label={t("Projection")}
|
||||
data-wd-key="modal:settings.projection"
|
||||
options={[
|
||||
["", "Undefined"],
|
||||
["mercator", "Mercator"],
|
||||
["globe", "Globe"],
|
||||
["vertical-perspective", "Vertical Perspective"]
|
||||
]}
|
||||
value={projection?.type?.toString() || ""}
|
||||
onChange={(value) => this.changeProjectionType(value)}
|
||||
/>
|
||||
|
||||
<FieldSelect
|
||||
@@ -284,7 +316,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
||||
["ol", t("Open Layers (experimental)")],
|
||||
]}
|
||||
value={metadata["maputnik:renderer"] || "mlgljs"}
|
||||
onChange={onChangeMetadataProperty.bind(this, "maputnik:renderer")}
|
||||
onChange={(value) => onChangeMetadataProperty("maputnik:renderer", value)}
|
||||
/>
|
||||
</div>
|
||||
</Modal>;
|
||||
|
||||
Reference in New Issue
Block a user