refactoring

This commit is contained in:
Pieter Vander Vennet 2023-03-28 05:13:48 +02:00
parent b94a8f5745
commit 5d0fe31c41
114 changed files with 2412 additions and 2958 deletions

View file

@ -8,7 +8,8 @@ import Toggle from "../Input/Toggle"
import { LoginToggle } from "./LoginButton"
import Combine from "../Base/Combine"
import Title from "../Base/Title"
import { SpecialVisualization } from "../SpecialVisualization"
import { SpecialVisualization, SpecialVisualizationState } from "../SpecialVisualization"
import { UIEventSource } from "../../Logic/UIEventSource"
export class AddNoteCommentViz implements SpecialVisualization {
funcName = "add_note_comment"
@ -21,7 +22,11 @@ export class AddNoteCommentViz implements SpecialVisualization {
},
]
public constr(state, tags, args) {
public constr(
state: SpecialVisualizationState,
tags: UIEventSource<Record<string, string>>,
args: string[]
) {
const t = Translations.t.notes
const textField = new TextField({
placeholder: t.addCommentPlaceholder,
@ -62,12 +67,11 @@ export class AddNoteCommentViz implements SpecialVisualization {
return t.addCommentAndClose
})
)
).onClick(() => {
).onClick(async () => {
const id = tags.data[args[1] ?? "id"]
state.osmConnection.closeNote(id, txt.data).then((_) => {
tags.data["closed_at"] = new Date().toISOString()
tags.ping()
})
await state.osmConnection.closeNote(id, txt.data)
tags.data["closed_at"] = new Date().toISOString()
tags.ping()
})
const reopen = new SubtleButton(
@ -80,12 +84,11 @@ export class AddNoteCommentViz implements SpecialVisualization {
return t.reopenNoteAndComment
})
)
).onClick(() => {
).onClick(async () => {
const id = tags.data[args[1] ?? "id"]
state.osmConnection.reopenNote(id, txt.data).then((_) => {
tags.data["closed_at"] = undefined
tags.ping()
})
await state.osmConnection.reopenNote(id, txt.data)
tags.data["closed_at"] = undefined
tags.ping()
})
const isClosed = tags.map((tags) => (tags["closed_at"] ?? "") !== "")

View file

@ -1,7 +1,5 @@
import FeaturePipelineState from "../../Logic/State/FeaturePipelineState"
import BaseUIElement from "../BaseUIElement"
import { Stores, UIEventSource } from "../../Logic/UIEventSource"
import { DefaultGuiState } from "../DefaultGuiState"
import { SubtleButton } from "../Base/SubtleButton"
import Img from "../Base/Img"
import { FixedUiElement } from "../Base/FixedUiElement"
@ -9,8 +7,6 @@ import Combine from "../Base/Combine"
import Link from "../Base/Link"
import { SubstitutedTranslation } from "../SubstitutedTranslation"
import { Utils } from "../../Utils"
import Minimap from "../Base/Minimap"
import ShowDataLayer from "../ShowDataLayer/ShowDataLayer"
import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource"
import { VariableUiElement } from "../Base/VariableUIElement"
import Loading from "../Base/Loading"
@ -23,15 +19,21 @@ import FilteredLayer from "../../Models/FilteredLayer"
import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig"
import Lazy from "../Base/Lazy"
import List from "../Base/List"
import { SpecialVisualization } from "../SpecialVisualization"
import { SpecialVisualization, SpecialVisualizationState } from "../SpecialVisualization"
import { IndexedFeatureSource } from "../../Logic/FeatureSource/FeatureSource"
import { MapLibreAdaptor } from "../Map/MapLibreAdaptor"
import ShowDataLayer from "../Map/ShowDataLayer"
import SvelteUIElement from "../Base/SvelteUIElement"
import MaplibreMap from "../Map/MaplibreMap.svelte"
export interface AutoAction extends SpecialVisualization {
supportsAutoAction: boolean
applyActionOn(
state: {
layoutToUse: LayoutConfig
layout: LayoutConfig
changes: Changes
indexedFeatures: IndexedFeatureSource
},
tagSource: UIEventSource<any>,
argument: string[]
@ -43,7 +45,7 @@ class ApplyButton extends UIElement {
private readonly text: string
private readonly targetTagRendering: string
private readonly target_layer_id: string
private readonly state: FeaturePipelineState
private readonly state: SpecialVisualizationState
private readonly target_feature_ids: string[]
private readonly buttonState = new UIEventSource<
"idle" | "running" | "done" | { error: string }
@ -52,7 +54,7 @@ class ApplyButton extends UIElement {
private readonly tagRenderingConfig: TagRenderingConfig
constructor(
state: FeaturePipelineState,
state: SpecialVisualizationState,
target_feature_ids: string[],
options: {
target_layer_id: string
@ -68,9 +70,7 @@ class ApplyButton extends UIElement {
this.targetTagRendering = options.targetTagRendering
this.text = options.text
this.icon = options.icon
this.layer = this.state.filteredLayers.data.find(
(l) => l.layerDef.id === this.target_layer_id
)
this.layer = this.state.layerState.filteredLayers.get(this.target_layer_id)
this.tagRenderingConfig = this.layer.layerDef.tagRenderings.find(
(tr) => tr.id === this.targetTagRendering
)
@ -101,22 +101,23 @@ class ApplyButton extends UIElement {
),
]).SetClass("subtle")
const previewMap = Minimap.createMiniMap({
allowMoving: false,
background: this.state.backgroundLayer,
addLayerControl: true,
}).SetClass("h-48")
const mlmap = new UIEventSource(undefined)
const mla = new MapLibreAdaptor(mlmap, {
rasterLayer: this.state.mapProperties.rasterLayer,
})
mla.allowZooming.setData(false)
mla.allowMoving.setData(false)
const previewMap = new SvelteUIElement(MaplibreMap, { map: mlmap }).SetClass("h-48")
const features = this.target_feature_ids.map((id) =>
this.state.allElements.ContainingFeatures.get(id)
this.state.indexedFeatures.featuresById.data.get(id)
)
new ShowDataLayer({
leafletMap: previewMap.leafletMap,
zoomToFeatures: true,
new ShowDataLayer(mlmap, {
features: StaticFeatureSource.fromGeojson(features),
state: this.state,
layerToShow: this.layer.layerDef,
zoomToFeatures: true,
layer: this.layer.layerDef,
})
return new VariableUiElement(
@ -144,7 +145,7 @@ class ApplyButton extends UIElement {
console.log("Applying auto-action on " + this.target_feature_ids.length + " features")
for (const targetFeatureId of this.target_feature_ids) {
const featureTags = this.state.allElements.getEventSourceById(targetFeatureId)
const featureTags = this.state.featureProperties.getStore(targetFeatureId)
const rendering = this.tagRenderingConfig.GetRenderValue(featureTags.data).txt
const specialRenderings = Utils.NoNull(
SubstitutedTranslation.ExtractSpecialComponents(rendering).map((x) => x.special)
@ -153,8 +154,8 @@ class ApplyButton extends UIElement {
if (specialRenderings.length == 0) {
console.warn(
"AutoApply: feature " +
targetFeatureId +
" got a rendering without supported auto actions:",
targetFeatureId +
" got a rendering without supported auto actions:",
rendering
)
}
@ -224,7 +225,7 @@ export default class AutoApplyButton implements SpecialVisualization {
"To effectively use this button, you'll need some ingredients:",
new List([
"A target layer with features for which an action is defined in a tag rendering. The following special visualisations support an autoAction: " +
supportedActions.join(", "),
supportedActions.join(", "),
"A host feature to place the auto-action on. This can be a big outline (such as a city). Another good option for this is the layer ",
new Link("current_view", "./BuiltinLayers.md#current_view"),
"Then, use a calculated tag on the host feature to determine the overlapping object ids",
@ -234,18 +235,17 @@ export default class AutoApplyButton implements SpecialVisualization {
}
constr(
state: FeaturePipelineState,
tagSource: UIEventSource<any>,
argument: string[],
guistate: DefaultGuiState
state: SpecialVisualizationState,
tagSource: UIEventSource<Record<string, string>>,
argument: string[]
): BaseUIElement {
try {
if (
!state.layoutToUse.official &&
!state.layout.official &&
!(
state.featureSwitchIsTesting.data ||
state.osmConnection._oauth_config.url ===
OsmConnection.oauth_configs["osm-test"].url
OsmConnection.oauth_configs["osm-test"].url
)
) {
const t = Translations.t.general.add.import

View file

@ -1,4 +1,3 @@
import FeaturePipelineState from "../../Logic/State/FeaturePipelineState"
import BaseUIElement from "../BaseUIElement"
import Translations from "../i18n/Translations"
import { Utils } from "../../Utils"
@ -7,7 +6,8 @@ import Img from "../Base/Img"
import { SubtleButton } from "../Base/SubtleButton"
import Toggle from "../Input/Toggle"
import { LoginToggle } from "./LoginButton"
import { SpecialVisualization } from "../SpecialVisualization"
import { SpecialVisualization, SpecialVisualizationState } from "../SpecialVisualization"
import { UIEventSource } from "../../Logic/UIEventSource"
export class CloseNoteButton implements SpecialVisualization {
public readonly funcName = "close_note"
@ -43,7 +43,11 @@ export class CloseNoteButton implements SpecialVisualization {
},
]
public constr(state: FeaturePipelineState, tags, args): BaseUIElement {
public constr(
state: SpecialVisualizationState,
tags: UIEventSource<Record<string, string>>,
args: string[]
): BaseUIElement {
const t = Translations.t.notes
const params: {
@ -78,7 +82,7 @@ export class CloseNoteButton implements SpecialVisualization {
closeButton = new Toggle(
closeButton,
params.zoomButton ?? "",
state.locationControl.map((l) => l.zoom >= Number(params.minZoom))
state.mapProperties.zoom.map((zoom) => zoom >= Number(params.minZoom))
)
}

View file

@ -4,14 +4,15 @@ import Svg from "../../Svg"
import Combine from "../Base/Combine"
import { GeoOperations } from "../../Logic/GeoOperations"
import { Utils } from "../../Utils"
import { SpecialVisualization } from "../SpecialVisualization"
import { SpecialVisualization, SpecialVisualizationState } from "../SpecialVisualization"
import { UIEventSource } from "../../Logic/UIEventSource"
export class ExportAsGpxViz implements SpecialVisualization {
funcName = "export_as_gpx"
docs = "Exports the selected feature as GPX-file"
args = []
constr(state, tagSource) {
constr(state: SpecialVisualizationState, tagSource: UIEventSource<Record<string, string>>) {
const t = Translations.t.general.download
return new SubtleButton(
@ -23,10 +24,10 @@ export class ExportAsGpxViz implements SpecialVisualization {
).onClick(() => {
console.log("Exporting as GPX!")
const tags = tagSource.data
const feature = state.allElements.ContainingFeatures.get(tags.id)
const matchingLayer = state?.layoutToUse?.getMatchingLayer(tags)
const gpx = GeoOperations.AsGpx(feature, matchingLayer)
const title = matchingLayer.title?.GetRenderValue(tags)?.Subs(tags)?.txt ?? "gpx_track"
const feature = state.indexedFeatures.featuresById.data.get(tags.id)
const layer = state?.layout?.getMatchingLayer(tags)
const gpx = GeoOperations.AsGpx(feature, { layer })
const title = layer.title?.GetRenderValue(tags)?.Subs(tags)?.txt ?? "gpx_track"
Utils.offerContentsAsDownloadableFile(gpx, title + "_mapcomplete_export.gpx", {
mimetype: "{gpx=application/gpx+xml}",
})

View file

@ -1,9 +1,8 @@
import { Store, UIEventSource } from "../../Logic/UIEventSource"
import { FixedUiElement } from "../Base/FixedUiElement"
// import Histogram from "../BigComponents/Histogram";
// import {SpecialVisualization} from "../SpecialVisualization";
import { SpecialVisualization, SpecialVisualizationState } from "../SpecialVisualization"
import Histogram from "../BigComponents/Histogram"
export class HistogramViz {
export class HistogramViz implements SpecialVisualization {
funcName = "histogram"
docs = "Create a histogram for a list of given values, read from the properties."
example =
@ -30,7 +29,11 @@ export class HistogramViz {
},
]
constr(state, tagSource: UIEventSource<any>, args: string[]) {
constr(
state: SpecialVisualizationState,
tagSource: UIEventSource<Record<string, string>>,
args: string[]
) {
let assignColors = undefined
if (args.length >= 3) {
const colors = [...args]
@ -63,10 +66,8 @@ export class HistogramViz {
return undefined
}
})
return new FixedUiElement("HISTORGRAM")
/*
return new Histogram(listSource, args[1], args[2], {
assignColor: assignColors,
})*/
})
}
}

View file

@ -1,51 +1,47 @@
import BaseUIElement from "../BaseUIElement"
import { SubtleButton } from "../Base/SubtleButton"
import { UIEventSource } from "../../Logic/UIEventSource"
import Combine from "../Base/Combine"
import { VariableUiElement } from "../Base/VariableUIElement"
import Translations from "../i18n/Translations"
import Toggle from "../Input/Toggle"
import CreateNewNodeAction from "../../Logic/Osm/Actions/CreateNewNodeAction"
import Loading from "../Base/Loading"
import { OsmConnection } from "../../Logic/Osm/OsmConnection"
import Lazy from "../Base/Lazy"
import ConfirmLocationOfPoint from "../NewPoint/ConfirmLocationOfPoint"
import Img from "../Base/Img"
import FilteredLayer from "../../Models/FilteredLayer"
import { FixedUiElement } from "../Base/FixedUiElement"
import Svg from "../../Svg"
import { Utils } from "../../Utils"
import Minimap from "../Base/Minimap"
import ShowDataLayer from "../ShowDataLayer/ShowDataLayer"
import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource"
import ShowDataMultiLayer from "../ShowDataLayer/ShowDataMultiLayer"
import CreateWayWithPointReuseAction, {
MergePointConfig,
} from "../../Logic/Osm/Actions/CreateWayWithPointReuseAction"
import OsmChangeAction from "../../Logic/Osm/Actions/OsmChangeAction"
import FeatureSource from "../../Logic/FeatureSource/FeatureSource"
import { OsmObject, OsmWay } from "../../Logic/Osm/OsmObject"
import FeaturePipelineState from "../../Logic/State/FeaturePipelineState"
import { DefaultGuiState } from "../DefaultGuiState"
import { PresetInfo } from "../BigComponents/SimpleAddUI"
import { TagUtils } from "../../Logic/Tags/TagUtils"
import { And } from "../../Logic/Tags/And"
import ReplaceGeometryAction from "../../Logic/Osm/Actions/ReplaceGeometryAction"
import CreateMultiPolygonWithPointReuseAction from "../../Logic/Osm/Actions/CreateMultiPolygonWithPointReuseAction"
import { Tag } from "../../Logic/Tags/Tag"
import TagApplyButton from "./TagApplyButton"
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
import conflation_json from "../../assets/layers/conflation/conflation.json"
import { GeoOperations } from "../../Logic/GeoOperations"
import { LoginToggle } from "./LoginButton"
import { AutoAction } from "./AutoApplyButton"
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"
import { Changes } from "../../Logic/Osm/Changes"
import { ElementStorage } from "../../Logic/ElementStorage"
import Hash from "../../Logic/Web/Hash"
import { PreciseInput } from "../../Models/ThemeConfig/PresetConfig"
import { SpecialVisualization } from "../SpecialVisualization"
import BaseUIElement from "../BaseUIElement";
import { SubtleButton } from "../Base/SubtleButton";
import { UIEventSource } from "../../Logic/UIEventSource";
import Combine from "../Base/Combine";
import { VariableUiElement } from "../Base/VariableUIElement";
import Translations from "../i18n/Translations";
import Toggle from "../Input/Toggle";
import CreateNewNodeAction from "../../Logic/Osm/Actions/CreateNewNodeAction";
import Loading from "../Base/Loading";
import { OsmConnection } from "../../Logic/Osm/OsmConnection";
import Lazy from "../Base/Lazy";
import ConfirmLocationOfPoint from "../NewPoint/ConfirmLocationOfPoint";
import Img from "../Base/Img";
import FilteredLayer from "../../Models/FilteredLayer";
import { FixedUiElement } from "../Base/FixedUiElement";
import Svg from "../../Svg";
import { Utils } from "../../Utils";
import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource";
import CreateWayWithPointReuseAction, { MergePointConfig } from "../../Logic/Osm/Actions/CreateWayWithPointReuseAction";
import OsmChangeAction, { OsmCreateAction } from "../../Logic/Osm/Actions/OsmChangeAction";
import FeatureSource from "../../Logic/FeatureSource/FeatureSource";
import { OsmObject, OsmWay } from "../../Logic/Osm/OsmObject";
import { PresetInfo } from "../BigComponents/SimpleAddUI";
import { TagUtils } from "../../Logic/Tags/TagUtils";
import { And } from "../../Logic/Tags/And";
import ReplaceGeometryAction from "../../Logic/Osm/Actions/ReplaceGeometryAction";
import CreateMultiPolygonWithPointReuseAction from "../../Logic/Osm/Actions/CreateMultiPolygonWithPointReuseAction";
import { Tag } from "../../Logic/Tags/Tag";
import TagApplyButton from "./TagApplyButton";
import LayerConfig from "../../Models/ThemeConfig/LayerConfig";
import conflation_json from "../../assets/layers/conflation/conflation.json";
import { GeoOperations } from "../../Logic/GeoOperations";
import { LoginToggle } from "./LoginButton";
import { AutoAction } from "./AutoApplyButton";
import Hash from "../../Logic/Web/Hash";
import { PreciseInput } from "../../Models/ThemeConfig/PresetConfig";
import { SpecialVisualization, SpecialVisualizationState } from "../SpecialVisualization";
import Maproulette from "../../Logic/Maproulette";
import { Feature, Point } from "geojson";
import { LayerConfigJson } from "../../Models/ThemeConfig/Json/LayerConfigJson";
import ShowDataLayer from "../Map/ShowDataLayer";
import { MapLibreAdaptor } from "../Map/MapLibreAdaptor";
import SvelteUIElement from "../Base/SvelteUIElement";
import MaplibreMap from "../Map/MaplibreMap.svelte";
/**
* A helper class for the various import-flows.
@ -106,7 +102,7 @@ ${Utils.special_visualizations_importRequirementDocs}
}
abstract constructElement(
state: FeaturePipelineState,
state: SpecialVisualizationState,
args: {
max_snap_distance: string
snap_onto_layers: string
@ -116,13 +112,16 @@ ${Utils.special_visualizations_importRequirementDocs}
newTags: UIEventSource<any>
targetLayer: string
},
tagSource: UIEventSource<any>,
guiState: DefaultGuiState,
feature: any,
tagSource: UIEventSource<Record<string, string>>,
feature: Feature,
onCancelClicked: () => void
): BaseUIElement
constr(state, tagSource: UIEventSource<any>, argsRaw, guiState) {
constr(
state: SpecialVisualizationState,
tagSource: UIEventSource<Record<string, string>>,
argsRaw: string[]
) {
/**
* Some generic import button pre-validation is implemented here:
* - Are we logged in?
@ -139,7 +138,7 @@ ${Utils.special_visualizations_importRequirementDocs}
{
// Some initial validation
if (
!state.layoutToUse.official &&
!state.layout.official &&
!(
state.featureSwitchIsTesting.data ||
state.osmConnection._oauth_config.url ===
@ -148,11 +147,9 @@ ${Utils.special_visualizations_importRequirementDocs}
) {
return new Combine([t.officialThemesOnly.SetClass("alert"), t.howToTest])
}
const targetLayer: FilteredLayer = state.filteredLayers.data.filter(
(fl) => fl.layerDef.id === args.targetLayer
)[0]
const targetLayer: FilteredLayer = state.layerState.filteredLayers.get(args.targetLayer)
if (targetLayer === undefined) {
const e = `Target layer not defined: error in import button for theme: ${state.layoutToUse.id}: layer ${args.targetLayer} not found`
const e = `Target layer not defined: error in import button for theme: ${state.layout.id}: layer ${args.targetLayer} not found`
console.error(e)
return new FixedUiElement(e).SetClass("alert")
}
@ -167,7 +164,7 @@ ${Utils.special_visualizations_importRequirementDocs}
const inviteToImportButton = new SubtleButton(img, args.text)
const id = tagSource.data.id
const feature = state.allElements.ContainingFeatures.get(id)
const feature = state.indexedFeatures.featuresById.data.get(id)
// Explanation of the tags that will be applied onto the imported/conflated object
@ -205,22 +202,13 @@ ${Utils.special_visualizations_importRequirementDocs}
return tags._imported === "yes"
})
/**** THe actual panel showing the import guiding map ****/
const importGuidingPanel = this.constructElement(
state,
args,
tagSource,
guiState,
feature,
() => importClicked.setData(false)
/**** The actual panel showing the import guiding map ****/
const importGuidingPanel = this.constructElement(state, args, tagSource, feature, () =>
importClicked.setData(false)
)
const importFlow = new Toggle(
new Toggle(
new Loading(t0.stillLoading),
importGuidingPanel,
state.featurePipeline.runningQuery
),
new Toggle(new Loading(t0.stillLoading), importGuidingPanel, state.dataIsLoading),
inviteToImportButton,
importClicked
)
@ -230,7 +218,7 @@ ${Utils.special_visualizations_importRequirementDocs}
new Toggle(
new Toggle(t.hasBeenImported, importFlow, isImported),
t.zoomInMore.SetClass("alert block"),
state.locationControl.map((l) => l.zoom >= 18)
state.mapProperties.zoom.map((zoom) => zoom >= 18)
),
pleaseLoginButton,
state
@ -258,8 +246,13 @@ ${Utils.special_visualizations_importRequirementDocs}
protected abstract canBeImported(feature: any)
private static readonly conflationLayer = new LayerConfig(
<LayerConfigJson>conflation_json,
"all_known_layers",
true
)
protected createConfirmPanelForWay(
state: FeaturePipelineState,
state: SpecialVisualizationState,
args: {
max_snap_distance: string
snap_onto_layers: string
@ -270,32 +263,32 @@ ${Utils.special_visualizations_importRequirementDocs}
},
feature: any,
originalFeatureTags: UIEventSource<any>,
action: OsmChangeAction & { getPreview(): Promise<FeatureSource>; newElementId?: string },
action: OsmChangeAction & { getPreview?(): Promise<FeatureSource>; newElementId?: string },
onCancel: () => void
): BaseUIElement {
const self = this
const confirmationMap = Minimap.createMiniMap({
allowMoving: state.featureSwitchIsDebugging.data ?? false,
background: state.backgroundLayer,
const map = new UIEventSource(undefined)
new MapLibreAdaptor(map, {
allowMoving: UIEventSource.feedFrom(state.featureSwitchIsTesting),
allowZooming: UIEventSource.feedFrom(state.featureSwitchIsTesting),
rasterLayer: state.mapProperties.rasterLayer,
})
const confirmationMap = new SvelteUIElement(MaplibreMap, { map })
confirmationMap.SetStyle("height: 20rem; overflow: hidden").SetClass("rounded-xl")
// SHow all relevant data - including (eventually) the way of which the geometry will be replaced
new ShowDataMultiLayer({
leafletMap: confirmationMap.leafletMap,
zoomToFeatures: true,
features: StaticFeatureSource.fromGeojson([feature]),
state: state,
layers: state.filteredLayers,
})
ShowDataLayer.showMultipleLayers(
map,
new StaticFeatureSource([feature]),
state.layout.layers,
{ zoomToFeatures: true }
)
// Show all relevant data - including (eventually) the way of which the geometry will be replaced
action.getPreview().then((changePreview) => {
new ShowDataLayer({
leafletMap: confirmationMap.leafletMap,
new ShowDataLayer(map, {
zoomToFeatures: false,
features: changePreview,
state,
layerToShow: new LayerConfig(conflation_json, "all_known_layers", true),
layer: AbstractImportButton.conflationLayer,
})
})
@ -317,9 +310,9 @@ ${Utils.special_visualizations_importRequirementDocs}
{
originalFeatureTags.data["_imported"] = "yes"
originalFeatureTags.ping() // will set isImported as per its definition
state.changes.applyAction(action)
await state.changes.applyAction(action)
const newId = action.newElementId ?? action.mainObjectId
state.selectedElement.setData(state.allElements.ContainingFeatures.get(newId))
state.selectedElement.setData(state.indexedFeatures.featuresById.data.get(newId))
}
})
@ -392,7 +385,7 @@ export class ConflateButton extends AbstractImportButton {
}
constructElement(
state: FeaturePipelineState,
state: SpecialVisualizationState,
args: {
max_snap_distance: string
snap_onto_layers: string
@ -403,8 +396,7 @@ export class ConflateButton extends AbstractImportButton {
targetLayer: string
},
tagSource: UIEventSource<any>,
guiState: DefaultGuiState,
feature: any,
feature: Feature,
onCancelClicked: () => void
): BaseUIElement {
const nodesMustMatch = args.snap_onto_layers
@ -424,10 +416,15 @@ export class ConflateButton extends AbstractImportButton {
const key = args["way_to_conflate"]
const wayToConflate = tagSource.data[key]
feature = GeoOperations.removeOvernoding(feature)
const action = new ReplaceGeometryAction(state, feature, wayToConflate, {
theme: state.layoutToUse.id,
newTags: args.newTags.data,
})
const action: OsmChangeAction & { getPreview(): Promise<any> } = new ReplaceGeometryAction(
state,
feature,
wayToConflate,
{
theme: state.layout.id,
newTags: args.newTags.data,
}
)
return this.createConfirmPanelForWay(
state,
@ -498,9 +495,9 @@ export class ImportWayButton extends AbstractImportButton implements AutoAction
newTags: UIEventSource<any>
targetLayer: string
},
state: FeaturePipelineState,
state: SpecialVisualizationState,
mergeConfigs: any[]
) {
): OsmCreateAction & { getPreview(): Promise<FeatureSource>; newElementId?: string } {
const coors = feature.geometry.coordinates
if (feature.geometry.type === "Polygon" && coors.length > 1) {
const outer = coors[0]
@ -525,8 +522,8 @@ export class ImportWayButton extends AbstractImportButton implements AutoAction
}
async applyActionOn(
state: { layoutToUse: LayoutConfig; changes: Changes; allElements: ElementStorage },
originalFeatureTags: UIEventSource<any>,
state: SpecialVisualizationState,
originalFeatureTags: UIEventSource<Record<string, string>>,
argument: string[]
): Promise<void> {
const id = originalFeatureTags.data.id
@ -535,14 +532,9 @@ export class ImportWayButton extends AbstractImportButton implements AutoAction
}
AbstractImportButton.importedIds.add(originalFeatureTags.data.id)
const args = this.parseArgs(argument, originalFeatureTags)
const feature = state.allElements.ContainingFeatures.get(id)
const feature = state.indexedFeatures.featuresById.data.get(id)
const mergeConfigs = this.GetMergeConfig(args)
const action = ImportWayButton.CreateAction(
feature,
args,
<FeaturePipelineState>state,
mergeConfigs
)
const action = ImportWayButton.CreateAction(feature, args, state, mergeConfigs)
await state.changes.applyAction(action)
}
@ -557,7 +549,13 @@ export class ImportWayButton extends AbstractImportButton implements AutoAction
return deps
}
constructElement(state, args, originalFeatureTags, guiState, feature, onCancel): BaseUIElement {
constructElement(
state: SpecialVisualizationState,
args,
originalFeatureTags: UIEventSource<Record<string, string>>,
feature,
onCancel
): BaseUIElement {
const geometry = feature.geometry
if (!(geometry.type == "LineString" || geometry.type === "Polygon")) {
@ -567,7 +565,12 @@ export class ImportWayButton extends AbstractImportButton implements AutoAction
// Upload the way to OSM
const mergeConfigs = this.GetMergeConfig(args)
let action = ImportWayButton.CreateAction(feature, args, state, mergeConfigs)
let action: OsmCreateAction & {getPreview?: any} = ImportWayButton.CreateAction(
feature,
args,
state,
mergeConfigs
)
return this.createConfirmPanelForWay(
state,
args,
@ -663,10 +666,9 @@ export class ImportPointButton extends AbstractImportButton {
note_id: string
maproulette_id: string
},
state: FeaturePipelineState,
guiState: DefaultGuiState,
state: SpecialVisualizationState,
originalFeatureTags: UIEventSource<any>,
feature: any,
feature: Feature<Point>,
onCancel: () => void,
close: () => void
): BaseUIElement {
@ -690,7 +692,7 @@ export class ImportPointButton extends AbstractImportButton {
}
const newElementAction = new CreateNewNodeAction(tags, location.lat, location.lon, {
theme: state.layoutToUse.id,
theme: state.layout.id,
changeType: "import",
snapOnto: <OsmWay>snapOnto,
specialMotivation: specialMotivation,
@ -698,7 +700,7 @@ export class ImportPointButton extends AbstractImportButton {
await state.changes.applyAction(newElementAction)
state.selectedElement.setData(
state.allElements.ContainingFeatures.get(newElementAction.newElementId)
state.indexedFeatures.featuresById.data.get(newElementAction.newElementId)
)
Hash.hash.setData(newElementAction.newElementId)
@ -742,19 +744,17 @@ export class ImportPointButton extends AbstractImportButton {
const presetInfo = <PresetInfo>{
tags: args.newTags.data,
icon: () => new Img(args.icon),
layerToAddTo: state.filteredLayers.data.filter(
(l) => l.layerDef.id === args.targetLayer
)[0],
layerToAddTo: state.layerState.filteredLayers.get(args.targetLayer),
name: args.text,
title: Translations.T(args.text),
preciseInput: preciseInputSpec, // must be explicitely assigned, if 'undefined' won't work otherwise
boundsFactor: 3,
}
const [lon, lat] = feature.geometry.coordinates
const [lon, lat] = <[number,number]> feature.geometry.coordinates
return new ConfirmLocationOfPoint(
state,
guiState.filterViewIsOpened,
state.guistate.filterViewIsOpened,
presetInfo,
Translations.W(args.text),
{
@ -783,10 +783,9 @@ export class ImportPointButton extends AbstractImportButton {
}
constructElement(
state,
state: SpecialVisualizationState,
args,
originalFeatureTags,
guiState,
feature,
onCancel: () => void
): BaseUIElement {
@ -797,7 +796,6 @@ export class ImportPointButton extends AbstractImportButton {
ImportPointButton.createConfirmPanelForPoint(
args,
state,
guiState,
originalFeatureTags,
feature,
onCancel,

View file

@ -1,9 +1,7 @@
import { SpecialVisualization } from "../SpecialVisualization"
import { SpecialVisualization, SpecialVisualizationState } from "../SpecialVisualization"
import BaseUIElement from "../BaseUIElement"
import { UIEventSource } from "../../Logic/UIEventSource"
import FeaturePipelineState from "../../Logic/State/FeaturePipelineState"
import { VariableUiElement } from "../Base/VariableUIElement"
import { OsmTags } from "../../Models/OsmFeature"
import all_languages from "../../assets/language_translations.json"
import { Translation } from "../i18n/Translation"
import Combine from "../Base/Combine"
@ -16,10 +14,9 @@ import ChangeTagAction from "../../Logic/Osm/Actions/ChangeTagAction"
import { And } from "../../Logic/Tags/And"
import { Tag } from "../../Logic/Tags/Tag"
import { EditButton, SaveButton } from "./SaveButton"
import { FixedUiElement } from "../Base/FixedUiElement"
import Translations from "../i18n/Translations"
import Toggle from "../Input/Toggle"
import { On } from "../../Models/ThemeConfig/Conversion/Conversion"
import { Feature } from "geojson"
export class LanguageElement implements SpecialVisualization {
funcName: string = "language_chooser"
@ -79,9 +76,10 @@ export class LanguageElement implements SpecialVisualization {
`
constr(
state: FeaturePipelineState,
tagSource: UIEventSource<OsmTags>,
argument: string[]
state: SpecialVisualizationState,
tagSource: UIEventSource<Record<string, string>>,
argument: string[],
feature: Feature
): BaseUIElement {
let [key, question, item_render, single_render, all_render, on_no_known_languages, mode] =
argument
@ -172,7 +170,7 @@ export class LanguageElement implements SpecialVisualization {
new And(selection),
tagSource.data,
{
theme: state?.layoutToUse?.id ?? "unkown",
theme: state?.layout?.id ?? "unkown",
changeType: "answer",
}
)

View file

@ -2,7 +2,9 @@ import { GeoOperations } from "../../Logic/GeoOperations"
import { MapillaryLink } from "../BigComponents/MapillaryLink"
import { UIEventSource } from "../../Logic/UIEventSource"
import Loc from "../../Models/Loc"
import { SpecialVisualization } from "../SpecialVisualization"
import { SpecialVisualization, SpecialVisualizationState } from "../SpecialVisualization"
import { Feature } from "geojson"
import BaseUIElement from "../BaseUIElement"
export class MapillaryLinkVis implements SpecialVisualization {
funcName = "mapillary_link"
@ -15,9 +17,13 @@ export class MapillaryLinkVis implements SpecialVisualization {
},
]
public constr(state, tagsSource, args) {
const feat = state.allElements.ContainingFeatures.get(tagsSource.data.id)
const [lon, lat] = GeoOperations.centerpointCoordinates(feat)
public constr(
state: SpecialVisualizationState,
tagsSource: UIEventSource<Record<string, string>>,
args: string[],
feature: Feature
): BaseUIElement {
const [lon, lat] = GeoOperations.centerpointCoordinates(feature)
let zoom = Number(args[0])
if (isNaN(zoom)) {
zoom = 18

View file

@ -1,9 +1,14 @@
import { Store, UIEventSource } from "../../Logic/UIEventSource"
import Loc from "../../Models/Loc"
import Minimap from "../Base/Minimap"
import ShowDataMultiLayer from "../ShowDataLayer/ShowDataMultiLayer"
import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource"
import { SpecialVisualization } from "../SpecialVisualization"
import { SpecialVisualization, SpecialVisualizationState } from "../SpecialVisualization"
import { Feature } from "geojson"
import { MapLibreAdaptor } from "../Map/MapLibreAdaptor"
import SvelteUIElement from "../Base/SvelteUIElement"
import MaplibreMap from "../Map/MaplibreMap.svelte"
import PerLayerFeatureSourceSplitter from "../../Logic/FeatureSource/PerLayerFeatureSourceSplitter"
import FilteredLayer from "../../Models/FilteredLayer"
import ShowDataLayer from "../Map/ShowDataLayer"
import { stat } from "fs"
export class MinimapViz implements SpecialVisualization {
funcName = "minimap"
@ -22,16 +27,20 @@ export class MinimapViz implements SpecialVisualization {
]
example: "`{minimap()}`, `{minimap(17, id, _list_of_embedded_feature_ids_calculated_by_calculated_tag):height:10rem; border: 2px solid black}`"
constr(state, tagSource, args, _) {
constr(
state: SpecialVisualizationState,
tagSource: UIEventSource<Record<string, string>>,
args: string[]
) {
if (state === undefined) {
return undefined
}
const keys = [...args]
keys.splice(0, 1)
const featureStore = state.allElements.ContainingFeatures
const featuresToShow: Store<{ freshness: Date; feature: any }[]> = tagSource.map(
(properties) => {
const features: { freshness: Date; feature: any }[] = []
const featuresToShow: Store<Feature[]> = state.indexedFeatures.featuresById.map(
(featuresById) => {
const properties = tagSource.data
const features: Feature[] = []
for (const key of keys) {
const value = properties[key]
if (value === undefined || value === null) {
@ -45,21 +54,22 @@ export class MinimapViz implements SpecialVisualization {
}
for (const id of idList) {
const feature = featureStore.get(id)
const feature = featuresById.get(id)
if (feature === undefined) {
console.warn("No feature found for id ", id)
continue
}
features.push({
freshness: new Date(),
feature,
})
features.push(feature)
}
}
return features
}
},
[tagSource]
)
const properties = tagSource.data
const mlmap = new UIEventSource(undefined)
const mla = new MapLibreAdaptor(mlmap)
let zoom = 18
if (args[0]) {
const parsed = Number(args[0])
@ -67,33 +77,18 @@ export class MinimapViz implements SpecialVisualization {
zoom = parsed
}
}
const locationSource = new UIEventSource<Loc>({
lat: Number(properties._lat),
lon: Number(properties._lon),
zoom: zoom,
})
const minimap = Minimap.createMiniMap({
background: state.backgroundLayer,
location: locationSource,
allowMoving: false,
})
mla.zoom.setData(zoom)
mla.allowMoving.setData(false)
mla.allowZooming.setData(false)
locationSource.addCallback((loc) => {
if (loc.zoom > zoom) {
// We zoom back
locationSource.data.zoom = zoom
locationSource.ping()
}
})
ShowDataLayer.showMultipleLayers(
mlmap,
new StaticFeatureSource(featuresToShow),
state.layout.layers
)
new ShowDataMultiLayer({
leafletMap: minimap["leafletMap"],
zoomToFeatures: true,
layers: state.filteredLayers,
features: new StaticFeatureSource(featuresToShow),
})
minimap.SetStyle("overflow: hidden; pointer-events: none;")
return minimap
return new SvelteUIElement(MaplibreMap, { map: mlmap }).SetStyle(
"overflow: hidden; pointer-events: none;"
)
}
}

View file

@ -2,17 +2,14 @@ import { Store } from "../../Logic/UIEventSource"
import BaseUIElement from "../BaseUIElement"
import Combine from "../Base/Combine"
import { SubtleButton } from "../Base/SubtleButton"
import { Changes } from "../../Logic/Osm/Changes"
import { FixedUiElement } from "../Base/FixedUiElement"
import Translations from "../i18n/Translations"
import { VariableUiElement } from "../Base/VariableUIElement"
import ChangeTagAction from "../../Logic/Osm/Actions/ChangeTagAction"
import { Tag } from "../../Logic/Tags/Tag"
import { ElementStorage } from "../../Logic/ElementStorage"
import { And } from "../../Logic/Tags/And"
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"
import Toggle from "../Input/Toggle"
import { OsmConnection } from "../../Logic/Osm/OsmConnection"
import { SpecialVisualizationState } from "../SpecialVisualization"
export interface MultiApplyParams {
featureIds: Store<string[]>
@ -21,12 +18,7 @@ export interface MultiApplyParams {
autoapply: boolean
overwrite: boolean
tagsSource: Store<any>
state: {
changes: Changes
allElements: ElementStorage
layoutToUse: LayoutConfig
osmConnection: OsmConnection
}
state: SpecialVisualizationState
}
class MultiApplyExecutor {
@ -68,14 +60,14 @@ class MultiApplyExecutor {
console.log("Multi-applying changes...")
const featuresToChange = this.params.featureIds.data
const changes = this.params.state.changes
const allElements = this.params.state.allElements
const allElements = this.params.state.featureProperties
const keysToChange = this.params.keysToApply
const overwrite = this.params.overwrite
const selfTags = this.params.tagsSource.data
const theme = this.params.state.layoutToUse.id
const theme = this.params.state.layout.id
for (const id of featuresToChange) {
const tagsToApply: Tag[] = []
const otherFeatureTags = allElements.getEventSourceById(id).data
const otherFeatureTags = allElements.getStore(id).data
for (const key of keysToChange) {
const newValue = selfTags[key]
if (newValue === undefined) {

View file

@ -1,6 +1,6 @@
import { Store } from "../../Logic/UIEventSource"
import { Store, UIEventSource } from "../../Logic/UIEventSource"
import MultiApply from "./MultiApply"
import { SpecialVisualization } from "../SpecialVisualization"
import { SpecialVisualization, SpecialVisualizationState } from "../SpecialVisualization"
export class MultiApplyViz implements SpecialVisualization {
funcName = "multi_apply"
@ -31,7 +31,11 @@ export class MultiApplyViz implements SpecialVisualization {
example =
"{multi_apply(_features_with_the_same_name_within_100m, name:etymology:wikidata;name:etymology, Apply etymology information on all nearby objects with the same name)}"
constr(state, tagsSource, args) {
constr(
state: SpecialVisualizationState,
tagsSource: UIEventSource<Record<string, string>>,
args: string[]
) {
const featureIdsKey = args[0]
const keysToApply = args[1].split(";")
const text = args[2]

View file

@ -1,6 +1,4 @@
import FeaturePipelineState from "../../Logic/State/FeaturePipelineState"
import { UIEventSource } from "../../Logic/UIEventSource"
import { DefaultGuiState } from "../DefaultGuiState"
import BaseUIElement from "../BaseUIElement"
import Translations from "../i18n/Translations"
import { GeoOperations } from "../../Logic/GeoOperations"
@ -19,7 +17,7 @@ import { VariableUiElement } from "../Base/VariableUIElement"
import Toggle from "../Input/Toggle"
import Title from "../Base/Title"
import { MapillaryLinkVis } from "./MapillaryLinkVis"
import { SpecialVisualization } from "../SpecialVisualization"
import { SpecialVisualization, SpecialVisualizationState } from "../SpecialVisualization"
export class NearbyImageVis implements SpecialVisualization {
args: { name: string; defaultValue?: string; doc: string; required?: boolean }[] = [
@ -39,14 +37,13 @@ export class NearbyImageVis implements SpecialVisualization {
funcName = "nearby_images"
constr(
state: FeaturePipelineState,
tagSource: UIEventSource<any>,
args: string[],
guistate: DefaultGuiState
state: SpecialVisualizationState,
tagSource: UIEventSource<Record<string, string>>,
args: string[]
): BaseUIElement {
const t = Translations.t.image.nearbyPictures
const mode: "open" | "expandable" | "collapsable" = <any>args[0]
const feature = state.allElements.ContainingFeatures.get(tagSource.data.id)
const feature = state.indexedFeatures.featuresById.data.get(tagSource.data.id)
const [lon, lat] = GeoOperations.centerpointCoordinates(feature)
const id: string = tagSource.data["id"]
const canBeEdited: boolean = !!id?.match("(node|way|relation)/-?[0-9]+")
@ -69,7 +66,7 @@ export class NearbyImageVis implements SpecialVisualization {
}
await state?.changes?.applyAction(
new ChangeTagAction(id, new And(tags), tagSource.data, {
theme: state?.layoutToUse.id,
theme: state?.layout.id,
changeType: "link-image",
})
)
@ -116,8 +113,8 @@ export class NearbyImageVis implements SpecialVisualization {
maxDaysOld: 365 * 3,
}
const slideshow = canBeEdited
? new SelectOneNearbyImage(options, state)
: new NearbyImages(options, state)
? new SelectOneNearbyImage(options, state.indexedFeatures)
: new NearbyImages(options, state.indexedFeatures)
const controls = new Combine([
towardsCenter,
new Combine([

View file

@ -13,9 +13,10 @@ import Translations from "../i18n/Translations"
import { Mapillary } from "../../Logic/ImageProviders/Mapillary"
import { SubtleButton } from "../Base/SubtleButton"
import { GeoOperations } from "../../Logic/GeoOperations"
import { ElementStorage } from "../../Logic/ElementStorage"
import Lazy from "../Base/Lazy"
import P4C from "pic4carto"
import { IndexedFeatureSource } from "../../Logic/FeatureSource/FeatureSource"
export interface P4CPicture {
pictureUrl: string
date?: number
@ -47,15 +48,15 @@ export interface NearbyImageOptions {
}
class ImagesInLoadedDataFetcher {
private allElements: ElementStorage
private indexedFeatures: IndexedFeatureSource
constructor(state: { allElements: ElementStorage }) {
this.allElements = state.allElements
constructor(indexedFeatures: IndexedFeatureSource) {
this.indexedFeatures = indexedFeatures
}
public fetchAround(loc: { lon: number; lat: number; searchRadius?: number }): P4CPicture[] {
const foundImages: P4CPicture[] = []
this.allElements.ContainingFeatures.forEach((feature) => {
this.indexedFeatures.features.data.forEach((feature) => {
const props = feature.properties
const images = []
if (props.image) {
@ -100,7 +101,7 @@ class ImagesInLoadedDataFetcher {
}
export default class NearbyImages extends Lazy {
constructor(options: NearbyImageOptions, state?: { allElements: ElementStorage }) {
constructor(options: NearbyImageOptions, state?: IndexedFeatureSource) {
super(() => {
const t = Translations.t.image.nearbyPictures
const shownImages = options.shownImagesCount ?? new UIEventSource(25)
@ -171,10 +172,7 @@ export default class NearbyImages extends Lazy {
)
}
private static buildPictureFetcher(
options: NearbyImageOptions,
state?: { allElements: ElementStorage }
) {
private static buildPictureFetcher(options: NearbyImageOptions, state?: IndexedFeatureSource) {
const picManager = new P4C.PicturesManager({})
const searchRadius = options.searchRadius ?? 500
@ -283,7 +281,7 @@ export class SelectOneNearbyImage extends NearbyImages implements InputElement<P
constructor(
options: NearbyImageOptions & { value?: UIEventSource<P4CPicture> },
state?: { allElements: ElementStorage }
state?: IndexedFeatureSource
) {
super(options, state)
this.value = options.value ?? new UIEventSource<P4CPicture>(undefined)

View file

@ -12,7 +12,7 @@ import Combine from "../Base/Combine"
import Svg from "../../Svg"
import Translations from "../i18n/Translations"
import AllImageProviders from "../../Logic/ImageProviders/AllImageProviders"
import { SpecialVisualization } from "../SpecialVisualization"
import { SpecialVisualization, SpecialVisualizationState } from "../SpecialVisualization";
export class PlantNetDetectionViz implements SpecialVisualization {
funcName = "plantnet_detection"
@ -27,7 +27,7 @@ export class PlantNetDetectionViz implements SpecialVisualization {
},
]
public constr(state, tags, args) {
public constr(state: SpecialVisualizationState, tags: UIEventSource<Record<string, string>>, args: string[]) {
let imagePrefixes: string[] = undefined
if (args.length > 0) {
imagePrefixes = [].concat(...args.map((a) => a.split(",")))
@ -53,7 +53,7 @@ export class PlantNetDetectionViz implements SpecialVisualization {
]),
tags.data,
{
theme: state.layoutToUse.id,
theme: state.layout.id,
changeType: "plantnet-ai-detection",
}
)

View file

@ -3,7 +3,7 @@ import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
import ShareButton from "../BigComponents/ShareButton"
import Svg from "../../Svg"
import { FixedUiElement } from "../Base/FixedUiElement"
import { SpecialVisualization } from "../SpecialVisualization"
import { SpecialVisualization, SpecialVisualizationState } from "../SpecialVisualization";
export class ShareLinkViz implements SpecialVisualization {
funcName = "share_link"
@ -17,12 +17,12 @@ export class ShareLinkViz implements SpecialVisualization {
},
]
public constr(state, tagSource: UIEventSource<any>, args) {
public constr(state: SpecialVisualizationState, tagSource: UIEventSource<Record<string, string>>, args: string[]) {
if (window.navigator.share) {
const generateShareData = () => {
const title = state?.layoutToUse?.title?.txt ?? "MapComplete"
const title = state?.layout?.title?.txt ?? "MapComplete"
let matchingLayer: LayerConfig = state?.layoutToUse?.getMatchingLayer(
let matchingLayer: LayerConfig = state?.layout?.getMatchingLayer(
tagSource?.data
)
let name =
@ -41,7 +41,7 @@ export class ShareLinkViz implements SpecialVisualization {
return {
title: name,
url: url,
text: state?.layoutToUse?.shortDescription?.txt ?? "MapComplete",
text: state?.layout?.shortDescription?.txt ?? "MapComplete",
}
}

View file

@ -4,7 +4,7 @@ import { VariableUiElement } from "../Base/VariableUIElement"
import BaseUIElement from "../BaseUIElement"
import EditableTagRendering from "./EditableTagRendering"
import Combine from "../Base/Combine"
import { SpecialVisualization } from "../SpecialVisualization"
import { SpecialVisualization, SpecialVisualizationState } from "../SpecialVisualization"
export class StealViz implements SpecialVisualization {
funcName = "steal"
@ -21,12 +21,12 @@ export class StealViz implements SpecialVisualization {
required: true,
},
]
constr(state, featureTags, args) {
constr(state: SpecialVisualizationState, featureTags, args) {
const [featureIdKey, layerAndtagRenderingIds] = args
const tagRenderings: [LayerConfig, TagRenderingConfig][] = []
for (const layerAndTagRenderingId of layerAndtagRenderingIds.split(";")) {
const [layerId, tagRenderingId] = layerAndTagRenderingId.trim().split(".")
const layer = state.layoutToUse.layers.find((l) => l.id === layerId)
const layer = state.layout.layers.find((l) => l.id === layerId)
const tagRendering = layer.tagRenderings.find((tr) => tr.id === tagRenderingId)
tagRenderings.push([layer, tagRendering])
}
@ -39,7 +39,7 @@ export class StealViz implements SpecialVisualization {
if (featureId === undefined) {
return undefined
}
const otherTags = state.allElements.getEventSourceById(featureId)
const otherTags = state.featureProperties.getStore(featureId)
const elements: BaseUIElement[] = []
for (const [layer, tagRendering] of tagRenderings) {
const el = new EditableTagRendering(

View file

@ -11,10 +11,9 @@ import { And } from "../../Logic/Tags/And"
import Toggle from "../Input/Toggle"
import { Utils } from "../../Utils"
import { Tag } from "../../Logic/Tags/Tag"
import FeaturePipelineState from "../../Logic/State/FeaturePipelineState"
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"
import { Changes } from "../../Logic/Osm/Changes"
import { SpecialVisualization } from "../SpecialVisualization"
import { SpecialVisualization, SpecialVisualizationState } from "../SpecialVisualization"
export default class TagApplyButton implements AutoAction, SpecialVisualization {
public readonly funcName = "tag_apply"
@ -76,7 +75,10 @@ export default class TagApplyButton implements AutoAction, SpecialVisualization
return tgsSpec
}
public static generateTagsToApply(spec: string, tagSource: Store<any>): Store<Tag[]> {
public static generateTagsToApply(
spec: string,
tagSource: Store<Record<string, string>>
): Store<Tag[]> {
// Check whether we need to look up a single value
if (!spec.includes(";") && !spec.includes("=") && spec.includes("$")) {
@ -110,7 +112,7 @@ export default class TagApplyButton implements AutoAction, SpecialVisualization
async applyActionOn(
state: {
layoutToUse: LayoutConfig
layout: LayoutConfig
changes: Changes
},
tags: UIEventSource<any>,
@ -125,7 +127,7 @@ export default class TagApplyButton implements AutoAction, SpecialVisualization
new And(tagsToApply.data),
tags.data, // We pass in the tags of the selected element, not the tags of the target element!
{
theme: state.layoutToUse.id,
theme: state.layout.id,
changeType: "answer",
}
)
@ -133,8 +135,8 @@ export default class TagApplyButton implements AutoAction, SpecialVisualization
}
public constr(
state: FeaturePipelineState,
tags: UIEventSource<any>,
state: SpecialVisualizationState,
tags: UIEventSource<Record<string, string>>,
args: string[]
): BaseUIElement {
const tagsToApply = TagApplyButton.generateTagsToApply(args[0], tags)
@ -162,9 +164,9 @@ export default class TagApplyButton implements AutoAction, SpecialVisualization
const applyButton = new SubtleButton(
image,
new Combine([msg, tagsExplanation]).SetClass("flex flex-col")
).onClick(() => {
self.applyActionOn(state, tags, args)
).onClick(async () => {
applied.setData(true)
await self.applyActionOn(state, tags, args)
})
return new Toggle(

View file

View file

@ -6,6 +6,7 @@ import { SubstitutedTranslation } from "../SubstitutedTranslation"
import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig"
import Combine from "../Base/Combine"
import Img from "../Base/Img"
import { SpecialVisualisationState } from "../SpecialVisualization"
/***
* Displays the correct value for a known tagrendering
@ -14,7 +15,7 @@ export default class TagRenderingAnswer extends VariableUiElement {
constructor(
tagsSource: UIEventSource<any>,
configuration: TagRenderingConfig,
state: any,
state: SpecialVisualisationState,
contentClasses: string = "",
contentStyle: string = "",
options?: {
@ -24,6 +25,7 @@ export default class TagRenderingAnswer extends VariableUiElement {
if (configuration === undefined) {
throw "Trying to generate a tagRenderingAnswer without configuration..."
}
UIEventSource
if (tagsSource === undefined) {
throw "Trying to generate a tagRenderingAnswer without tagSource..."
}

View file

@ -3,7 +3,8 @@ import { Feature } from "geojson"
import { Point } from "@turf/turf"
import { GeoLocationPointProperties } from "../../Logic/State/GeoLocationState"
import UploadTraceToOsmUI from "../BigComponents/UploadTraceToOsmUI"
import { SpecialVisualization } from "../SpecialVisualization"
import { SpecialVisualization, SpecialVisualizationState } from "../SpecialVisualization"
import { UIEventSource } from "../../Logic/UIEventSource"
/**
* Wrapper around 'UploadTraceToOsmUI'
@ -14,15 +15,20 @@ export class UploadToOsmViz implements SpecialVisualization {
"Uploads the GPS-history as GPX to OpenStreetMap.org; clears the history afterwards. The actual feature is ignored."
args = []
constr(state, featureTags, args) {
constr(
state: SpecialVisualizationState,
featureTags: UIEventSource<Record<string, string>>,
args: string[]
) {
function getTrace(title: string) {
title = title?.trim()
if (title === undefined || title === "") {
title = "Uploaded with MapComplete"
}
title = Utils.EncodeXmlValue(title)
const userLocations: Feature<Point, GeoLocationPointProperties>[] =
state.historicalUserLocations.features.data.map((f) => f.feature)
const userLocations = <Feature<Point, GeoLocationPointProperties>[]>(
state.historicalUserLocations.features.data
)
const trackPoints: string[] = []
for (const l of userLocations) {
let trkpt = ` <trkpt lat="${l.geometry.coordinates[1]}" lon="${l.geometry.coordinates[0]}">`