forked from MapComplete/MapComplete
refactoring
This commit is contained in:
parent
b94a8f5745
commit
5d0fe31c41
114 changed files with 2412 additions and 2958 deletions
|
@ -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"] ?? "") !== "")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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}",
|
||||
})
|
||||
|
|
|
@ -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,
|
||||
})*/
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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",
|
||||
}
|
||||
)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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([
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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",
|
||||
}
|
||||
)
|
||||
|
|
|
@ -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",
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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(
|
||||
|
|
0
UI/Popup/TagRenderingAnswer.svelte
Normal file
0
UI/Popup/TagRenderingAnswer.svelte
Normal 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..."
|
||||
}
|
||||
|
|
|
@ -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]}">`
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue