forked from MapComplete/MapComplete
Merge develop
This commit is contained in:
commit
c8bd412476
49 changed files with 1342 additions and 977 deletions
|
@ -28,7 +28,7 @@ export default class Minimap extends BaseUIElement {
|
|||
super()
|
||||
options = options ?? {}
|
||||
this._background = options?.background ?? new UIEventSource<BaseLayer>(AvailableBaseLayers.osmCarto)
|
||||
this._location = options?.location ?? new UIEventSource<Loc>(undefined)
|
||||
this._location = options?.location ?? new UIEventSource<Loc>({lat: 0, lon: 0, zoom: 1})
|
||||
this._id = "minimap" + Minimap._nextId;
|
||||
this._allowMoving = options.allowMoving ?? true;
|
||||
this._leafletoptions = options.leafletOptions ?? {}
|
||||
|
@ -43,6 +43,7 @@ export default class Minimap extends BaseUIElement {
|
|||
div.style.width = "100%"
|
||||
div.style.minWidth = "40px"
|
||||
div.style.minHeight = "40px"
|
||||
div.style.position = "relative"
|
||||
const wrapper = document.createElement("div")
|
||||
wrapper.appendChild(div)
|
||||
const self = this;
|
||||
|
|
|
@ -64,13 +64,11 @@ export default class PersonalLayersPanel extends VariableUiElement {
|
|||
private static CreateLayerToggle(layer: LayerConfig): Toggle {
|
||||
let icon :BaseUIElement =new Combine([ layer.GenerateLeafletStyle(
|
||||
new UIEventSource<any>({id: "node/-1"}),
|
||||
false,
|
||||
"2em"
|
||||
false
|
||||
).icon.html]).SetClass("relative")
|
||||
let iconUnset =new Combine([ layer.GenerateLeafletStyle(
|
||||
new UIEventSource<any>({id: "node/-1"}),
|
||||
false,
|
||||
"2em"
|
||||
false
|
||||
).icon.html]).SetClass("relative")
|
||||
|
||||
iconUnset.SetStyle("opacity:0.1")
|
||||
|
|
|
@ -20,6 +20,8 @@ import LocationInput from "../Input/LocationInput";
|
|||
import {InputElement} from "../Input/InputElement";
|
||||
import Loc from "../../Models/Loc";
|
||||
import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers";
|
||||
import CreateNewNodeAction from "../../Logic/Osm/Actions/CreateNewNodeAction";
|
||||
import Hash from "../../Logic/Web/Hash";
|
||||
|
||||
/*
|
||||
* The SimpleAddUI is a single panel, which can have multiple states:
|
||||
|
@ -61,11 +63,6 @@ export default class SimpleAddUI extends Toggle {
|
|||
const selectedPreset = new UIEventSource<PresetInfo>(undefined);
|
||||
isShown.addCallback(_ => selectedPreset.setData(undefined)) // Clear preset selection when the UI is closed/opened
|
||||
|
||||
function createNewPoint(tags: any[], location: { lat: number, lon: number }) {
|
||||
let feature = State.state.changes.createElement(tags, location.lat, location.lon);
|
||||
State.state.selectedElement.setData(feature);
|
||||
}
|
||||
|
||||
const presetsOverview = SimpleAddUI.CreateAllPresetsPanel(selectedPreset)
|
||||
|
||||
const addUi = new VariableUiElement(
|
||||
|
@ -75,8 +72,16 @@ export default class SimpleAddUI extends Toggle {
|
|||
}
|
||||
return SimpleAddUI.CreateConfirmButton(preset,
|
||||
(tags, location) => {
|
||||
createNewPoint(tags, location)
|
||||
const newElementAction = new CreateNewNodeAction(tags, location.lat, location.lon)
|
||||
State.state.changes.applyAction(newElementAction)
|
||||
selectedPreset.setData(undefined)
|
||||
isShown.setData(false)
|
||||
State.state.selectedElement.setData(State.state.allElements.ContainingFeatures.get(
|
||||
newElementAction.newElementId
|
||||
))
|
||||
console.log("Did set selected element to",State.state.allElements.ContainingFeatures.get(
|
||||
newElementAction.newElementId
|
||||
))
|
||||
}, () => {
|
||||
selectedPreset.setData(undefined)
|
||||
})
|
||||
|
@ -121,16 +126,16 @@ export default class SimpleAddUI extends Toggle {
|
|||
lon: location.data.lon,
|
||||
zoom: 19
|
||||
});
|
||||
|
||||
|
||||
let backgroundLayer = undefined;
|
||||
if(preset.preciseInput.preferredBackground){
|
||||
backgroundLayer= AvailableBaseLayers.SelectBestLayerAccordingTo(locationSrc, new UIEventSource<string | string[]>(preset.preciseInput.preferredBackground))
|
||||
if (preset.preciseInput.preferredBackground) {
|
||||
backgroundLayer = AvailableBaseLayers.SelectBestLayerAccordingTo(locationSrc, new UIEventSource<string | string[]>(preset.preciseInput.preferredBackground))
|
||||
}
|
||||
|
||||
|
||||
preciseInput = new LocationInput({
|
||||
mapBackground: backgroundLayer,
|
||||
centerLocation:locationSrc
|
||||
|
||||
centerLocation: locationSrc
|
||||
|
||||
})
|
||||
preciseInput.SetClass("h-32 rounded-xl overflow-hidden border border-gray").SetStyle("height: 12rem;")
|
||||
}
|
||||
|
@ -145,7 +150,7 @@ export default class SimpleAddUI extends Toggle {
|
|||
.onClick(() => {
|
||||
confirm(preset.tags, (preciseInput?.GetValue() ?? location).data);
|
||||
});
|
||||
|
||||
|
||||
if (preciseInput !== undefined) {
|
||||
confirmButton = new Combine([preciseInput, confirmButton])
|
||||
}
|
||||
|
@ -241,7 +246,7 @@ export default class SimpleAddUI extends Toggle {
|
|||
for (const preset of presets) {
|
||||
|
||||
const tags = TagUtils.KVtoProperties(preset.tags ?? []);
|
||||
let icon:() => BaseUIElement = () => layer.layerDef.GenerateLeafletStyle(new UIEventSource<any>(tags), false).icon.html
|
||||
let icon: () => BaseUIElement = () => layer.layerDef.GenerateLeafletStyle(new UIEventSource<any>(tags), false).icon.html
|
||||
.SetClass("w-12 h-12 block relative");
|
||||
const presetInfo: PresetInfo = {
|
||||
tags: preset.tags,
|
||||
|
|
|
@ -5,6 +5,7 @@ import Combine from "../Base/Combine";
|
|||
import State from "../../State";
|
||||
import Svg from "../../Svg";
|
||||
import {Tag} from "../../Logic/Tags/Tag";
|
||||
import ChangeTagAction from "../../Logic/Osm/Actions/ChangeTagAction";
|
||||
|
||||
|
||||
export default class DeleteImage extends Toggle {
|
||||
|
@ -15,14 +16,17 @@ export default class DeleteImage extends Toggle {
|
|||
.SetClass("rounded-full p-1")
|
||||
.SetStyle("color:white;background:#ff8c8c")
|
||||
.onClick(() => {
|
||||
State.state?.changes?.addTag(tags.data.id, new Tag(key, oldValue), tags);
|
||||
State.state?.changes?.
|
||||
applyAction(new ChangeTagAction(tags.data.id, new Tag(key, oldValue), tags.data))
|
||||
});
|
||||
|
||||
const deleteButton = Translations.t.image.doDelete.Clone()
|
||||
.SetClass("block w-full pl-4 pr-4")
|
||||
.SetStyle("color:white;background:#ff8c8c; border-top-left-radius:30rem; border-top-right-radius: 30rem;")
|
||||
.onClick(() => {
|
||||
State.state?.changes?.addTag(tags.data.id, new Tag(key, ""), tags);
|
||||
State.state?.changes?.applyAction(
|
||||
new ChangeTagAction( tags.data.id, new Tag(key, ""), tags.data)
|
||||
)
|
||||
});
|
||||
|
||||
const cancelButton = Translations.t.general.cancel.Clone().SetClass("bg-white pl-4 pr-4").SetStyle("border-bottom-left-radius:30rem; border-bottom-right-radius: 30rem;");
|
||||
|
|
|
@ -11,6 +11,7 @@ import FileSelectorButton from "../Input/FileSelectorButton";
|
|||
import ImgurUploader from "../../Logic/ImageProviders/ImgurUploader";
|
||||
import UploadFlowStateUI from "../BigComponents/UploadFlowStateUI";
|
||||
import LayerConfig from "../../Customizations/JSON/LayerConfig";
|
||||
import ChangeTagAction from "../../Logic/Osm/Actions/ChangeTagAction";
|
||||
|
||||
export class ImageUploadFlow extends Toggle {
|
||||
|
||||
|
@ -28,7 +29,10 @@ export class ImageUploadFlow extends Toggle {
|
|||
key = imagePrefix + ":" + freeIndex;
|
||||
}
|
||||
console.log("Adding image:" + key, url);
|
||||
State.state.changes.addTag(tags.id, new Tag(key, url), tagsSource);
|
||||
State.state.changes
|
||||
.applyAction(new ChangeTagAction(
|
||||
tags.id, new Tag(key, url), tagsSource.data
|
||||
))
|
||||
})
|
||||
|
||||
|
||||
|
|
|
@ -42,7 +42,6 @@ export default class LocationInput extends InputElement<Loc> {
|
|||
}
|
||||
)
|
||||
map.leafletMap.addCallbackAndRunD(leaflet => {
|
||||
console.log(leaflet.getBounds(), leaflet.getBounds().pad(0.15))
|
||||
leaflet.setMaxBounds(
|
||||
leaflet.getBounds().pad(0.15)
|
||||
)
|
||||
|
|
|
@ -3,7 +3,7 @@ import State from "../../State";
|
|||
import Toggle from "../Input/Toggle";
|
||||
import Translations from "../i18n/Translations";
|
||||
import Svg from "../../Svg";
|
||||
import DeleteAction from "../../Logic/Osm/DeleteAction";
|
||||
import DeleteAction from "../../Logic/Osm/Actions/DeleteAction";
|
||||
import {Tag} from "../../Logic/Tags/Tag";
|
||||
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||
import {TagsFilter} from "../../Logic/Tags/TagsFilter";
|
||||
|
@ -19,6 +19,7 @@ import {Changes} from "../../Logic/Osm/Changes";
|
|||
import {And} from "../../Logic/Tags/And";
|
||||
import Constants from "../../Models/Constants";
|
||||
import DeleteConfig from "../../Customizations/JSON/DeleteConfig";
|
||||
import ChangeTagAction from "../../Logic/Osm/Actions/ChangeTagAction";
|
||||
|
||||
export default class DeleteWizard extends Toggle {
|
||||
/**
|
||||
|
@ -58,7 +59,9 @@ export default class DeleteWizard extends Toggle {
|
|||
})
|
||||
}
|
||||
(State.state?.changes ?? new Changes())
|
||||
.addTag(id, new And(tagsToApply.map(kv => new Tag(kv.k, kv.v))), tagsSource);
|
||||
.applyAction(new ChangeTagAction(
|
||||
id, new And(tagsToApply.map(kv => new Tag(kv.k, kv.v))), tagsSource.data
|
||||
))
|
||||
}
|
||||
|
||||
function doDelete(selected: TagsFilter) {
|
||||
|
|
|
@ -13,6 +13,7 @@ import SharedTagRenderings from "../../Customizations/SharedTagRenderings";
|
|||
import BaseUIElement from "../BaseUIElement";
|
||||
import {VariableUiElement} from "../Base/VariableUIElement";
|
||||
import DeleteWizard from "./DeleteWizard";
|
||||
import SplitRoadWizard from "./SplitRoadWizard";
|
||||
|
||||
export default class FeatureInfoBox extends ScrollableFullScreen {
|
||||
|
||||
|
@ -66,10 +67,6 @@ export default class FeatureInfoBox extends ScrollableFullScreen {
|
|||
renderings.push(questionBox);
|
||||
}
|
||||
|
||||
const hasMinimap = layerConfig.tagRenderings.some(tr => tr.hasMinimap())
|
||||
if (!hasMinimap) {
|
||||
renderings.push(new TagRenderingAnswer(tags, SharedTagRenderings.SharedTagRendering.get("minimap")))
|
||||
}
|
||||
|
||||
if (layerConfig.deletion) {
|
||||
renderings.push(
|
||||
|
@ -81,6 +78,19 @@ export default class FeatureInfoBox extends ScrollableFullScreen {
|
|||
))
|
||||
}
|
||||
|
||||
if (layerConfig.allowSplit) {
|
||||
renderings.push(
|
||||
new VariableUiElement(tags.map(tags => tags.id).map(id =>
|
||||
new SplitRoadWizard(id))
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
const hasMinimap = layerConfig.tagRenderings.some(tr => tr.hasMinimap())
|
||||
if (!hasMinimap) {
|
||||
renderings.push(new TagRenderingAnswer(tags, SharedTagRenderings.SharedTagRendering.get("minimap")))
|
||||
}
|
||||
|
||||
renderings.push(
|
||||
new VariableUiElement(
|
||||
State.state.osmConnection.userDetails
|
||||
|
|
155
UI/Popup/SplitRoadWizard.ts
Normal file
155
UI/Popup/SplitRoadWizard.ts
Normal file
|
@ -0,0 +1,155 @@
|
|||
import Toggle from "../Input/Toggle";
|
||||
import Svg from "../../Svg";
|
||||
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||
import {SubtleButton} from "../Base/SubtleButton";
|
||||
import Minimap from "../Base/Minimap";
|
||||
import State from "../../State";
|
||||
import ShowDataLayer from "../ShowDataLayer";
|
||||
import {GeoOperations} from "../../Logic/GeoOperations";
|
||||
import {LeafletMouseEvent} from "leaflet";
|
||||
import Combine from "../Base/Combine";
|
||||
import {Button} from "../Base/Button";
|
||||
import Translations from "../i18n/Translations";
|
||||
import LayoutConfig from "../../Customizations/JSON/LayoutConfig";
|
||||
import SplitAction from "../../Logic/Osm/Actions/SplitAction";
|
||||
import {OsmObject, OsmWay} from "../../Logic/Osm/OsmObject";
|
||||
import Title from "../Base/Title";
|
||||
|
||||
export default class SplitRoadWizard extends Toggle {
|
||||
private static splitLayout = new UIEventSource(SplitRoadWizard.GetSplitLayout())
|
||||
|
||||
/**
|
||||
* A UI Element used for splitting roads
|
||||
*
|
||||
* @param id: The id of the road to remove
|
||||
*/
|
||||
constructor(id: string) {
|
||||
|
||||
const t = Translations.t.split;
|
||||
|
||||
// Contains the points on the road that are selected to split on - contains geojson points with extra properties such as 'location' with the distance along the linestring
|
||||
const splitPoints = new UIEventSource<{ feature: any, freshness: Date }[]>([]);
|
||||
|
||||
const hasBeenSplit = new UIEventSource(false)
|
||||
|
||||
// Toggle variable between show split button and map
|
||||
const splitClicked = new UIEventSource<boolean>(false);
|
||||
|
||||
// Minimap on which you can select the points to be splitted
|
||||
const miniMap = new Minimap({background: State.state.backgroundLayer, allowMoving: false});
|
||||
miniMap.SetStyle("width: 100%; height: 24rem;");
|
||||
|
||||
// Define how a cut is displayed on the map
|
||||
|
||||
// Load the road with given id on the minimap
|
||||
const roadElement = State.state.allElements.ContainingFeatures.get(id)
|
||||
const roadEventSource = new UIEventSource([{feature: roadElement, freshness: new Date()}]);
|
||||
// Datalayer displaying the road and the cut points (if any)
|
||||
new ShowDataLayer(roadEventSource, miniMap.leafletMap, State.state.layoutToUse, false, true, "splitRoadWay");
|
||||
new ShowDataLayer(splitPoints, miniMap.leafletMap, SplitRoadWizard.splitLayout, false, false, "splitRoad: splitpoints")
|
||||
|
||||
/**
|
||||
* Handles a click on the overleaf map.
|
||||
* Finds the closest intersection with the road and adds a point there, ready to confirm the cut.
|
||||
* @param coordinates Clicked location, [lon, lat]
|
||||
*/
|
||||
function onMapClick(coordinates) {
|
||||
// Get nearest point on the road
|
||||
const pointOnRoad = GeoOperations.nearestPoint(roadElement, coordinates); // pointOnRoad is a geojson
|
||||
|
||||
// Update point properties to let it match the layer
|
||||
pointOnRoad.properties._cutposition = "yes";
|
||||
pointOnRoad["_matching_layer_id"] = "splitpositions";
|
||||
|
||||
// let the state remember the point, to be able to retrieve it later by id
|
||||
State.state.allElements.addOrGetElement(pointOnRoad);
|
||||
|
||||
// Add it to the list of all points and notify observers
|
||||
splitPoints.data.push({feature: pointOnRoad, freshness: new Date()}); // show the point on the data layer
|
||||
splitPoints.ping(); // not updated using .setData, so manually ping observers
|
||||
}
|
||||
|
||||
// When clicked, pass clicked location coordinates to onMapClick function
|
||||
miniMap.leafletMap.addCallbackAndRunD(
|
||||
(leafletMap) => leafletMap.on("click", (mouseEvent: LeafletMouseEvent) => {
|
||||
onMapClick([mouseEvent.latlng.lng, mouseEvent.latlng.lat])
|
||||
}))
|
||||
|
||||
// Toggle between splitmap
|
||||
const splitButton = new SubtleButton(Svg.scissors_ui(), t.inviteToSplit.Clone());
|
||||
splitButton.onClick(
|
||||
() => {
|
||||
splitClicked.setData(true)
|
||||
}
|
||||
)
|
||||
|
||||
// Only show the splitButton if logged in, else show login prompt
|
||||
const loginBtn = t.loginToSplit.Clone()
|
||||
.onClick(() => State.state.osmConnection.AttemptLogin())
|
||||
.SetClass("login-button-friendly");
|
||||
const splitToggle = new Toggle(splitButton, loginBtn, State.state.osmConnection.isLoggedIn)
|
||||
|
||||
// Save button
|
||||
const saveButton = new Button(t.split.Clone(), () => {
|
||||
hasBeenSplit.setData(true)
|
||||
const way = OsmObject.DownloadObject(id)
|
||||
const partOfSrc = OsmObject.DownloadReferencingRelations(id);
|
||||
let hasRun = false
|
||||
way.map(way => {
|
||||
const partOf = partOfSrc.data
|
||||
if(way === undefined || partOf === undefined){
|
||||
return;
|
||||
}
|
||||
if(hasRun){
|
||||
return
|
||||
}
|
||||
hasRun = true
|
||||
const splitAction = new SplitAction(
|
||||
<OsmWay>way, way.asGeoJson(), partOf, splitPoints.data.map(ff => ff.feature)
|
||||
)
|
||||
State.state.changes.applyAction(splitAction)
|
||||
|
||||
}, [partOfSrc])
|
||||
|
||||
|
||||
});
|
||||
saveButton.SetClass("btn btn-primary mr-3");
|
||||
const disabledSaveButton = new Button("Split", undefined);
|
||||
disabledSaveButton.SetClass("btn btn-disabled mr-3");
|
||||
// Only show the save button if there are split points defined
|
||||
const saveToggle = new Toggle(disabledSaveButton, saveButton, splitPoints.map((data) => data.length === 0))
|
||||
|
||||
const cancelButton = Translations.t.general.cancel.Clone() // Not using Button() element to prevent full width button
|
||||
.SetClass("btn btn-secondary mr-3")
|
||||
.onClick(() => {
|
||||
splitPoints.setData([]);
|
||||
splitClicked.setData(false);
|
||||
});
|
||||
|
||||
cancelButton.SetClass("btn btn-secondary block");
|
||||
|
||||
const splitTitle = new Title(t.splitTitle);
|
||||
|
||||
const mapView = new Combine([splitTitle, miniMap, new Combine([cancelButton, saveToggle]).SetClass("flex flex-row")]);
|
||||
mapView.SetClass("question")
|
||||
const confirm = new Toggle(mapView, splitToggle, splitClicked);
|
||||
super(t.hasBeenSplit.Clone(), confirm, hasBeenSplit)
|
||||
}
|
||||
|
||||
private static GetSplitLayout(): LayoutConfig {
|
||||
return new LayoutConfig({
|
||||
maintainer: "mapcomplete",
|
||||
language: ["en"],
|
||||
startLon: 0,
|
||||
startLat: 0,
|
||||
description: "Split points visualisations - built in at SplitRoadWizard.ts",
|
||||
icon: "", startZoom: 0,
|
||||
title: "Split locations",
|
||||
version: "",
|
||||
|
||||
id: "splitpositions",
|
||||
layers: [{id: "splitpositions", source: {osmTags: "_cutposition=yes"}, icon: "./assets/svg/plus.svg"}]
|
||||
}, true, "(BUILTIN) SplitRoadWizard.ts")
|
||||
|
||||
}
|
||||
}
|
|
@ -25,6 +25,7 @@ import BaseUIElement from "../BaseUIElement";
|
|||
import {DropDown} from "../Input/DropDown";
|
||||
import {Unit} from "../../Customizations/JSON/Denomination";
|
||||
import InputElementWrapper from "../Input/InputElementWrapper";
|
||||
import ChangeTagAction from "../../Logic/Osm/Actions/ChangeTagAction";
|
||||
|
||||
/**
|
||||
* Shows the question element.
|
||||
|
@ -56,7 +57,9 @@ export default class TagRenderingQuestion extends Combine {
|
|||
const selection = inputElement.GetValue().data;
|
||||
if (selection) {
|
||||
(State.state?.changes ?? new Changes())
|
||||
.addTag(tags.data.id, selection, tags);
|
||||
.applyAction(new ChangeTagAction(
|
||||
tags.data.id, selection, tags.data
|
||||
))
|
||||
}
|
||||
|
||||
if (options.afterSave) {
|
||||
|
@ -195,9 +198,7 @@ export default class TagRenderingQuestion extends Combine {
|
|||
oppositeTags.push(notSelected);
|
||||
}
|
||||
tags.push(TagUtils.FlattenMultiAnswer(oppositeTags));
|
||||
const actualTags = TagUtils.FlattenMultiAnswer(tags);
|
||||
console.log("Converted ", indices.join(","), "into", actualTags.asHumanString(false, false, {}), "with elems", elements)
|
||||
return actualTags;
|
||||
return TagUtils.FlattenMultiAnswer(tags);
|
||||
},
|
||||
(tags: TagsFilter) => {
|
||||
// {key --> values[]}
|
||||
|
|
|
@ -22,7 +22,8 @@ export default class ShowDataLayer {
|
|||
leafletMap: UIEventSource<L.Map>,
|
||||
layoutToUse: UIEventSource<LayoutConfig>,
|
||||
enablePopups = true,
|
||||
zoomToFeatures = false) {
|
||||
zoomToFeatures = false,
|
||||
name?: string) {
|
||||
this._leafletMap = leafletMap;
|
||||
this._enablePopups = enablePopups;
|
||||
this._features = features;
|
||||
|
@ -85,9 +86,7 @@ export default class ShowDataLayer {
|
|||
console.error(e)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
State.state.selectedElement.ping();
|
||||
State.state.selectedElement.ping()
|
||||
}
|
||||
|
||||
features.addCallback(() => update());
|
||||
|
@ -131,6 +130,7 @@ export default class ShowDataLayer {
|
|||
})
|
||||
});
|
||||
}
|
||||
|
||||
private postProcessFeature(feature, leafletLayer: L.Layer) {
|
||||
const layer: LayerConfig = this._layerDict[feature._matching_layer_id];
|
||||
if (layer === undefined) {
|
||||
|
@ -160,7 +160,7 @@ export default class ShowDataLayer {
|
|||
|
||||
leafletLayer.on("popupopen", () => {
|
||||
State.state.selectedElement.setData(feature)
|
||||
|
||||
|
||||
if (infobox === undefined) {
|
||||
const tags = State.state.allElements.getEventSourceById(feature.properties.id);
|
||||
infobox = new FeatureInfoBox(tags, layer);
|
||||
|
@ -175,7 +175,7 @@ export default class ShowDataLayer {
|
|||
|
||||
|
||||
infobox.AttachTo(id)
|
||||
infobox.Activate();
|
||||
infobox.Activate();
|
||||
});
|
||||
const self = this;
|
||||
State.state.selectedElement.addCallbackAndRunD(selected => {
|
||||
|
@ -188,11 +188,13 @@ export default class ShowDataLayer {
|
|||
if (selected.properties.id === feature.properties.id) {
|
||||
// A small sanity check to prevent infinite loops:
|
||||
if (selected.geometry.type === feature.geometry.type // If a feature is rendered both as way and as point, opening one popup might trigger the other to open, which might trigger the one to open again
|
||||
|
||||
&& feature.id === feature.properties.id // the feature might have as id 'node/-1' and as 'feature.properties.id' = 'the newly assigned id'. That is no good too
|
||||
) {
|
||||
&& feature.id === feature.properties.id // the feature might have as id 'node/-1' and as 'feature.properties.id' = 'the newly assigned id'. That is no good too
|
||||
) {
|
||||
leafletLayer.openPopup()
|
||||
}
|
||||
if(feature.id !== feature.properties.id){
|
||||
console.trace("Not opening the popup for", feature)
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
|
|
|
@ -56,9 +56,12 @@ export default class SpecialVisualizations {
|
|||
if (!tags.hasOwnProperty(key)) {
|
||||
continue;
|
||||
}
|
||||
parts.push(key + "=" + tags[key]);
|
||||
parts.push([key , tags[key] ?? "<b>undefined</b>" ]);
|
||||
}
|
||||
return parts.join("<br/>")
|
||||
return new Table(
|
||||
["key","value"],
|
||||
parts
|
||||
)
|
||||
})).SetStyle("border: 1px solid black; border-radius: 1em;padding:1em;display:block;")
|
||||
})
|
||||
},
|
||||
|
@ -127,6 +130,7 @@ export default class SpecialVisualizations {
|
|||
// This is a list of values
|
||||
idList = JSON.parse(value)
|
||||
}
|
||||
|
||||
for (const id of idList) {
|
||||
features.push({
|
||||
freshness: new Date(),
|
||||
|
|
|
@ -19,7 +19,6 @@ export class SubstitutedTranslation extends VariableUiElement {
|
|||
const extraMappings: SpecialVisualization[] = [];
|
||||
|
||||
mapping?.forEach((value, key) => {
|
||||
console.log("KV:", key, value)
|
||||
extraMappings.push(
|
||||
{
|
||||
funcName: key,
|
||||
|
@ -73,11 +72,6 @@ export class SubstitutedTranslation extends VariableUiElement {
|
|||
}
|
||||
}[] {
|
||||
|
||||
if (extraMappings.length > 0) {
|
||||
|
||||
console.log("Extra mappings are", extraMappings)
|
||||
}
|
||||
|
||||
for (const knownSpecial of SpecialVisualizations.specialVisualizations.concat(extraMappings)) {
|
||||
|
||||
// Note: the '.*?' in the regex reads as 'any character, but in a non-greedy way'
|
||||
|
|
|
@ -109,9 +109,9 @@ export class Translation extends BaseUIElement {
|
|||
// @ts-ignore
|
||||
const date: Date = el;
|
||||
rtext = date.toLocaleString();
|
||||
} else if (el.ConstructElement() === undefined) {
|
||||
console.error("InnerREnder is not defined", el);
|
||||
throw "Hmmm, el.InnerRender is not defined?"
|
||||
} else if (el.ConstructElement === undefined) {
|
||||
console.error("ConstructElement is not defined", el);
|
||||
throw "ConstructElement is not defined, you are working with a "+(typeof el)+":"+(el.constructor.name)
|
||||
} else {
|
||||
Translation.forcedLanguage = lang; // This is a very dirty hack - it'll bite me one day
|
||||
rtext = el.ConstructElement().innerHTML;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue