forked from MapComplete/MapComplete
Refactoring: port import flow
This commit is contained in:
parent
8ed4da4e9d
commit
ace7caada1
48 changed files with 852 additions and 574 deletions
|
@ -78,6 +78,9 @@ export default class Table extends BaseUIElement {
|
|||
for (let j = 0; j < row.length; j++) {
|
||||
try {
|
||||
let elem = row[j]
|
||||
if(elem?.ConstructElement === undefined){
|
||||
continue
|
||||
}
|
||||
const htmlElem = elem?.ConstructElement()
|
||||
if (htmlElem === undefined) {
|
||||
continue
|
||||
|
|
|
@ -330,6 +330,7 @@ class LineRenderingLayer {
|
|||
})
|
||||
if (this._onClick) {
|
||||
map.on("click", polylayer, (e) => {
|
||||
console.log("Got polylayer click:", e)
|
||||
// polygon-layer-listener
|
||||
if(e.originalEvent["consumed"]){
|
||||
// This is a polygon beneath a marker, we can ignore it
|
||||
|
@ -469,8 +470,10 @@ export default class ShowDataLayer {
|
|||
if (this._options.zoomToFeatures) {
|
||||
const features = this._options.features.features.data
|
||||
const bbox = BBox.bboxAroundAll(features.map(BBox.get))
|
||||
map.resize()
|
||||
map.fitBounds(bbox.toLngLat(), {
|
||||
padding: {top: 10, bottom: 10, left: 10, right: 10},
|
||||
animate: false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,26 +1,26 @@
|
|||
import BaseUIElement from "../BaseUIElement"
|
||||
import { Stores, UIEventSource } from "../../Logic/UIEventSource"
|
||||
import { SubtleButton } from "../Base/SubtleButton"
|
||||
import {Stores, UIEventSource} from "../../Logic/UIEventSource"
|
||||
import {SubtleButton} from "../Base/SubtleButton"
|
||||
import Img from "../Base/Img"
|
||||
import { FixedUiElement } from "../Base/FixedUiElement"
|
||||
import {FixedUiElement} from "../Base/FixedUiElement"
|
||||
import Combine from "../Base/Combine"
|
||||
import Link from "../Base/Link"
|
||||
import { Utils } from "../../Utils"
|
||||
import {Utils} from "../../Utils"
|
||||
import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource"
|
||||
import { VariableUiElement } from "../Base/VariableUIElement"
|
||||
import {VariableUiElement} from "../Base/VariableUIElement"
|
||||
import Loading from "../Base/Loading"
|
||||
import { OsmConnection } from "../../Logic/Osm/OsmConnection"
|
||||
import {OsmConnection} from "../../Logic/Osm/OsmConnection"
|
||||
import Translations from "../i18n/Translations"
|
||||
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"
|
||||
import { Changes } from "../../Logic/Osm/Changes"
|
||||
import { UIElement } from "../UIElement"
|
||||
import {Changes} from "../../Logic/Osm/Changes"
|
||||
import {UIElement} from "../UIElement"
|
||||
import FilteredLayer from "../../Models/FilteredLayer"
|
||||
import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig"
|
||||
import Lazy from "../Base/Lazy"
|
||||
import List from "../Base/List"
|
||||
import { SpecialVisualization, SpecialVisualizationState } from "../SpecialVisualization"
|
||||
import { IndexedFeatureSource } from "../../Logic/FeatureSource/FeatureSource"
|
||||
import { MapLibreAdaptor } from "../Map/MapLibreAdaptor"
|
||||
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"
|
||||
|
@ -54,6 +54,7 @@ class ApplyButton extends UIElement {
|
|||
>("idle")
|
||||
private readonly layer: FilteredLayer
|
||||
private readonly tagRenderingConfig: TagRenderingConfig
|
||||
private readonly appliedNumberOfFeatures = new UIEventSource<number>(0)
|
||||
|
||||
constructor(
|
||||
state: SpecialVisualizationState,
|
||||
|
@ -110,7 +111,7 @@ class ApplyButton extends UIElement {
|
|||
mla.allowZooming.setData(false)
|
||||
mla.allowMoving.setData(false)
|
||||
|
||||
const previewMap = new SvelteUIElement(MaplibreMap, { map: mlmap }).SetClass("h-48")
|
||||
const previewMap = new SvelteUIElement(MaplibreMap, {map: mlmap}).SetClass("h-48")
|
||||
|
||||
const features = this.target_feature_ids.map((id) =>
|
||||
this.state.indexedFeatures.featuresById.data.get(id)
|
||||
|
@ -131,7 +132,9 @@ class ApplyButton extends UIElement {
|
|||
return new FixedUiElement("All done!").SetClass("thanks")
|
||||
}
|
||||
if (st === "running") {
|
||||
return new Loading("Applying changes...")
|
||||
return new Loading(new VariableUiElement(this.appliedNumberOfFeatures.map(appliedTo => {
|
||||
return "Applying changes, currently at " + appliedTo + "/" + this.target_feature_ids.length
|
||||
})))
|
||||
}
|
||||
const error = st.error
|
||||
return new Combine([
|
||||
|
@ -142,11 +145,16 @@ class ApplyButton extends UIElement {
|
|||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually applies all the changes...
|
||||
*/
|
||||
private async Run() {
|
||||
try {
|
||||
console.log("Applying auto-action on " + this.target_feature_ids.length + " features")
|
||||
|
||||
for (const targetFeatureId of this.target_feature_ids) {
|
||||
for (let i = 0; i < this.target_feature_ids.length; i++) {
|
||||
const targetFeatureId = this.target_feature_ids[i];
|
||||
const feature = this.state.indexedFeatures.featuresById.data.get(targetFeatureId)
|
||||
const featureTags = this.state.featureProperties.getStore(targetFeatureId)
|
||||
const rendering = this.tagRenderingConfig.GetRenderValue(featureTags.data).txt
|
||||
const specialRenderings = Utils.NoNull(
|
||||
|
@ -156,8 +164,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
|
||||
)
|
||||
}
|
||||
|
@ -167,15 +175,19 @@ class ApplyButton extends UIElement {
|
|||
continue
|
||||
}
|
||||
const action = <AutoAction>specialRendering.func
|
||||
await action.applyActionOn(this.state, featureTags, specialRendering.args)
|
||||
await action.applyActionOn(feature, this.state, featureTags, specialRendering.args)
|
||||
}
|
||||
if( i % 50 === 0){
|
||||
await this.state.changes.flushChanges("Auto button: intermediate save")
|
||||
}
|
||||
this.appliedNumberOfFeatures.setData(i + 1)
|
||||
}
|
||||
console.log("Flushing changes...")
|
||||
await this.state.changes.flushChanges("Auto button")
|
||||
await this.state.changes.flushChanges("Auto button: done")
|
||||
this.buttonState.setData("done")
|
||||
} catch (e) {
|
||||
console.error("Error while running autoApply: ", e)
|
||||
this.buttonState.setData({ error: e })
|
||||
this.buttonState.setData({error: e})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -230,7 +242,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",
|
||||
|
@ -250,7 +262,7 @@ export default class AutoApplyButton implements SpecialVisualization {
|
|||
!(
|
||||
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
|
||||
|
@ -274,21 +286,27 @@ export default class AutoApplyButton implements SpecialVisualization {
|
|||
}
|
||||
|
||||
return new Lazy(() => {
|
||||
const to_parse = new UIEventSource(undefined)
|
||||
const to_parse = new UIEventSource<string[]>(undefined)
|
||||
// Very ugly hack: read the value every 500ms
|
||||
Stores.Chronic(500, () => to_parse.data === undefined).addCallback(() => {
|
||||
const applicable = tagSource.data[argument[1]]
|
||||
to_parse.setData(applicable)
|
||||
let applicable = <string | string[]> tagSource.data[argument[1]]
|
||||
if(typeof applicable === "string"){
|
||||
applicable = JSON.parse(applicable)
|
||||
}
|
||||
to_parse.setData(<string[]> applicable)
|
||||
})
|
||||
|
||||
const loading = new Loading("Gathering which elements support auto-apply... ")
|
||||
return new VariableUiElement(
|
||||
to_parse.map((ids) => {
|
||||
Stores.ListStabilized(to_parse).map((ids) => {
|
||||
if (ids === undefined) {
|
||||
return loading
|
||||
}
|
||||
|
||||
return new ApplyButton(state, JSON.parse(ids), options)
|
||||
if (typeof ids === "string") {
|
||||
ids = JSON.parse(ids)
|
||||
}
|
||||
return new ApplyButton(state, ids, options)
|
||||
})
|
||||
)
|
||||
})
|
||||
|
|
|
@ -1,162 +0,0 @@
|
|||
import BaseUIElement from "../BaseUIElement"
|
||||
import {Store, UIEventSource} from "../../Logic/UIEventSource"
|
||||
import Translations from "../i18n/Translations"
|
||||
import {FixedUiElement} from "../Base/FixedUiElement"
|
||||
import OsmChangeAction from "../../Logic/Osm/Actions/OsmChangeAction"
|
||||
import {And} from "../../Logic/Tags/And"
|
||||
import {Tag} from "../../Logic/Tags/Tag"
|
||||
import {SpecialVisualization, SpecialVisualizationState} from "../SpecialVisualization"
|
||||
import {Feature} from "geojson"
|
||||
import {ImportFlowArguments, ImportFlowUtils} from "./ImportButtons/ImportFlow";
|
||||
import {MergePointConfig} from "../../Logic/Osm/Actions/CreateWayWithPointReuseAction";
|
||||
import {GeoOperations} from "../../Logic/GeoOperations";
|
||||
import ReplaceGeometryAction from "../../Logic/Osm/Actions/ReplaceGeometryAction";
|
||||
import {TagUtils} from "../../Logic/Tags/TagUtils";
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* A helper class for the various import-flows.
|
||||
* An import-flow always starts with a 'Import this'-button. Upon click, a custom confirmation panel is provided
|
||||
*/
|
||||
export abstract class AbstractImportButton implements SpecialVisualization {
|
||||
|
||||
public readonly funcName: string
|
||||
public readonly docs: string
|
||||
public readonly args: { name: string; defaultValue?: string; doc: string }[]
|
||||
private readonly showRemovedTags: boolean
|
||||
private readonly cannotBeImportedMessage: BaseUIElement | undefined
|
||||
|
||||
constructor(
|
||||
funcName: string,
|
||||
docsIntro: string,
|
||||
extraArgs: { name: string; doc: string; defaultValue?: string; required?: boolean }[],
|
||||
options?: { showRemovedTags?: true | boolean; cannotBeImportedMessage?: BaseUIElement }
|
||||
) {
|
||||
this.funcName = funcName
|
||||
this.showRemovedTags = options?.showRemovedTags ?? true
|
||||
this.cannotBeImportedMessage = options?.cannotBeImportedMessage
|
||||
this.docs = `${docsIntro}${ImportFlowUtils.documentationGeneral}`
|
||||
|
||||
this.args = [
|
||||
{
|
||||
name: "targetLayer",
|
||||
doc: "The id of the layer where this point should end up. This is not very strict, it will simply result in checking that this layer is shown preventing possible duplicate elements",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: "tags",
|
||||
doc: "The tags to add onto the new object - see specification above. If this is a key (a single word occuring in the properties of the object), the corresponding value is taken and expanded instead",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: "text",
|
||||
doc: "The text to show on the button",
|
||||
defaultValue: "Import this data into OpenStreetMap",
|
||||
},
|
||||
{
|
||||
name: "icon",
|
||||
doc: "A nice icon to show in the button",
|
||||
defaultValue: "./assets/svg/addSmall.svg",
|
||||
},
|
||||
...extraArgs,
|
||||
]
|
||||
}
|
||||
|
||||
abstract constructElement(
|
||||
state: SpecialVisualizationState,
|
||||
args: ImportFlowArguments,
|
||||
newTags: Store<Tag[]>,
|
||||
tagSource: UIEventSource<Record<string, string>>,
|
||||
feature: Feature,
|
||||
onCancelClicked: () => void
|
||||
): BaseUIElement
|
||||
|
||||
constr(
|
||||
state: SpecialVisualizationState,
|
||||
tagSource: UIEventSource<Record<string, string>>,
|
||||
argsRaw: string[]
|
||||
) {
|
||||
return new FixedUiElement("Deprecated")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class ConflateButton extends AbstractImportButton {
|
||||
needsNodeDatabase = true
|
||||
|
||||
constructor() {
|
||||
super(
|
||||
"conflate_button",
|
||||
"This button will modify the geometry of an existing OSM way to match the specified geometry. This can conflate OSM-ways with LineStrings and Polygons (only simple polygons with one single ring). An attempt is made to move points with special values to a decent new location (e.g. entrances)",
|
||||
[
|
||||
{
|
||||
name: "way_to_conflate",
|
||||
doc: "The key, of which the corresponding value is the id of the OSM-way that must be conflated; typically a calculatedTag",
|
||||
},
|
||||
],
|
||||
{
|
||||
cannotBeImportedMessage: Translations.t.general.add.import.wrongTypeToConflate,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
constructElement(
|
||||
state: SpecialVisualizationState,
|
||||
args: {
|
||||
max_snap_distance: string
|
||||
snap_onto_layers: string
|
||||
icon: string
|
||||
text: string
|
||||
tags: string
|
||||
newTags: UIEventSource<Tag[]>
|
||||
targetLayer: string
|
||||
},
|
||||
tagSource: UIEventSource<any>,
|
||||
feature: Feature,
|
||||
onCancelClicked: () => void
|
||||
): BaseUIElement {
|
||||
const nodesMustMatch = args.snap_onto_layers
|
||||
?.split(";")
|
||||
?.map((tag, i) => TagUtils.Tag(tag, "TagsSpec for import button " + i))
|
||||
|
||||
const mergeConfigs = []
|
||||
if (nodesMustMatch !== undefined && nodesMustMatch.length > 0) {
|
||||
const mergeConfig: MergePointConfig = {
|
||||
mode: args["point_move_mode"] == "move_osm" ? "move_osm_point" : "reuse_osm_point",
|
||||
ifMatches: new And(nodesMustMatch),
|
||||
withinRangeOfM: Number(args.max_snap_distance),
|
||||
}
|
||||
mergeConfigs.push(mergeConfig)
|
||||
}
|
||||
|
||||
const key = args["way_to_conflate"]
|
||||
const wayToConflate = tagSource.data[key]
|
||||
feature = GeoOperations.removeOvernoding(feature)
|
||||
const action: OsmChangeAction & { getPreview(): Promise<any> } = new ReplaceGeometryAction(
|
||||
state,
|
||||
feature,
|
||||
wayToConflate,
|
||||
{
|
||||
theme: state.layout.id,
|
||||
newTags: args.newTags.data,
|
||||
}
|
||||
)
|
||||
|
||||
return this.createConfirmPanelForWay(
|
||||
state,
|
||||
args,
|
||||
feature,
|
||||
tagSource,
|
||||
action,
|
||||
onCancelClicked
|
||||
)
|
||||
}
|
||||
|
||||
protected canBeImported(feature: Feature) {
|
||||
return (
|
||||
feature.geometry.type === "LineString" ||
|
||||
(feature.geometry.type === "Polygon" && feature.geometry.coordinates.length === 1)
|
||||
)
|
||||
}
|
||||
}
|
89
UI/Popup/ImportButtons/ConflateImportButtonViz.ts
Normal file
89
UI/Popup/ImportButtons/ConflateImportButtonViz.ts
Normal file
|
@ -0,0 +1,89 @@
|
|||
import {SpecialVisualization, SpecialVisualizationState} from "../../SpecialVisualization";
|
||||
import {UIEventSource} from "../../../Logic/UIEventSource";
|
||||
import {Feature, Geometry, LineString, Polygon} from "geojson";
|
||||
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig";
|
||||
import BaseUIElement from "../../BaseUIElement";
|
||||
import {ImportFlowArguments, ImportFlowUtils} from "./ImportFlow";
|
||||
import Translations from "../../i18n/Translations";
|
||||
import {Utils} from "../../../Utils";
|
||||
import SvelteUIElement from "../../Base/SvelteUIElement";
|
||||
import WayImportFlow from "./WayImportFlow.svelte";
|
||||
import ConflateImportFlowState from "./ConflateImportFlowState";
|
||||
import {AutoAction} from "../AutoApplyButton";
|
||||
import {IndexedFeatureSource} from "../../../Logic/FeatureSource/FeatureSource";
|
||||
import {Changes} from "../../../Logic/Osm/Changes";
|
||||
import LayoutConfig from "../../../Models/ThemeConfig/LayoutConfig";
|
||||
import {OsmConnection} from "../../../Logic/Osm/OsmConnection";
|
||||
|
||||
export interface ConflateFlowArguments extends ImportFlowArguments {
|
||||
way_to_conflate: string
|
||||
point_move_mode?: "move_osm" | undefined;
|
||||
max_snap_distance?: string
|
||||
snap_onto_layers?: string,
|
||||
}
|
||||
|
||||
export default class ConflateImportButtonViz implements SpecialVisualization, AutoAction {
|
||||
supportsAutoAction: boolean = true;
|
||||
public readonly funcName: string = "conflate_button";
|
||||
public readonly args: { name: string; defaultValue?: string; doc: string; required?: boolean }[] = [
|
||||
...ImportFlowUtils.generalArguments,
|
||||
{
|
||||
name: "way_to_conflate",
|
||||
doc: "The key, of which the corresponding value is the id of the OSM-way that must be conflated; typically a calculatedTag",
|
||||
},
|
||||
|
||||
|
||||
];
|
||||
readonly docs: string = "This button will modify the geometry of an existing OSM way to match the specified geometry. This can conflate OSM-ways with LineStrings and Polygons (only simple polygons with one single ring). An attempt is made to move points with special values to a decent new location (e.g. entrances)" + ImportFlowUtils.documentationGeneral
|
||||
public readonly needsNodeDatabase = true
|
||||
|
||||
|
||||
async applyActionOn(feature: Feature<Geometry, { [name: string]: any; }>, state: {
|
||||
osmConnection: OsmConnection,
|
||||
layout: LayoutConfig;
|
||||
changes: Changes;
|
||||
indexedFeatures: IndexedFeatureSource;
|
||||
}, tagSource: UIEventSource<any>, argument: string[]): Promise<void> {
|
||||
|
||||
{
|
||||
// Small safety check to prevent duplicate imports
|
||||
const id = tagSource.data.id
|
||||
if (ImportFlowUtils.importedIds.has(id)) {
|
||||
return
|
||||
}
|
||||
ImportFlowUtils.importedIds.add(id)
|
||||
}
|
||||
|
||||
if (feature.geometry.type !== "LineString" && feature.geometry.type !== "Polygon") {
|
||||
return
|
||||
}
|
||||
|
||||
const args: ConflateFlowArguments = <any>Utils.ParseVisArgs(this.args, argument)
|
||||
const tagsToApply = ImportFlowUtils.getTagsToApply(tagSource, args)
|
||||
const idOfWayToReplaceGeometry = tagSource.data[args.way_to_conflate]
|
||||
const action = ConflateImportFlowState.createAction(<Feature<LineString | Polygon>>feature, args, state, idOfWayToReplaceGeometry, tagsToApply)
|
||||
tagSource.data["_imported"] = "yes"
|
||||
tagSource.ping()
|
||||
await state.changes.applyAction(action)
|
||||
}
|
||||
|
||||
constr(state: SpecialVisualizationState, tagSource: UIEventSource<Record<string, string>>, argument: string[], feature: Feature, layer: LayerConfig): BaseUIElement {
|
||||
|
||||
const canBeImported = feature.geometry.type === "LineString" ||
|
||||
(feature.geometry.type === "Polygon" && feature.geometry.coordinates.length === 1)
|
||||
if (!canBeImported) {
|
||||
return Translations.t.general.add.import.wrongTypeToConflate.SetClass("alert")
|
||||
}
|
||||
const args: ConflateFlowArguments = <any>Utils.ParseVisArgs(this.args, argument)
|
||||
const tagsToApply = ImportFlowUtils.getTagsToApply(tagSource, args)
|
||||
const idOfWayToReplaceGeometry = tagSource.data[args.way_to_conflate]
|
||||
const importFlow = new ConflateImportFlowState(state, <Feature<LineString | Polygon>>feature, args, tagsToApply, tagSource, idOfWayToReplaceGeometry)
|
||||
return new SvelteUIElement(WayImportFlow, {
|
||||
importFlow
|
||||
})
|
||||
}
|
||||
|
||||
getLayerDependencies(args: string[]) {
|
||||
return ImportFlowUtils.getLayerDependenciesWithSnapOnto(this.args, args)
|
||||
}
|
||||
}
|
83
UI/Popup/ImportButtons/ConflateImportFlowState.ts
Normal file
83
UI/Popup/ImportButtons/ConflateImportFlowState.ts
Normal file
|
@ -0,0 +1,83 @@
|
|||
import ImportFlow from "./ImportFlow";
|
||||
import {ConflateFlowArguments} from "./ConflateImportButtonViz";
|
||||
import {SpecialVisualizationState} from "../../SpecialVisualization";
|
||||
import {Feature, LineString, Polygon} from "geojson";
|
||||
import {Store, UIEventSource} from "../../../Logic/UIEventSource";
|
||||
import {Tag} from "../../../Logic/Tags/Tag";
|
||||
import OsmChangeAction from "../../../Logic/Osm/Actions/OsmChangeAction";
|
||||
import ReplaceGeometryAction from "../../../Logic/Osm/Actions/ReplaceGeometryAction";
|
||||
import {GeoOperations} from "../../../Logic/GeoOperations";
|
||||
import {TagUtils} from "../../../Logic/Tags/TagUtils";
|
||||
import {MergePointConfig} from "../../../Logic/Osm/Actions/CreateWayWithPointReuseAction";
|
||||
import {And} from "../../../Logic/Tags/And";
|
||||
import LayoutConfig from "../../../Models/ThemeConfig/LayoutConfig";
|
||||
import {Changes} from "../../../Logic/Osm/Changes";
|
||||
import {FeatureSource, IndexedFeatureSource} from "../../../Logic/FeatureSource/FeatureSource";
|
||||
import FullNodeDatabaseSource from "../../../Logic/FeatureSource/TiledFeatureSource/FullNodeDatabaseSource";
|
||||
import {OsmConnection} from "../../../Logic/Osm/OsmConnection";
|
||||
|
||||
export default class ConflateImportFlowState extends ImportFlow<ConflateFlowArguments> {
|
||||
|
||||
public readonly originalFeature: Feature
|
||||
private readonly action: OsmChangeAction & { getPreview?(): Promise<FeatureSource>; newElementId?: string };
|
||||
constructor(state: SpecialVisualizationState, originalFeature: Feature<LineString | Polygon>, args: ConflateFlowArguments, tagsToApply: Store<Tag[]>, originalFeatureTags: UIEventSource<Record<string, string>>, idOfFeatureToReplaceGeometry: string) {
|
||||
super(state, args, tagsToApply, originalFeatureTags)
|
||||
this.originalFeature = originalFeature
|
||||
this.action = ConflateImportFlowState.createAction(originalFeature, args, state, idOfFeatureToReplaceGeometry, tagsToApply)
|
||||
|
||||
}
|
||||
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
public GetPreview(): Promise<FeatureSource>{
|
||||
return this.action.getPreview()
|
||||
}
|
||||
|
||||
public async onConfirm() {
|
||||
const originalFeatureTags = this._originalFeatureTags
|
||||
originalFeatureTags.data["_imported"] = "yes"
|
||||
originalFeatureTags.ping() // will set isImported as per its definition
|
||||
const action = this.action
|
||||
await this.state.changes.applyAction(action)
|
||||
const newId = action.newElementId ?? action.mainObjectId
|
||||
this.state.selectedLayer.setData(this.targetLayer.layerDef)
|
||||
this.state.selectedElement.setData(this.state.indexedFeatures.featuresById.data.get(newId))
|
||||
}
|
||||
|
||||
public static createAction(feature: Feature<LineString | Polygon>,
|
||||
args: ConflateFlowArguments,
|
||||
state: {
|
||||
osmConnection: OsmConnection,
|
||||
layout: LayoutConfig;
|
||||
changes: Changes;
|
||||
indexedFeatures: IndexedFeatureSource,
|
||||
fullNodeDatabase?: FullNodeDatabaseSource
|
||||
},
|
||||
idOfFeatureToReplaceGeometry,
|
||||
tagsToApply: Store<Tag[]>
|
||||
): OsmChangeAction & { getPreview?(): Promise<FeatureSource>; newElementId?: string } {
|
||||
const nodesMustMatch = args.snap_onto_layers
|
||||
?.split(";")
|
||||
?.map((tag, i) => TagUtils.Tag(tag, "TagsSpec for import button " + i))
|
||||
|
||||
const mergeConfigs = []
|
||||
if (nodesMustMatch !== undefined && nodesMustMatch.length > 0) {
|
||||
const mergeConfig: MergePointConfig = {
|
||||
mode: args.point_move_mode == "move_osm" ? "move_osm_point" : "reuse_osm_point",
|
||||
ifMatches: new And(nodesMustMatch),
|
||||
withinRangeOfM: Number(args.max_snap_distance ?? 0),
|
||||
}
|
||||
mergeConfigs.push(mergeConfig)
|
||||
}
|
||||
|
||||
|
||||
return new ReplaceGeometryAction(
|
||||
state,
|
||||
GeoOperations.removeOvernoding(feature),
|
||||
idOfFeatureToReplaceGeometry,
|
||||
{
|
||||
theme: state.layout.id,
|
||||
newTags: tagsToApply.data,
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
|
@ -15,6 +15,9 @@
|
|||
import TagHint from "../TagHint.svelte";
|
||||
import {TagsFilter} from "../../../Logic/Tags/TagsFilter";
|
||||
import {Store} from "../../../Logic/UIEventSource";
|
||||
import Svg from "../../../Svg";
|
||||
import ToSvelte from "../../Base/ToSvelte.svelte";
|
||||
import {EyeIcon, EyeOffIcon} from "@rgossiaux/svelte-heroicons/solid";
|
||||
|
||||
export let importFlow: ImportFlow
|
||||
let state = importFlow.state
|
||||
|
@ -24,16 +27,74 @@
|
|||
const isLoading = state.dataIsLoading
|
||||
const dispatch = createEventDispatcher<{ confirm }>()
|
||||
const canBeImported = importFlow.canBeImported()
|
||||
const tags : Store<TagsFilter> = importFlow.tagsToApply.map(tags => new And(tags))
|
||||
const tags: Store<TagsFilter> = importFlow.tagsToApply.map(tags => new And(tags))
|
||||
|
||||
const isDisplayed = importFlow.targetLayer.isDisplayed
|
||||
const hasFilter = importFlow.targetLayer.appliedFilters
|
||||
const hasFilter = importFlow.targetLayer.hasFilter
|
||||
|
||||
function abort() {
|
||||
state.selectedElement.setData(undefined)
|
||||
state.selectedLayer.setData(undefined)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<LoginToggle {state}>
|
||||
|
||||
{#if currentFlowStep === "start"}
|
||||
|
||||
{#if $canBeImported !== true && $canBeImported !== undefined}
|
||||
<Tr cls="alert w-full flex justify-center" t={$canBeImported.error}/>
|
||||
{#if $canBeImported.extraHelp}
|
||||
<Tr t={$canBeImported.extraHelp}/>
|
||||
{/if}
|
||||
{:else if !$isDisplayed}
|
||||
<!-- Check that the layer is enabled, so that we don't add a duplicate -->
|
||||
<div class="alert flex justify-center items-center">
|
||||
<EyeOffIcon class="w-8"/>
|
||||
<Tr t={Translations.t.general.add.layerNotEnabled
|
||||
.Subs({ layer: importFlow.targetLayer.layerDef.name })
|
||||
}/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap-reverse md:flex-nowrap">
|
||||
|
||||
<button class="flex w-full gap-x-1"
|
||||
on:click={() => {abort();state.guistate.openFilterView(importFlow.targetLayer.layerDef)}}>
|
||||
<ToSvelte construct={Svg.layers_svg().SetClass("w-12")}/>
|
||||
<Tr t={Translations.t.general.add.openLayerControl}/>
|
||||
</button>
|
||||
|
||||
<button class="flex w-full gap-x-1 primary" on:click={() => {isDisplayed.setData(true);abort()}}>
|
||||
<EyeIcon class="w-12"/>
|
||||
<Tr t={Translations.t.general.add.enableLayer.Subs({name: importFlow.targetLayer.layerDef.name})}/>
|
||||
</button>
|
||||
</div>
|
||||
{:else if $hasFilter}
|
||||
<!-- Some filters are enabled. The feature to add might already be mapped, but hidden -->
|
||||
<div class="alert flex justify-center items-center">
|
||||
<EyeOffIcon class="w-8"/>
|
||||
<Tr t={Translations.t.general.add.disableFiltersExplanation}/>
|
||||
</div>
|
||||
<div class="flex flex-wrap-reverse md:flex-nowrap">
|
||||
<button class="flex w-full gap-x-1 primary"
|
||||
on:click={() => {abort(); importFlow.targetLayer.disableAllFilters()}}>
|
||||
<EyeOffIcon class="w-12"/>
|
||||
<Tr t={Translations.t.general.add.disableFilters}/>
|
||||
</button>
|
||||
<button class="flex w-full gap-x-1"
|
||||
on:click={() => {abort();state.guistate.openFilterView(importFlow.targetLayer.layerDef)}}>
|
||||
<ToSvelte construct={Svg.layers_svg().SetClass("w-12")}/>
|
||||
<Tr t={Translations.t.general.add.openLayerControl}/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{:else if $isLoading}
|
||||
<Loading>
|
||||
<Tr t={Translations.t.general.add.stillLoading}/>
|
||||
</Loading>
|
||||
|
||||
{:else if currentFlowStep === "start"}
|
||||
<NextButton clss="primary w-full" on:click={() => currentFlowStep = "confirm"}>
|
||||
<slot name="start-flow-text">
|
||||
{#if importFlow?.args?.icon}
|
||||
|
@ -42,15 +103,6 @@
|
|||
{importFlow.args.text}
|
||||
</slot>
|
||||
</NextButton>
|
||||
{:else if $canBeImported !== true && $canBeImported !== undefined}
|
||||
<Tr cls="alert w-full flex justify-center" t={$canBeImported.error}/>
|
||||
{#if $canBeImported.extraHelp}
|
||||
<Tr t={$canBeImported.extraHelp}/>
|
||||
{/if}
|
||||
{:else if $isLoading}
|
||||
<Loading>
|
||||
<Tr t={Translations.t.general.add.stillLoading}/>
|
||||
</Loading>
|
||||
{:else if currentFlowStep === "confirm"}
|
||||
<div class="h-full w-full flex flex-col">
|
||||
<div class="w-full h-full">
|
||||
|
|
|
@ -149,11 +149,13 @@ export default abstract class ImportFlow<ArgT extends ImportFlowArguments> {
|
|||
public readonly args: ArgT;
|
||||
public readonly targetLayer: FilteredLayer;
|
||||
public readonly tagsToApply: Store<Tag[]>
|
||||
protected readonly _originalFeatureTags: UIEventSource<Record<string, string>>;
|
||||
|
||||
constructor(state: SpecialVisualizationState, args: ArgT, tagsToApply: Store<Tag[]>) {
|
||||
constructor(state: SpecialVisualizationState, args: ArgT, tagsToApply: Store<Tag[]>, originalTags: UIEventSource<Record<string, string>>) {
|
||||
this.state = state;
|
||||
this.args = args;
|
||||
this.tagsToApply = tagsToApply;
|
||||
this._originalFeatureTags = originalTags;
|
||||
this.targetLayer = state.layerState.filteredLayers.get(args.targetLayer)
|
||||
|
||||
|
||||
|
@ -166,6 +168,11 @@ export default abstract class ImportFlow<ArgT extends ImportFlowArguments> {
|
|||
const state = this.state
|
||||
return state.featureSwitchIsTesting.map(isTesting => {
|
||||
const t = Translations.t.general.add.import
|
||||
|
||||
if(this._originalFeatureTags.data["_imported"] === "yes"){
|
||||
return {error: t.hasBeenImported}
|
||||
}
|
||||
|
||||
const usesTestUrl = this.state.osmConnection._oauth_config.url === OsmConnection.oauth_configs["osm-test"].url
|
||||
if (!state.layout.official && !(isTesting || usesTestUrl)) {
|
||||
// Unofficial theme - imports not allowed
|
||||
|
@ -191,7 +198,7 @@ export default abstract class ImportFlow<ArgT extends ImportFlowArguments> {
|
|||
}
|
||||
|
||||
return undefined
|
||||
}, [state.mapProperties.zoom, state.dataIsLoading])
|
||||
}, [state.mapProperties.zoom, state.dataIsLoading, this._originalFeatureTags])
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ import Translations from "../../i18n/Translations";
|
|||
/**
|
||||
* The wrapper to make the special visualisation for the PointImportFlow
|
||||
*/
|
||||
export class ImportPointButtonViz implements SpecialVisualization {
|
||||
export class PointImportButtonViz implements SpecialVisualization {
|
||||
|
||||
public readonly funcName: string
|
||||
public readonly docs: string | BaseUIElement
|
||||
|
@ -51,7 +51,7 @@ export class ImportPointButtonViz implements SpecialVisualization {
|
|||
}
|
||||
const baseArgs: PointImportFlowArguments = <any> Utils.ParseVisArgs(this.args, argument)
|
||||
const tagsToApply = ImportFlowUtils.getTagsToApply(tagSource , baseArgs)
|
||||
const importFlow = new PointImportFlowState(state, <Feature<Point>> feature, baseArgs, tagsToApply)
|
||||
const importFlow = new PointImportFlowState(state, <Feature<Point>> feature, baseArgs, tagsToApply, tagSource)
|
||||
|
||||
return new SvelteUIElement(
|
||||
PointImportFlow, {
|
||||
|
@ -60,7 +60,5 @@ export class ImportPointButtonViz implements SpecialVisualization {
|
|||
)
|
||||
}
|
||||
|
||||
getLayerDependencies(argsRaw: string[]): string[] {
|
||||
return ImportFlowUtils.getLayerDependenciesWithSnapOnto(this.args, argsRaw)
|
||||
}
|
||||
|
||||
}
|
|
@ -20,12 +20,10 @@ export interface PointImportFlowArguments extends ImportFlowArguments {
|
|||
export class PointImportFlowState extends ImportFlow<PointImportFlowArguments> {
|
||||
public readonly startCoordinate: [number, number]
|
||||
private readonly _originalFeature: Feature<Point>;
|
||||
private readonly _originalFeatureTags: UIEventSource<Record<string, string>>
|
||||
|
||||
constructor(state: SpecialVisualizationState, originalFeature: Feature<Point>, args: PointImportFlowArguments, tagsToApply: Store<Tag[]>) {
|
||||
super(state, args, tagsToApply);
|
||||
constructor(state: SpecialVisualizationState, originalFeature: Feature<Point>, args: PointImportFlowArguments, tagsToApply: Store<Tag[]>, originalFeatureTags: UIEventSource<Record<string, string>>) {
|
||||
super(state, args, tagsToApply, originalFeatureTags);
|
||||
this._originalFeature = originalFeature;
|
||||
this._originalFeatureTags = state.featureProperties.getStore(originalFeature.properties.id)
|
||||
this.startCoordinate = GeoOperations.centerpointCoordinates(originalFeature)
|
||||
}
|
||||
|
||||
|
|
|
@ -22,49 +22,42 @@ import FullNodeDatabaseSource from "../../../Logic/FeatureSource/TiledFeatureSou
|
|||
export default class WayImportButtonViz implements AutoAction, SpecialVisualization {
|
||||
|
||||
|
||||
public readonly funcName: string
|
||||
public readonly docs: string | BaseUIElement
|
||||
public readonly example?: string
|
||||
public readonly args: { name: string; defaultValue?: string; doc: string }[]
|
||||
public readonly funcName: string= "import_way_button"
|
||||
public readonly docs: string = "This button will copy the data from an external dataset into OpenStreetMap, copying the geometry and adding it as a 'line'" + ImportFlowUtils.documentationGeneral
|
||||
public readonly args: { name: string; defaultValue?: string; doc: string }[]= [
|
||||
...ImportFlowUtils.generalArguments,
|
||||
{
|
||||
name: "snap_to_point_if",
|
||||
doc: "Points with the given tags will be snapped to or moved",
|
||||
},
|
||||
{
|
||||
name: "max_snap_distance",
|
||||
doc: "If the imported object is a LineString or (Multi)Polygon, already existing OSM-points will be reused to construct the geometry of the newly imported way",
|
||||
defaultValue: "0.05",
|
||||
},
|
||||
{
|
||||
name: "move_osm_point_if",
|
||||
doc: "Moves the OSM-point to the newly imported point if these conditions are met",
|
||||
},
|
||||
{
|
||||
name: "max_move_distance",
|
||||
doc: "If an OSM-point is moved, the maximum amount of meters it is moved. Capped on 20m",
|
||||
defaultValue: "0.05",
|
||||
},
|
||||
{
|
||||
name: "snap_onto_layers",
|
||||
doc: "If no existing nearby point exists, but a line of a specified layer is closeby, snap to this layer instead",
|
||||
},
|
||||
{
|
||||
name: "snap_to_layer_max_distance",
|
||||
doc: "Distance to distort the geometry to snap to this layer",
|
||||
defaultValue: "0.1",
|
||||
},
|
||||
]
|
||||
|
||||
public readonly supportsAutoAction = true
|
||||
public readonly needsNodeDatabase = true
|
||||
|
||||
constructor() {
|
||||
this.funcName = "import_way_button"
|
||||
this.docs = "This button will copy the data from an external dataset into OpenStreetMap, copying the geometry and adding it as a 'line'" + ImportFlowUtils.documentationGeneral
|
||||
this.args = [
|
||||
...ImportFlowUtils.generalArguments,
|
||||
{
|
||||
name: "snap_to_point_if",
|
||||
doc: "Points with the given tags will be snapped to or moved",
|
||||
},
|
||||
{
|
||||
name: "max_snap_distance",
|
||||
doc: "If the imported object is a LineString or (Multi)Polygon, already existing OSM-points will be reused to construct the geometry of the newly imported way",
|
||||
defaultValue: "0.05",
|
||||
},
|
||||
{
|
||||
name: "move_osm_point_if",
|
||||
doc: "Moves the OSM-point to the newly imported point if these conditions are met",
|
||||
},
|
||||
{
|
||||
name: "max_move_distance",
|
||||
doc: "If an OSM-point is moved, the maximum amount of meters it is moved. Capped on 20m",
|
||||
defaultValue: "0.05",
|
||||
},
|
||||
{
|
||||
name: "snap_onto_layers",
|
||||
doc: "If no existing nearby point exists, but a line of a specified layer is closeby, snap to this layer instead",
|
||||
},
|
||||
{
|
||||
name: "snap_to_layer_max_distance",
|
||||
doc: "Distance to distort the geometry to snap to this layer",
|
||||
defaultValue: "0.1",
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
constr(state: SpecialVisualizationState, tagSource: UIEventSource<Record<string, string>>, argument: string[], feature: Feature, layer: LayerConfig): BaseUIElement {
|
||||
const geometry = feature.geometry
|
||||
if (!(geometry.type == "LineString" || geometry.type === "Polygon")) {
|
||||
|
@ -100,8 +93,10 @@ export default class WayImportButtonViz implements AutoAction, SpecialVisualizat
|
|||
|
||||
const args: WayImportFlowArguments = <any>Utils.ParseVisArgs(this.args, argument)
|
||||
const tagsToApply = ImportFlowUtils.getTagsToApply(tagSource, args)
|
||||
const mergeConfigs = WayImportFlowState.GetMergeConfig(args, tagsToApply)
|
||||
const mergeConfigs = WayImportFlowState.GetMergeConfig(args)
|
||||
const action = WayImportFlowState.CreateAction(<Feature<LineString | Polygon >>feature, args, state, tagsToApply, mergeConfigs)
|
||||
tagSource.data["_imported"] = "yes"
|
||||
tagSource.ping()
|
||||
await state.changes.applyAction(action)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
<script lang="ts">
|
||||
|
||||
/**
|
||||
* Can be used for both WayImportFlow and ConflateImportFlow
|
||||
*/
|
||||
import WayImportFlowState from "./WayImportFlowState";
|
||||
import ImportFlow from "./ImportFlow.svelte";
|
||||
import MapControlButton from "../../Base/MapControlButton.svelte";
|
||||
|
@ -12,7 +14,8 @@
|
|||
import StaticFeatureSource from "../../../Logic/FeatureSource/Sources/StaticFeatureSource";
|
||||
import {ImportFlowUtils} from "./ImportFlow";
|
||||
import {GeoOperations} from "../../../Logic/GeoOperations";
|
||||
export let importFlow: WayImportFlowState
|
||||
import ConflateImportFlowState from "./ConflateImportFlowState";
|
||||
export let importFlow: WayImportFlowState | ConflateImportFlowState
|
||||
|
||||
const state = importFlow.state
|
||||
const map = new UIEventSource<MlMap>(undefined)
|
||||
|
|
|
@ -8,14 +8,14 @@ import CreateWayWithPointReuseAction, {
|
|||
MergePointConfig
|
||||
} from "../../../Logic/Osm/Actions/CreateWayWithPointReuseAction";
|
||||
import {TagUtils} from "../../../Logic/Tags/TagUtils";
|
||||
import {OsmCreateAction} from "../../../Logic/Osm/Actions/OsmChangeAction";
|
||||
import {OsmCreateAction, PreviewableAction} from "../../../Logic/Osm/Actions/OsmChangeAction";
|
||||
import {FeatureSource, IndexedFeatureSource} from "../../../Logic/FeatureSource/FeatureSource";
|
||||
import CreateMultiPolygonWithPointReuseAction from "../../../Logic/Osm/Actions/CreateMultiPolygonWithPointReuseAction";
|
||||
import LayoutConfig from "../../../Models/ThemeConfig/LayoutConfig";
|
||||
import {Changes} from "../../../Logic/Osm/Changes";
|
||||
import FullNodeDatabaseSource from "../../../Logic/FeatureSource/TiledFeatureSource/FullNodeDatabaseSource";
|
||||
|
||||
export interface WayImportFlowArguments extends ImportFlowArguments {
|
||||
export interface WayImportFlowArguments extends ImportFlowArguments {
|
||||
max_snap_distance: string
|
||||
snap_onto_layers: string,
|
||||
snap_to_layer_max_distance: string,
|
||||
|
@ -28,13 +28,11 @@ export default class WayImportFlowState extends ImportFlow<WayImportFlowArgument
|
|||
public readonly originalFeature: Feature<LineString | Polygon>;
|
||||
|
||||
private readonly action: OsmCreateAction & { getPreview?(): Promise<FeatureSource>; }
|
||||
private readonly _originalFeatureTags: UIEventSource<Record<string, string>>;
|
||||
|
||||
constructor(state: SpecialVisualizationState, originalFeature: Feature<LineString | Polygon>, args: WayImportFlowArguments, tagsToApply: Store<Tag[]>, originalFeatureTags: UIEventSource<Record<string, string>>) {
|
||||
super(state, args, tagsToApply);
|
||||
super(state, args, tagsToApply, originalFeatureTags);
|
||||
this.originalFeature = originalFeature;
|
||||
this._originalFeatureTags = originalFeatureTags;
|
||||
const mergeConfigs = WayImportFlowState.GetMergeConfig(args, tagsToApply)
|
||||
const mergeConfigs = WayImportFlowState.GetMergeConfig(args)
|
||||
this.action = WayImportFlowState.CreateAction(originalFeature, args, state, tagsToApply, mergeConfigs)
|
||||
}
|
||||
|
||||
|
@ -49,7 +47,7 @@ export default class WayImportFlowState extends ImportFlow<WayImportFlowArgument
|
|||
},
|
||||
tagsToApply: Store<Tag[]>,
|
||||
mergeConfigs: MergePointConfig[]
|
||||
): OsmCreateAction & { getPreview?(): Promise<FeatureSource>; newElementId?: string } {
|
||||
): OsmCreateAction & PreviewableAction & { newElementId?: string } {
|
||||
if (feature.geometry.type === "Polygon" && feature.geometry.coordinates.length > 1) {
|
||||
const coors = (<Polygon>feature.geometry).coordinates
|
||||
const outer = coors[0]
|
||||
|
@ -76,7 +74,7 @@ export default class WayImportFlowState extends ImportFlow<WayImportFlowArgument
|
|||
}
|
||||
}
|
||||
|
||||
public static GetMergeConfig(args: WayImportFlowArguments, newTags: Store<Tag[]>): MergePointConfig[] {
|
||||
public static GetMergeConfig(args: WayImportFlowArguments): MergePointConfig[] {
|
||||
const nodesMustMatch = args.snap_to_point_if
|
||||
?.split(";")
|
||||
?.map((tag, i) => TagUtils.Tag(tag, "TagsSpec for import button " + i))
|
||||
|
@ -108,6 +106,7 @@ export default class WayImportFlowState extends ImportFlow<WayImportFlowArgument
|
|||
return mergeConfigs
|
||||
}
|
||||
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
public async onConfirm() {
|
||||
const originalFeatureTags = this._originalFeatureTags
|
||||
originalFeatureTags.data["_imported"] = "yes"
|
||||
|
|
|
@ -14,6 +14,9 @@ import { Tag } from "../../Logic/Tags/Tag"
|
|||
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"
|
||||
import { Changes } from "../../Logic/Osm/Changes"
|
||||
import { SpecialVisualization, SpecialVisualizationState } from "../SpecialVisualization"
|
||||
import {IndexedFeatureSource} from "../../Logic/FeatureSource/FeatureSource";
|
||||
import {Feature} from "geojson";
|
||||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig";
|
||||
|
||||
export default class TagApplyButton implements AutoAction, SpecialVisualization {
|
||||
public readonly funcName = "tag_apply"
|
||||
|
@ -111,13 +114,16 @@ export default class TagApplyButton implements AutoAction, SpecialVisualization
|
|||
})
|
||||
}
|
||||
|
||||
async applyActionOn(
|
||||
public async applyActionOn(
|
||||
feature: Feature,
|
||||
state: {
|
||||
layout: LayoutConfig
|
||||
changes: Changes
|
||||
indexedFeatures: IndexedFeatureSource
|
||||
},
|
||||
tags: UIEventSource<any>,
|
||||
args: string[]
|
||||
args: string[],
|
||||
|
||||
): Promise<void> {
|
||||
const tagsToApply = TagApplyButton.generateTagsToApply(args[0], tags)
|
||||
const targetIdKey = args[3]
|
||||
|
@ -138,7 +144,9 @@ export default class TagApplyButton implements AutoAction, SpecialVisualization
|
|||
public constr(
|
||||
state: SpecialVisualizationState,
|
||||
tags: UIEventSource<Record<string, string>>,
|
||||
args: string[]
|
||||
args: string[],
|
||||
feature: Feature,
|
||||
layer: LayerConfig
|
||||
): BaseUIElement {
|
||||
const tagsToApply = TagApplyButton.generateTagsToApply(args[0], tags)
|
||||
const msg = args[1]
|
||||
|
@ -167,7 +175,7 @@ export default class TagApplyButton implements AutoAction, SpecialVisualization
|
|||
new Combine([msg, tagsExplanation]).SetClass("flex flex-col")
|
||||
).onClick(async () => {
|
||||
applied.setData(true)
|
||||
await self.applyActionOn(state, tags, args)
|
||||
await self.applyActionOn(feature, state, tags, args)
|
||||
})
|
||||
|
||||
return new Toggle(
|
||||
|
|
|
@ -71,8 +71,9 @@ import SplitRoadWizard from "./Popup/SplitRoadWizard"
|
|||
import {ExportAsGpxViz} from "./Popup/ExportAsGpxViz"
|
||||
import WikipediaPanel from "./Wikipedia/WikipediaPanel.svelte"
|
||||
import TagRenderingEditable from "./Popup/TagRendering/TagRenderingEditable.svelte";
|
||||
import {ImportPointButtonViz} from "./Popup/ImportButtons/ImportPointButtonViz";
|
||||
import {PointImportButtonViz} from "./Popup/ImportButtons/PointImportButtonViz";
|
||||
import WayImportButtonViz from "./Popup/ImportButtons/WayImportButtonViz";
|
||||
import ConflateImportButtonViz from "./Popup/ImportButtons/ConflateImportButtonViz";
|
||||
|
||||
class NearbyImageVis implements SpecialVisualization {
|
||||
// Class must be in SpecialVisualisations due to weird cyclical import that breaks the tests
|
||||
|
@ -619,9 +620,9 @@ export default class SpecialVisualizations {
|
|||
|
||||
new TagApplyButton(),
|
||||
|
||||
new ImportPointButtonViz(),
|
||||
new PointImportButtonViz(),
|
||||
new WayImportButtonViz(),
|
||||
// TODO new ConflateButton(),
|
||||
new ConflateImportButtonViz(),
|
||||
|
||||
new NearbyImageVis(),
|
||||
|
||||
|
|
|
@ -1,22 +1,38 @@
|
|||
import { FixedUiElement } from "../Base/FixedUiElement"
|
||||
import { Translation, TypedTranslation } from "./Translation"
|
||||
import {FixedUiElement} from "../Base/FixedUiElement"
|
||||
import {Translation, TypedTranslation} from "./Translation"
|
||||
import BaseUIElement from "../BaseUIElement"
|
||||
import CompiledTranslations from "../../assets/generated/CompiledTranslations"
|
||||
import LanguageUtils from "../../Utils/LanguageUtils"
|
||||
import {ClickableToggle} from "../Input/Toggle";
|
||||
|
||||
export default class Translations {
|
||||
static readonly t: Readonly<typeof CompiledTranslations.t> = CompiledTranslations.t
|
||||
private static knownLanguages = LanguageUtils.usedLanguages
|
||||
|
||||
constructor() {
|
||||
throw "Translations is static. If you want to intitialize a new translation, use the singular form"
|
||||
}
|
||||
|
||||
public static W(s: string | number | BaseUIElement): BaseUIElement {
|
||||
public static W(s: string | number | boolean | BaseUIElement): BaseUIElement {
|
||||
if (typeof s === "string") {
|
||||
return new FixedUiElement(s)
|
||||
}
|
||||
if (typeof s === "number") {
|
||||
return new FixedUiElement("" + s)
|
||||
return new FixedUiElement("" + s).SetClass("font-bold")
|
||||
}
|
||||
if (typeof s === "boolean") {
|
||||
return new FixedUiElement("" + s).SetClass("font-bold")
|
||||
}
|
||||
if (typeof s === "object") {
|
||||
if (s.ConstructElement) {
|
||||
return s
|
||||
}
|
||||
const v = JSON.stringify(s)
|
||||
if (v.length > 100) {
|
||||
const shortened = v.substring(0, 100) + "..."
|
||||
return new ClickableToggle(v, shortened).ToggleOnClick().SetClass("literal-code button")
|
||||
}
|
||||
return new FixedUiElement(v).SetClass("literal-code")
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
@ -57,7 +73,7 @@ export default class Translations {
|
|||
t = "" + t
|
||||
}
|
||||
if (typeof t === "string") {
|
||||
return new TypedTranslation<object>({ "*": t }, context)
|
||||
return new TypedTranslation<object>({"*": t}, context)
|
||||
}
|
||||
if (t["render"] !== undefined) {
|
||||
const msg =
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue