Refactoring: fix 'delete' and 'move'-buttons as special elements
This commit is contained in:
parent
466dd16568
commit
8a1f0599d9
19 changed files with 317 additions and 296 deletions
|
@ -38,9 +38,9 @@ export class MenuState {
|
|||
[],
|
||||
(str) => MenuState._menuviewTabs.indexOf(<any>str)
|
||||
)
|
||||
this.themeIsOpened.addCallbackAndRun((isOpen) => {
|
||||
this.menuIsOpened.addCallbackAndRun((isOpen) => {
|
||||
if (!isOpen) {
|
||||
this.highlightedLayerInFilters.setData(undefined)
|
||||
this.highlightedUserSetting.setData(undefined)
|
||||
}
|
||||
})
|
||||
this.themeViewTab.addCallbackAndRun((tab) => {
|
||||
|
|
|
@ -234,9 +234,9 @@ class ExpandTagRendering extends Conversion<
|
|||
|
||||
if (typeof layer.source !== "string") {
|
||||
if (found.condition === undefined) {
|
||||
found.condition = layer.source.osmTags
|
||||
found.condition = layer.source["osmTags"]
|
||||
} else {
|
||||
found.condition = { and: [found.condition, layer.source.osmTags] }
|
||||
found.condition = { and: [found.condition, layer.source["osmTags"]] }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -436,7 +436,7 @@ class DetectInline extends DesugaringStep<QuestionableTagRenderingConfigJson> {
|
|||
if (typeof json.render === "string") {
|
||||
spec = { "*": json.render }
|
||||
} else {
|
||||
spec = json.render
|
||||
spec = <Record<string, string>>json.render
|
||||
}
|
||||
const errors: string[] = []
|
||||
for (const key in spec) {
|
||||
|
@ -480,7 +480,10 @@ export class AddQuestionBox extends DesugaringStep<LayerConfigJson> {
|
|||
json: LayerConfigJson,
|
||||
context: string
|
||||
): { result: LayerConfigJson; errors?: string[]; warnings?: string[]; information?: string[] } {
|
||||
if (json.tagRenderings === undefined) {
|
||||
if (
|
||||
json.tagRenderings === undefined ||
|
||||
json.tagRenderings.some((tr) => tr["id"] === "leftover-questions")
|
||||
) {
|
||||
return { result: json }
|
||||
}
|
||||
json = JSON.parse(JSON.stringify(json))
|
||||
|
@ -500,7 +503,6 @@ export class AddQuestionBox extends DesugaringStep<LayerConfigJson> {
|
|||
const errors: string[] = []
|
||||
const warnings: string[] = []
|
||||
if (noLabels.length > 1) {
|
||||
console.log(json.tagRenderings)
|
||||
errors.push(
|
||||
"At " +
|
||||
context +
|
||||
|
@ -572,6 +574,45 @@ export class AddQuestionBox extends DesugaringStep<LayerConfigJson> {
|
|||
}
|
||||
}
|
||||
|
||||
export class AddEditingElements extends DesugaringStep<LayerConfigJson> {
|
||||
constructor() {
|
||||
super(
|
||||
"Add some editing elements, such as the delete button or the move button if they are configured. These used to be handled by the feature info box, but this has been replaced by special visualisation elements",
|
||||
[],
|
||||
"AddEditingElements"
|
||||
)
|
||||
}
|
||||
|
||||
convert(
|
||||
json: LayerConfigJson,
|
||||
context: string
|
||||
): { result: LayerConfigJson; errors?: string[]; warnings?: string[]; information?: string[] } {
|
||||
json = JSON.parse(JSON.stringify(json))
|
||||
|
||||
if (json.allowSplit && !ValidationUtils.hasSpecialVisualisation(json, "split_button")) {
|
||||
json.tagRenderings.push({
|
||||
id: "split-button",
|
||||
render: { "*": "{split_button()}" },
|
||||
})
|
||||
}
|
||||
|
||||
if (json.allowMove && !ValidationUtils.hasSpecialVisualisation(json, "move_button")) {
|
||||
json.tagRenderings.push({
|
||||
id: "move-button",
|
||||
render: { "*": "{move_button()}" },
|
||||
})
|
||||
}
|
||||
if (json.deletion && !ValidationUtils.hasSpecialVisualisation(json, "delete_button")) {
|
||||
json.tagRenderings.push({
|
||||
id: "delete-button",
|
||||
render: { "*": "{delete_button()}" },
|
||||
})
|
||||
}
|
||||
|
||||
return { result: json }
|
||||
}
|
||||
}
|
||||
|
||||
export class ExpandRewrite<T> extends Conversion<T | RewritableConfigJson<T>, T[]> {
|
||||
constructor() {
|
||||
super("Applies a rewrite", [], "ExpandRewrite")
|
||||
|
@ -1064,6 +1105,36 @@ class PreparePointRendering extends Fuse<PointRenderingConfigJson | LineRenderin
|
|||
}
|
||||
}
|
||||
|
||||
export class AddMiniMap extends DesugaringStep<LayerConfigJson> {
|
||||
private readonly _state: DesugaringContext
|
||||
|
||||
constructor(state: DesugaringContext) {
|
||||
super(
|
||||
"Adds a default 'minimap'-element to the tagrenderings if none of the elements define such a minimap",
|
||||
["tagRenderings"],
|
||||
"AddMiniMap"
|
||||
)
|
||||
this._state = state
|
||||
}
|
||||
|
||||
convert(layerConfig: LayerConfigJson, context: string): { result: LayerConfigJson } {
|
||||
if (!layerConfig.tagRenderings) {
|
||||
return { result: layerConfig }
|
||||
}
|
||||
const state = this._state
|
||||
const hasMinimap = ValidationUtils.hasSpecialVisualisation(layerConfig, "minimap")
|
||||
if (!hasMinimap) {
|
||||
layerConfig = { ...layerConfig }
|
||||
layerConfig.tagRenderings = [...layerConfig.tagRenderings]
|
||||
layerConfig.tagRenderings.push(state.tagRenderings.get("minimap"))
|
||||
}
|
||||
|
||||
return {
|
||||
result: layerConfig,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class PrepareLayer extends Fuse<LayerConfigJson> {
|
||||
constructor(state: DesugaringContext) {
|
||||
super(
|
||||
|
@ -1072,6 +1143,9 @@ export class PrepareLayer extends Fuse<LayerConfigJson> {
|
|||
new On("tagRenderings", new Concat(new ExpandRewrite()).andThenF(Utils.Flatten)),
|
||||
new On("tagRenderings", (layer) => new Concat(new ExpandTagRendering(state, layer))),
|
||||
new On("tagRenderings", new Each(new DetectInline())),
|
||||
new AddQuestionBox(),
|
||||
new AddMiniMap(state),
|
||||
new AddEditingElements(),
|
||||
new On("mapRendering", new Concat(new ExpandRewrite()).andThenF(Utils.Flatten)),
|
||||
new On<(PointRenderingConfigJson | LineRenderingConfigJson)[], LayerConfigJson>(
|
||||
"mapRendering",
|
||||
|
|
|
@ -10,7 +10,7 @@ import {
|
|||
SetDefault,
|
||||
} from "./Conversion"
|
||||
import { LayoutConfigJson } from "../Json/LayoutConfigJson"
|
||||
import { AddQuestionBox, PrepareLayer } from "./PrepareLayer"
|
||||
import { PrepareLayer } from "./PrepareLayer"
|
||||
import { LayerConfigJson } from "../Json/LayerConfigJson"
|
||||
import { Utils } from "../../../Utils"
|
||||
import Constants from "../../Constants"
|
||||
|
@ -295,56 +295,6 @@ class AddImportLayers extends DesugaringStep<LayoutConfigJson> {
|
|||
}
|
||||
}
|
||||
|
||||
export class AddMiniMap extends DesugaringStep<LayerConfigJson> {
|
||||
private readonly _state: DesugaringContext
|
||||
|
||||
constructor(state: DesugaringContext) {
|
||||
super(
|
||||
"Adds a default 'minimap'-element to the tagrenderings if none of the elements define such a minimap",
|
||||
["tagRenderings"],
|
||||
"AddMiniMap"
|
||||
)
|
||||
this._state = state
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this tag rendering has a minimap in some language.
|
||||
* Note: this minimap can be hidden by conditions
|
||||
*
|
||||
* AddMiniMap.hasMinimap({render: "{minimap()}"}) // => true
|
||||
* AddMiniMap.hasMinimap({render: {en: "{minimap()}"}}) // => true
|
||||
* AddMiniMap.hasMinimap({render: {en: "{minimap()}", nl: "{minimap()}"}}) // => true
|
||||
* AddMiniMap.hasMinimap({render: {en: "{minimap()}", nl: "No map for the dutch!"}}) // => true
|
||||
* AddMiniMap.hasMinimap({render: "{minimap()}"}) // => true
|
||||
* AddMiniMap.hasMinimap({render: "{minimap(18,featurelist)}"}) // => true
|
||||
* AddMiniMap.hasMinimap({mappings: [{if: "xyz=abc",then: "{minimap(18,featurelist)}"}]}) // => true
|
||||
* AddMiniMap.hasMinimap({render: "Some random value {key}"}) // => false
|
||||
* AddMiniMap.hasMinimap({render: "Some random value {minimap}"}) // => false
|
||||
*/
|
||||
static hasMinimap(renderingConfig: TagRenderingConfigJson): boolean {
|
||||
return ValidationUtils.getSpecialVisualisations(renderingConfig).some(
|
||||
(vis) => vis.funcName === "minimap"
|
||||
)
|
||||
}
|
||||
|
||||
convert(layerConfig: LayerConfigJson, context: string): { result: LayerConfigJson } {
|
||||
const state = this._state
|
||||
const hasMinimap =
|
||||
layerConfig.tagRenderings?.some((tr) =>
|
||||
AddMiniMap.hasMinimap(<TagRenderingConfigJson>tr)
|
||||
) ?? true
|
||||
if (!hasMinimap) {
|
||||
layerConfig = { ...layerConfig }
|
||||
layerConfig.tagRenderings = [...layerConfig.tagRenderings]
|
||||
layerConfig.tagRenderings.push(state.tagRenderings.get("minimap"))
|
||||
}
|
||||
|
||||
return {
|
||||
result: layerConfig,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AddContextToTranslationsInLayout extends DesugaringStep<LayoutConfigJson> {
|
||||
constructor() {
|
||||
super(
|
||||
|
@ -660,9 +610,7 @@ export class PrepareTheme extends Fuse<LayoutConfigJson> {
|
|||
? new Pass("AddDefaultLayers is disabled due to the set flag")
|
||||
: new AddDefaultLayers(state),
|
||||
new AddDependencyLayersToTheme(state),
|
||||
new AddImportLayers(),
|
||||
new On("layers", new Each(new AddQuestionBox())),
|
||||
new On("layers", new Each(new AddMiniMap(state)))
|
||||
new AddImportLayers()
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -2,8 +2,22 @@ import { TagRenderingConfigJson } from "../Json/TagRenderingConfigJson"
|
|||
import { Utils } from "../../../Utils"
|
||||
import SpecialVisualizations from "../../../UI/SpecialVisualizations"
|
||||
import { RenderingSpecification, SpecialVisualization } from "../../../UI/SpecialVisualization"
|
||||
import { LayerConfigJson } from "../Json/LayerConfigJson"
|
||||
|
||||
export default class ValidationUtils {
|
||||
public static hasSpecialVisualisation(
|
||||
layer: LayerConfigJson,
|
||||
specialVisualisation: string
|
||||
): boolean {
|
||||
return (
|
||||
layer.tagRenderings?.some((tagRendering) =>
|
||||
ValidationUtils.getSpecialVisualisations(<TagRenderingConfigJson>tagRendering).some(
|
||||
(vis) => vis.funcName === specialVisualisation
|
||||
)
|
||||
) ?? false
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives all the (function names of) used special visualisations
|
||||
* @param renderingConfig
|
||||
|
@ -15,6 +29,7 @@ export default class ValidationUtils {
|
|||
(spec) => spec["func"]
|
||||
)
|
||||
}
|
||||
|
||||
public static getSpecialVisualsationsWithArgs(
|
||||
renderingConfig: TagRenderingConfigJson
|
||||
): RenderingSpecification[] {
|
||||
|
|
|
@ -90,7 +90,7 @@ export default class LayerConfig extends WithContextLoader {
|
|||
|
||||
if (json.source === "special" || json.source === "special:library") {
|
||||
this.source = null
|
||||
} else if (json.source.osmTags === undefined) {
|
||||
} else if (json.source["osmTags"] === undefined) {
|
||||
throw (
|
||||
"Layer " +
|
||||
this.id +
|
||||
|
@ -122,8 +122,8 @@ export default class LayerConfig extends WithContextLoader {
|
|||
}
|
||||
this.syncSelection = json.syncSelection ?? "no"
|
||||
if (typeof json.source !== "string") {
|
||||
this.maxAgeOfCache = json.source.maxCacheAge ?? 24 * 60 * 60 * 30
|
||||
const osmTags = TagUtils.Tag(json.source.osmTags, context + "source.osmTags")
|
||||
this.maxAgeOfCache = json.source["maxCacheAge"] ?? 24 * 60 * 60 * 30
|
||||
const osmTags = TagUtils.Tag(json.source["osmTags"], context + "source.osmTags")
|
||||
if (osmTags.isNegative()) {
|
||||
throw (
|
||||
context +
|
||||
|
|
|
@ -248,7 +248,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
|||
new UIEventSource<Record<string, string>>(last_click.properties)
|
||||
)
|
||||
new ShowDataLayer(this.map, {
|
||||
features: last_click,
|
||||
features: new FilteringFeatureSource(last_click_layer, last_click),
|
||||
doShowLayer: new ImmutableStore(true),
|
||||
layer: last_click_layer.layerDef,
|
||||
selectedElement: this.selectedElement,
|
||||
|
|
|
@ -10,56 +10,58 @@
|
|||
import Hotkeys from "../Base/Hotkeys";
|
||||
import { Geocoding } from "../../Logic/Osm/Geocoding";
|
||||
import { BBox } from "../../Logic/BBox";
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization";
|
||||
import { GeoIndexedStoreForLayer } from "../../Logic/FeatureSource/Actors/GeoIndexedStore";
|
||||
|
||||
export let state: SpecialVisualizationState
|
||||
export let bounds: UIEventSource<BBox>
|
||||
export let selectedElement: UIEventSource<Feature>;
|
||||
export let selectedLayer: UIEventSource<LayerConfig>;
|
||||
export let perLayer: ReadonlyMap<string, GeoIndexedStoreForLayer> | undefined = undefined;
|
||||
export let bounds: UIEventSource<BBox>;
|
||||
export let selectedElement: UIEventSource<Feature> | undefined = undefined;
|
||||
export let selectedLayer: UIEventSource<LayerConfig> | undefined = undefined;
|
||||
|
||||
let searchContents: string = undefined;
|
||||
|
||||
let isRunning: boolean = false;
|
||||
|
||||
let inputElement: HTMLInputElement;
|
||||
|
||||
let feedback: string = undefined
|
||||
|
||||
let feedback: string = undefined;
|
||||
|
||||
Hotkeys.RegisterHotkey(
|
||||
{ ctrl: "F" },
|
||||
Translations.t.hotkeyDocumentation.selectSearch,
|
||||
() => {
|
||||
inputElement?.focus()
|
||||
inputElement?.select()
|
||||
inputElement?.focus();
|
||||
inputElement?.select();
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
async function performSearch() {
|
||||
try {
|
||||
isRunning = true;
|
||||
searchContents = searchContents?.trim() ?? ""
|
||||
searchContents = searchContents?.trim() ?? "";
|
||||
if (searchContents === "") {
|
||||
return
|
||||
return;
|
||||
}
|
||||
const result = await Geocoding.Search(searchContents, bounds.data)
|
||||
const result = await Geocoding.Search(searchContents, bounds.data);
|
||||
if (result.length == 0) {
|
||||
feedback = Translations.t.search.nothing.txt
|
||||
return
|
||||
feedback = Translations.t.search.nothing.txt;
|
||||
return;
|
||||
}
|
||||
const poi = result[0]
|
||||
const [lat0, lat1, lon0, lon1] = poi.boundingbox
|
||||
bounds.set(new BBox([[lon0, lat0], [lon1, lat1]]).pad(0.01))
|
||||
const id = poi.osm_type + "/" + poi.osm_id
|
||||
const perLayer = state.perLayer
|
||||
const layers = Array.from(perLayer.values())
|
||||
for (const layer of layers) {
|
||||
const found = layer.features.data.find(f => f.properties.id === id)
|
||||
selectedElement.setData(found)
|
||||
selectedLayer.setData(layer.layer.layerDef)
|
||||
|
||||
const poi = result[0];
|
||||
const [lat0, lat1, lon0, lon1] = poi.boundingbox;
|
||||
bounds.set(new BBox([[lon0, lat0], [lon1, lat1]]).pad(0.01));
|
||||
if (perLayer !== undefined) {
|
||||
const id = poi.osm_type + "/" + poi.osm_id;
|
||||
const layers = Array.from(perLayer?.values() ?? []);
|
||||
for (const layer of layers) {
|
||||
const found = layer.features.data.find(f => f.properties.id === id);
|
||||
selectedElement?.setData(found);
|
||||
selectedLayer?.setData(layer.layer.layerDef);
|
||||
|
||||
}
|
||||
}
|
||||
}catch (e) {
|
||||
console.error(e)
|
||||
feedback = Translations.t.search.error.txt
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
feedback = Translations.t.search.error.txt;
|
||||
} finally {
|
||||
isRunning = false;
|
||||
}
|
||||
|
@ -72,7 +74,7 @@
|
|||
|
||||
{#if isRunning}
|
||||
<Loading>{Translations.t.general.search.searching}</Loading>
|
||||
{:else if feedback !== undefined}
|
||||
{:else if feedback !== undefined}
|
||||
<div class="alert" on:click={() => feedback = undefined}>
|
||||
{feedback}
|
||||
</div>
|
||||
|
|
|
@ -177,9 +177,7 @@ export class ImageUploadFlow extends Toggle {
|
|||
)
|
||||
.onClick(() => {
|
||||
console.log("Opening the license settings... ")
|
||||
ScrollableFullScreen.collapse()
|
||||
DefaultGuiState.state.userInfoIsOpened.setData(true)
|
||||
DefaultGuiState.state.userInfoFocusedQuestion.setData("picture-license")
|
||||
state.guistate.openUsersettings("picture-license")
|
||||
})
|
||||
.SetClass("underline"),
|
||||
]).SetStyle("font-size:small;"),
|
||||
|
|
|
@ -4,8 +4,6 @@
|
|||
import { Map as MlMap } from "maplibre-gl";
|
||||
import { MapLibreAdaptor } from "../../Map/MapLibreAdaptor";
|
||||
import MaplibreMap from "../../Map/MaplibreMap.svelte";
|
||||
import Svg from "../../../Svg";
|
||||
import ToSvelte from "../../Base/ToSvelte.svelte";
|
||||
import DragInvitation from "../../Base/DragInvitation.svelte";
|
||||
|
||||
/**
|
||||
|
@ -14,13 +12,13 @@
|
|||
export let value: UIEventSource<{lon: number, lat: number}>;
|
||||
export let mapProperties: Partial<MapProperties> & { readonly location: UIEventSource<{ lon: number; lat: number }> } = undefined;
|
||||
/**
|
||||
* Called when setup is done, cna be used to add layrs to the map
|
||||
* Called when setup is done, can be used to add more layers to the map
|
||||
*/
|
||||
export let onCreated : (value: Store<{lon: number, lat: number}> , map: Store<MlMap>, mapProperties: MapProperties ) => void
|
||||
|
||||
export let map: UIEventSource<MlMap> = new UIEventSource<MlMap>(undefined);
|
||||
let mla = new MapLibreAdaptor(map, mapProperties);
|
||||
|
||||
mapProperties.location.syncWith(value)
|
||||
if(onCreated){
|
||||
onCreated(value, map, mla)
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import PointRenderingConfig from "../../Models/ThemeConfig/PointRenderingConfig"
|
|||
import { OsmTags } from "../../Models/OsmFeature"
|
||||
import { FeatureSource } from "../../Logic/FeatureSource/FeatureSource"
|
||||
import { BBox } from "../../Logic/BBox"
|
||||
import { Feature } from "geojson"
|
||||
import { Feature, Point } from "geojson"
|
||||
import ScrollableFullScreen from "../Base/ScrollableFullScreen"
|
||||
import LineRenderingConfig from "../../Models/ThemeConfig/LineRenderingConfig"
|
||||
import { Utils } from "../../Utils"
|
||||
|
@ -26,6 +26,7 @@ class PointRenderingLayer {
|
|||
private readonly _onClick: (feature: Feature) => void
|
||||
private readonly _allMarkers: Map<string, Marker> = new Map<string, Marker>()
|
||||
private _dirty = false
|
||||
|
||||
constructor(
|
||||
map: MlMap,
|
||||
features: FeatureSource,
|
||||
|
@ -139,6 +140,18 @@ class PointRenderingLayer {
|
|||
store
|
||||
.map((tags) => this._config.rotationAlignment.GetRenderValue(tags).Subs(tags).txt)
|
||||
.addCallbackAndRun((pitchAligment) => marker.setRotationAlignment(pitchAligment))
|
||||
if (feature.geometry.type === "Point") {
|
||||
// When the tags get 'pinged', check that the location didn't change
|
||||
store.addCallbackAndRunD(() => {
|
||||
// Check if the location is still the same
|
||||
const oldLoc = marker.getLngLat()
|
||||
const newloc = (<Point>feature.geometry).coordinates
|
||||
if (newloc[0] === oldLoc.lng && newloc[1] === oldLoc.lat) {
|
||||
return
|
||||
}
|
||||
marker.setLngLat({ lon: newloc[0], lat: newloc[1] })
|
||||
})
|
||||
}
|
||||
return marker
|
||||
}
|
||||
}
|
||||
|
@ -159,6 +172,7 @@ class LineRenderingLayer {
|
|||
|
||||
private static readonly lineConfigKeysColor = ["color", "fillColor"] as const
|
||||
private static readonly lineConfigKeysNumber = ["width", "offset"] as const
|
||||
private static missingIdTriggered = false
|
||||
private readonly _map: MlMap
|
||||
private readonly _config: LineRenderingConfig
|
||||
private readonly _visibility?: Store<boolean>
|
||||
|
@ -167,7 +181,6 @@ class LineRenderingLayer {
|
|||
private readonly _layername: string
|
||||
private readonly _listenerInstalledOn: Set<string> = new Set<string>()
|
||||
|
||||
private static missingIdTriggered = false
|
||||
constructor(
|
||||
map: MlMap,
|
||||
features: FeatureSource,
|
||||
|
@ -248,6 +261,11 @@ class LineRenderingLayer {
|
|||
},
|
||||
})
|
||||
|
||||
map.on("click", linelayer, (e) => {
|
||||
console.log("Click", e)
|
||||
e.originalEvent["consumed"] = true
|
||||
this._onClick(e.features[0])
|
||||
})
|
||||
const polylayer = this._layername + "_polygon"
|
||||
map.addLayer({
|
||||
source: this._layername,
|
||||
|
@ -260,6 +278,10 @@ class LineRenderingLayer {
|
|||
"fill-opacity": 0.1,
|
||||
},
|
||||
})
|
||||
map.on("click", polylayer, (e) => {
|
||||
e.originalEvent["consumed"] = true
|
||||
this._onClick(e.features[0])
|
||||
})
|
||||
|
||||
this._visibility?.addCallbackAndRunD((visible) => {
|
||||
try {
|
||||
|
|
|
@ -20,10 +20,10 @@ import { RadioButton } from "../Input/RadioButton"
|
|||
import { FixedInputElement } from "../Input/FixedInputElement"
|
||||
import Title from "../Base/Title"
|
||||
import { SubstitutedTranslation } from "../SubstitutedTranslation"
|
||||
import FeaturePipelineState from "../../Logic/State/FeaturePipelineState"
|
||||
import TagRenderingQuestion from "./TagRenderingQuestion"
|
||||
import { OsmId } from "../../Models/OsmFeature"
|
||||
import { OsmId, OsmTags } from "../../Models/OsmFeature"
|
||||
import { LoginToggle } from "./LoginButton"
|
||||
import { SpecialVisualizationState } from "../SpecialVisualization"
|
||||
|
||||
export default class DeleteWizard extends Toggle {
|
||||
/**
|
||||
|
@ -41,13 +41,18 @@ export default class DeleteWizard extends Toggle {
|
|||
* Ideal for the case of "THIS PATH IS ON MY GROUND AND SHOULD BE DELETED IMMEDIATELY OR I WILL GET MY LAWYER" but to mark it as private instead.
|
||||
* (Note that _delete_reason is used as trigger to do actual deletion - setting such a tag WILL delete from the database with that as changeset comment)
|
||||
*
|
||||
* @param id: The id of the element to remove
|
||||
* @param state: the state of the application
|
||||
* @param options softDeletionTags: the tags to apply if the user doesn't have permission to delete, e.g. 'disused:amenity=public_bookcase', 'amenity='. After applying, the element should not be picked up on the map anymore. If undefined, the wizard will only show up if the point can be (hard) deleted
|
||||
*/
|
||||
constructor(id: OsmId, state: FeaturePipelineState, options: DeleteConfig) {
|
||||
const deleteAbility = new DeleteabilityChecker(id, state, options.neededChangesets)
|
||||
const tagsSource = state.allElements.getEventSourceById(id)
|
||||
constructor(
|
||||
id: OsmId,
|
||||
tagsSource: UIEventSource<OsmTags>,
|
||||
state: SpecialVisualizationState,
|
||||
options: DeleteConfig
|
||||
) {
|
||||
const deleteAbility = new DeleteabilityChecker(
|
||||
id,
|
||||
state.osmConnection,
|
||||
options.neededChangesets
|
||||
)
|
||||
|
||||
const isDeleted = new UIEventSource(false)
|
||||
const allowSoftDeletion = !!options.softDeletionTags
|
||||
|
@ -62,7 +67,7 @@ export default class DeleteWizard extends Toggle {
|
|||
if (selected["retagTo"] !== undefined) {
|
||||
// no _delete_reason is given, which implies that this is _not_ a deletion but merely a retagging via a nonDeleteMapping
|
||||
actionToTake = new ChangeTagAction(id, selected["retagTo"], tagsSource.data, {
|
||||
theme: state?.layoutToUse?.id ?? "unkown",
|
||||
theme: state?.layout?.id ?? "unkown",
|
||||
changeType: "special-delete",
|
||||
})
|
||||
} else {
|
||||
|
@ -70,7 +75,7 @@ export default class DeleteWizard extends Toggle {
|
|||
id,
|
||||
options.softDeletionTags,
|
||||
{
|
||||
theme: state?.layoutToUse?.id ?? "unkown",
|
||||
theme: state?.layout?.id ?? "unkown",
|
||||
specialMotivation: selected["deleteReason"],
|
||||
},
|
||||
deleteAbility.canBeDeleted.data.canBeDeleted
|
||||
|
@ -250,7 +255,7 @@ export default class DeleteWizard extends Toggle {
|
|||
private static constructMultipleChoice(
|
||||
config: DeleteConfig,
|
||||
tagsSource: UIEventSource<Record<string, string>>,
|
||||
state: FeaturePipelineState
|
||||
state: SpecialVisualizationState
|
||||
): InputElement<{ deleteReason: string } | { retagTo: TagsFilter }> {
|
||||
const elements: InputElement<{ deleteReason: string } | { retagTo: TagsFilter }>[] = []
|
||||
|
||||
|
@ -282,19 +287,13 @@ export default class DeleteWizard extends Toggle {
|
|||
|
||||
class DeleteabilityChecker {
|
||||
public readonly canBeDeleted: UIEventSource<{ canBeDeleted?: boolean; reason: Translation }>
|
||||
private readonly _id: string
|
||||
private readonly _id: OsmId
|
||||
private readonly _allowDeletionAtChangesetCount: number
|
||||
private readonly _state: {
|
||||
osmConnection: OsmConnection
|
||||
}
|
||||
private readonly _osmConnection: OsmConnection
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
state: { osmConnection: OsmConnection },
|
||||
allowDeletionAtChangesetCount?: number
|
||||
) {
|
||||
constructor(id: OsmId, osmConnection: OsmConnection, allowDeletionAtChangesetCount?: number) {
|
||||
this._id = id
|
||||
this._state = state
|
||||
this._osmConnection = osmConnection
|
||||
this._allowDeletionAtChangesetCount = allowDeletionAtChangesetCount ?? Number.MAX_VALUE
|
||||
|
||||
this.canBeDeleted = new UIEventSource<{ canBeDeleted?: boolean; reason: Translation }>({
|
||||
|
@ -324,7 +323,7 @@ class DeleteabilityChecker {
|
|||
}
|
||||
|
||||
// Does the currently logged in user have enough experience to delete this point?
|
||||
const deletingPointsOfOtherAllowed = this._state.osmConnection.userDetails.map((ud) => {
|
||||
const deletingPointsOfOtherAllowed = this._osmConnection.userDetails.map((ud) => {
|
||||
if (ud === undefined) {
|
||||
return undefined
|
||||
}
|
||||
|
@ -347,10 +346,10 @@ class DeleteabilityChecker {
|
|||
// Not yet downloaded
|
||||
return null
|
||||
}
|
||||
const userId = self._state.osmConnection.userDetails.data.uid
|
||||
const userId = self._osmConnection.userDetails.data.uid
|
||||
return !previous.some((editor) => editor !== userId)
|
||||
},
|
||||
[self._state.osmConnection.userDetails]
|
||||
[self._osmConnection.userDetails]
|
||||
)
|
||||
|
||||
// User allowed OR only edited by self?
|
||||
|
|
|
@ -17,7 +17,6 @@ import MoveWizard from "./MoveWizard"
|
|||
import Toggle from "../Input/Toggle"
|
||||
import Lazy from "../Base/Lazy"
|
||||
import FeaturePipelineState from "../../Logic/State/FeaturePipelineState"
|
||||
import { Tag } from "../../Logic/Tags/Tag"
|
||||
import Svg from "../../Svg"
|
||||
import Translations from "../i18n/Translations"
|
||||
|
||||
|
@ -32,9 +31,6 @@ export default class FeatureInfoBox extends ScrollableFullScreen {
|
|||
setHash?: true | boolean
|
||||
}
|
||||
) {
|
||||
if (state === undefined) {
|
||||
throw "State is undefined!"
|
||||
}
|
||||
const showAllQuestions = state.featureSwitchShowAllQuestions.map(
|
||||
(fsShow) => fsShow || state.showAllQuestionsAtOnce.data,
|
||||
[state.showAllQuestionsAtOnce]
|
||||
|
@ -98,27 +94,11 @@ export default class FeatureInfoBox extends ScrollableFullScreen {
|
|||
private static GenerateMainContent(
|
||||
tags: UIEventSource<any>,
|
||||
layerConfig: LayerConfig,
|
||||
state: FeaturePipelineState,
|
||||
showAllQuestions?: Store<boolean>
|
||||
state: FeaturePipelineState
|
||||
): BaseUIElement {
|
||||
let questionBoxes: Map<string, QuestionBox> = new Map<string, QuestionBox>()
|
||||
const t = Translations.t.general
|
||||
const allGroupNames = Utils.Dedup(layerConfig.tagRenderings.map((tr) => tr.group))
|
||||
if (state?.featureSwitchUserbadge?.data ?? true) {
|
||||
const questionSpecs = layerConfig.tagRenderings.filter((tr) => tr.id === "questions")
|
||||
for (const groupName of allGroupNames) {
|
||||
const questions = layerConfig.tagRenderings.filter((tr) => tr.group === groupName)
|
||||
const questionSpec = questionSpecs.filter((tr) => tr.group === groupName)[0]
|
||||
const questionBox = new QuestionBox(state, {
|
||||
tagsSource: tags,
|
||||
tagRenderings: questions,
|
||||
units: layerConfig.units,
|
||||
showAllQuestionsAtOnce:
|
||||
questionSpec?.freeform?.helperArgs["showAllQuestions"] ?? showAllQuestions,
|
||||
})
|
||||
questionBoxes.set(groupName, questionBox)
|
||||
}
|
||||
}
|
||||
|
||||
const withQuestion = layerConfig.tagRenderings.filter(
|
||||
(tr) => tr.question !== undefined
|
||||
|
@ -243,40 +223,6 @@ export default class FeatureInfoBox extends ScrollableFullScreen {
|
|||
editElements.push(questionBox)
|
||||
})
|
||||
|
||||
if (layerConfig.allowMove) {
|
||||
editElements.push(
|
||||
new VariableUiElement(
|
||||
tags
|
||||
.map((tags) => tags.id)
|
||||
.map((id) => {
|
||||
const feature = state.allElements.ContainingFeatures.get(id)
|
||||
if (feature === undefined) {
|
||||
return "This feature is not register in the state.allElements and cannot be moved"
|
||||
}
|
||||
return new MoveWizard(feature, state, layerConfig.allowMove)
|
||||
})
|
||||
).SetClass("text-base")
|
||||
)
|
||||
}
|
||||
|
||||
if (layerConfig.deletion) {
|
||||
editElements.push(
|
||||
new VariableUiElement(
|
||||
tags
|
||||
.map((tags) => tags.id)
|
||||
.map((id) => new DeleteWizard(id, state, layerConfig.deletion))
|
||||
).SetClass("text-base")
|
||||
)
|
||||
}
|
||||
|
||||
if (layerConfig.allowSplit) {
|
||||
editElements.push(
|
||||
new VariableUiElement(
|
||||
tags.map((tags) => tags.id).map((id) => new SplitRoadWizard(id, state))
|
||||
).SetClass("text-base")
|
||||
)
|
||||
}
|
||||
|
||||
editElements.push(
|
||||
new VariableUiElement(
|
||||
state.osmConnection.userDetails
|
||||
|
@ -302,30 +248,6 @@ export default class FeatureInfoBox extends ScrollableFullScreen {
|
|||
)
|
||||
)
|
||||
|
||||
editElements.push(
|
||||
Toggle.If(state.featureSwitchIsDebugging, () => {
|
||||
const config_all_tags: TagRenderingConfig = new TagRenderingConfig(
|
||||
{ render: "{all_tags()}" },
|
||||
""
|
||||
)
|
||||
const config_download: TagRenderingConfig = new TagRenderingConfig(
|
||||
{ render: "{export_as_geojson()}" },
|
||||
""
|
||||
)
|
||||
const config_id: TagRenderingConfig = new TagRenderingConfig(
|
||||
{ render: "{open_in_iD()}" },
|
||||
""
|
||||
)
|
||||
|
||||
return new Combine([
|
||||
new TagRenderingAnswer(tags, config_all_tags, state),
|
||||
new TagRenderingAnswer(tags, config_download, state),
|
||||
new TagRenderingAnswer(tags, config_id, state),
|
||||
"This is layer " + layerConfig.id,
|
||||
])
|
||||
})
|
||||
)
|
||||
|
||||
return new Combine(editElements).SetClass("flex flex-col")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,29 +1,28 @@
|
|||
import { SubtleButton } from "../Base/SubtleButton"
|
||||
import Combine from "../Base/Combine"
|
||||
import Svg from "../../Svg"
|
||||
import { OsmConnection } from "../../Logic/Osm/OsmConnection"
|
||||
import Toggle from "../Input/Toggle"
|
||||
import { UIEventSource } from "../../Logic/UIEventSource"
|
||||
import Translations from "../i18n/Translations"
|
||||
import { VariableUiElement } from "../Base/VariableUIElement"
|
||||
import { Translation } from "../i18n/Translation"
|
||||
import BaseUIElement from "../BaseUIElement"
|
||||
import LocationInput from "../Input/LocationInput"
|
||||
import Loc from "../../Models/Loc"
|
||||
import { GeoOperations } from "../../Logic/GeoOperations"
|
||||
import { OsmObject } from "../../Logic/Osm/OsmObject"
|
||||
import { Changes } from "../../Logic/Osm/Changes"
|
||||
import ChangeLocationAction from "../../Logic/Osm/Actions/ChangeLocationAction"
|
||||
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"
|
||||
import MoveConfig from "../../Models/ThemeConfig/MoveConfig"
|
||||
import { ElementStorage } from "../../Logic/ElementStorage"
|
||||
import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers"
|
||||
import BaseLayer from "../../Models/BaseLayer"
|
||||
import SearchAndGo from "../BigComponents/SearchAndGo"
|
||||
import ChangeTagAction from "../../Logic/Osm/Actions/ChangeTagAction"
|
||||
import { And } from "../../Logic/Tags/And"
|
||||
import { Tag } from "../../Logic/Tags/Tag"
|
||||
import { LoginToggle } from "./LoginButton"
|
||||
import { SpecialVisualizationState } from "../SpecialVisualization"
|
||||
import { Feature, Point } from "geojson"
|
||||
import { OsmTags } from "../../Models/OsmFeature"
|
||||
import SvelteUIElement from "../Base/SvelteUIElement"
|
||||
import { MapProperties } from "../../Models/MapProperties"
|
||||
import LocationInput from "../InputElement/Helpers/LocationInput.svelte"
|
||||
import Geosearch from "../BigComponents/Geosearch.svelte"
|
||||
import Constants from "../../Models/Constants"
|
||||
|
||||
interface MoveReason {
|
||||
text: Translation | string
|
||||
|
@ -43,14 +42,9 @@ export default class MoveWizard extends Toggle {
|
|||
* The UI-element which helps moving a point
|
||||
*/
|
||||
constructor(
|
||||
featureToMove: any,
|
||||
state: {
|
||||
osmConnection: OsmConnection
|
||||
featureSwitchUserbadge: UIEventSource<boolean>
|
||||
changes: Changes
|
||||
layoutToUse: LayoutConfig
|
||||
allElements: ElementStorage
|
||||
},
|
||||
featureToMove: Feature<Point>,
|
||||
tags: UIEventSource<OsmTags>,
|
||||
state: SpecialVisualizationState,
|
||||
options: MoveConfig
|
||||
) {
|
||||
const t = Translations.t.move
|
||||
|
@ -130,56 +124,38 @@ export default class MoveWizard extends Toggle {
|
|||
if (reason === undefined) {
|
||||
return undefined
|
||||
}
|
||||
const loc = new UIEventSource<Loc>({
|
||||
lon: lon,
|
||||
lat: lat,
|
||||
zoom: reason?.startZoom ?? 16,
|
||||
})
|
||||
|
||||
let background: string[]
|
||||
if (typeof reason.background == "string") {
|
||||
background = [reason.background]
|
||||
} else {
|
||||
background = reason.background
|
||||
const mapProperties: Partial<MapProperties> = {
|
||||
minzoom: new UIEventSource(reason.minZoom),
|
||||
zoom: new UIEventSource(reason?.startZoom ?? 16),
|
||||
location: new UIEventSource({ lon, lat }),
|
||||
bounds: new UIEventSource(undefined),
|
||||
}
|
||||
|
||||
const preferredBackground = AvailableBaseLayers.SelectBestLayerAccordingTo(
|
||||
loc,
|
||||
new UIEventSource(background)
|
||||
).data
|
||||
|
||||
const locationInput = new LocationInput({
|
||||
minZoom: reason.minZoom,
|
||||
centerLocation: loc,
|
||||
mapBackground: new UIEventSource<BaseLayer>(preferredBackground), // We detach the layer
|
||||
state: <any>state,
|
||||
const value = new UIEventSource<{ lon: number; lat: number }>(undefined)
|
||||
const locationInput = new SvelteUIElement(LocationInput, {
|
||||
mapProperties,
|
||||
value,
|
||||
})
|
||||
|
||||
if (reason.lockBounds) {
|
||||
locationInput.installBounds(0.05, true)
|
||||
}
|
||||
|
||||
let searchPanel: BaseUIElement = undefined
|
||||
if (reason.includeSearch) {
|
||||
searchPanel = new SearchAndGo({
|
||||
leafletMap: locationInput.leafletMap,
|
||||
})
|
||||
searchPanel = new SvelteUIElement(Geosearch, { bounds: mapProperties.bounds })
|
||||
}
|
||||
|
||||
locationInput.SetStyle("height: 17.5rem")
|
||||
|
||||
const confirmMove = new SubtleButton(Svg.move_confirm_svg(), t.confirmMove)
|
||||
confirmMove.onClick(async () => {
|
||||
const loc = locationInput.GetValue().data
|
||||
const loc = value.data
|
||||
await state.changes.applyAction(
|
||||
new ChangeLocationAction(featureToMove.properties.id, [loc.lon, loc.lat], {
|
||||
reason: reason.changesetCommentValue,
|
||||
theme: state.layoutToUse.id,
|
||||
theme: state.layout.id,
|
||||
})
|
||||
)
|
||||
featureToMove.properties._lat = loc.lat
|
||||
featureToMove.properties._lon = loc.lon
|
||||
|
||||
featureToMove.geometry.coordinates = [loc.lon, loc.lat]
|
||||
if (reason.eraseAddressFields) {
|
||||
await state.changes.applyAction(
|
||||
new ChangeTagAction(
|
||||
|
@ -193,13 +169,13 @@ export default class MoveWizard extends Toggle {
|
|||
featureToMove.properties,
|
||||
{
|
||||
changeType: "relocated",
|
||||
theme: state.layoutToUse.id,
|
||||
theme: state.layout.id,
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
state.allElements.getEventSourceById(id).ping()
|
||||
state.featureProperties.getStore(id).ping()
|
||||
currentStep.setData("moved")
|
||||
})
|
||||
const zoomInFurhter = t.zoomInFurther.SetClass("alert block m-6")
|
||||
|
@ -209,7 +185,7 @@ export default class MoveWizard extends Toggle {
|
|||
new Toggle(
|
||||
confirmMove,
|
||||
zoomInFurhter,
|
||||
locationInput.GetValue().map((l) => l.zoom >= 19)
|
||||
mapProperties.zoom.map((zoom) => zoom >= Constants.minZoomLevelToAddNewPoint)
|
||||
),
|
||||
]).SetClass("flex flex-col")
|
||||
})
|
||||
|
|
|
@ -2,33 +2,27 @@ import Toggle from "../Input/Toggle"
|
|||
import Svg from "../../Svg"
|
||||
import { UIEventSource } from "../../Logic/UIEventSource"
|
||||
import { SubtleButton } from "../Base/SubtleButton"
|
||||
import Minimap from "../Base/Minimap"
|
||||
import ShowDataLayer from "../ShowDataLayer/ShowDataLayer"
|
||||
import { GeoOperations } from "../../Logic/GeoOperations"
|
||||
import { LeafletMouseEvent } from "leaflet"
|
||||
import Combine from "../Base/Combine"
|
||||
import { Button } from "../Base/Button"
|
||||
import Translations from "../i18n/Translations"
|
||||
import SplitAction from "../../Logic/Osm/Actions/SplitAction"
|
||||
import Title from "../Base/Title"
|
||||
import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource"
|
||||
import ShowDataMultiLayer from "../ShowDataLayer/ShowDataMultiLayer"
|
||||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
||||
import { BBox } from "../../Logic/BBox"
|
||||
import split_point from "../../assets/layers/split_point/split_point.json"
|
||||
import { OsmConnection } from "../../Logic/Osm/OsmConnection"
|
||||
import { Changes } from "../../Logic/Osm/Changes"
|
||||
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"
|
||||
import { ElementStorage } from "../../Logic/ElementStorage"
|
||||
import BaseLayer from "../../Models/BaseLayer"
|
||||
import FilteredLayer from "../../Models/FilteredLayer"
|
||||
import BaseUIElement from "../BaseUIElement"
|
||||
import { VariableUiElement } from "../Base/VariableUIElement"
|
||||
import ScrollableFullScreen from "../Base/ScrollableFullScreen"
|
||||
import { LoginToggle } from "./LoginButton"
|
||||
import { SpecialVisualizationState } from "../SpecialVisualization"
|
||||
|
||||
export default class SplitRoadWizard extends Combine {
|
||||
// @ts-ignore
|
||||
private static splitLayerStyling = new LayerConfig(
|
||||
split_point,
|
||||
"(BUILTIN) SplitRoadWizard.ts",
|
||||
|
@ -43,22 +37,7 @@ export default class SplitRoadWizard extends Combine {
|
|||
* @param id: The id of the road to remove
|
||||
* @param state: the state of the application
|
||||
*/
|
||||
constructor(
|
||||
id: string,
|
||||
state: {
|
||||
filteredLayers: UIEventSource<FilteredLayer[]>
|
||||
backgroundLayer: UIEventSource<BaseLayer>
|
||||
featureSwitchIsTesting: UIEventSource<boolean>
|
||||
featureSwitchIsDebugging: UIEventSource<boolean>
|
||||
featureSwitchShowAllQuestions: UIEventSource<boolean>
|
||||
osmConnection: OsmConnection
|
||||
featureSwitchUserbadge: UIEventSource<boolean>
|
||||
changes: Changes
|
||||
layoutToUse: LayoutConfig
|
||||
allElements: ElementStorage
|
||||
selectedElement: UIEventSource<any>
|
||||
}
|
||||
) {
|
||||
constructor(id: string, state: SpecialVisualizationState) {
|
||||
const t = Translations.t.split
|
||||
|
||||
// Contains the points on the road that are selected to split on - contains geojson points with extra properties such as 'location' with the distance along the linestring
|
||||
|
|
|
@ -26,18 +26,27 @@
|
|||
}));
|
||||
|
||||
let htmlElem: HTMLElement;
|
||||
const _htmlElement = new UIEventSource<HTMLElement>(undefined);
|
||||
$: _htmlElement.setData(htmlElem);
|
||||
|
||||
function setHighlighting() {
|
||||
if (highlightedRendering === undefined) {
|
||||
return;
|
||||
}
|
||||
if (htmlElem === undefined) {
|
||||
return;
|
||||
}
|
||||
const highlighted = highlightedRendering.data;
|
||||
if (config.id === highlighted) {
|
||||
htmlElem.classList.add("glowing-shadow");
|
||||
} else {
|
||||
htmlElem.classList.remove("glowing-shadow");
|
||||
}
|
||||
}
|
||||
|
||||
if (highlightedRendering) {
|
||||
$: onDestroy(highlightedRendering.addCallbackAndRun(highlighted => {
|
||||
console.log("Highlighted rendering is", highlighted)
|
||||
if(htmlElem === undefined){
|
||||
return
|
||||
}
|
||||
if (config.id === highlighted) {
|
||||
htmlElem.classList.add("glowing-shadow");
|
||||
} else {
|
||||
htmlElem.classList.remove("glowing-shadow");
|
||||
}
|
||||
}));
|
||||
onDestroy(highlightedRendering?.addCallbackAndRun(() => setHighlighting()))
|
||||
onDestroy(_htmlElement.addCallbackAndRun(() => setHighlighting()))
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ import Maproulette from "../Logic/Maproulette"
|
|||
import SvelteUIElement from "./Base/SvelteUIElement"
|
||||
import { BBoxFeatureSourceForLayer } from "../Logic/FeatureSource/Sources/TouchesBboxFeatureSource"
|
||||
import QuestionViz from "./Popup/QuestionViz"
|
||||
import { Feature } from "geojson"
|
||||
import { Feature, Point } from "geojson"
|
||||
import { GeoOperations } from "../Logic/GeoOperations"
|
||||
import CreateNewNote from "./Popup/CreateNewNote.svelte"
|
||||
import AddNewPoint from "./Popup/AddNewPoint/AddNewPoint.svelte"
|
||||
|
@ -76,6 +76,9 @@ import { SaveButton } from "./Popup/SaveButton"
|
|||
import Lazy from "./Base/Lazy"
|
||||
import { CheckBox } from "./Input/Checkboxes"
|
||||
import Slider from "./Input/Slider"
|
||||
import DeleteWizard from "./Popup/DeleteWizard"
|
||||
import { OsmId, OsmTags } from "../Models/OsmFeature"
|
||||
import MoveWizard from "./Popup/MoveWizard"
|
||||
|
||||
class NearbyImageVis implements SpecialVisualization {
|
||||
// Class must be in SpecialVisualisations due to weird cyclical import that breaks the tests
|
||||
|
@ -226,6 +229,7 @@ class StealViz implements SpecialVisualization {
|
|||
required: true,
|
||||
},
|
||||
]
|
||||
|
||||
constr(state: SpecialVisualizationState, featureTags, args) {
|
||||
const [featureIdKey, layerAndtagRenderingIds] = args
|
||||
const tagRenderings: [LayerConfig, TagRenderingConfig][] = []
|
||||
|
@ -273,6 +277,7 @@ class StealViz implements SpecialVisualization {
|
|||
return [layerId]
|
||||
}
|
||||
}
|
||||
|
||||
export default class SpecialVisualizations {
|
||||
public static specialVisualizations: SpecialVisualization[] = SpecialVisualizations.initList()
|
||||
|
||||
|
@ -521,6 +526,74 @@ export default class SpecialVisualizations {
|
|||
new HistogramViz(),
|
||||
new StealViz(),
|
||||
new MinimapViz(),
|
||||
{
|
||||
funcName: "split_button",
|
||||
docs: "Adds a button which allows to split a way",
|
||||
args: [],
|
||||
constr(
|
||||
state: SpecialVisualizationState,
|
||||
tagSource: UIEventSource<Record<string, string>>,
|
||||
argument: string[],
|
||||
feature: Feature,
|
||||
layer: LayerConfig
|
||||
): BaseUIElement {
|
||||
return new VariableUiElement(
|
||||
// TODO
|
||||
tagSource
|
||||
.map((tags) => tags.id)
|
||||
.map((id) => new FixedUiElement("TODO: enable splitting")) // new SplitRoadWizard(id, state))
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
funcName: "move_button",
|
||||
docs: "Adds a button which allows to move the object to another location. The config will be read from the layer config",
|
||||
args: [],
|
||||
constr(
|
||||
state: SpecialVisualizationState,
|
||||
tagSource: UIEventSource<Record<string, string>>,
|
||||
argument: string[],
|
||||
feature: Feature,
|
||||
layer: LayerConfig
|
||||
): BaseUIElement {
|
||||
if (feature.geometry.type !== "Point") {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return new MoveWizard(
|
||||
<Feature<Point>>feature,
|
||||
<UIEventSource<OsmTags>>tagSource,
|
||||
state,
|
||||
layer.allowMove
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
funcName: "delete_button",
|
||||
docs: "Adds a button which allows to delete the object at this location. The config will be read from the layer config",
|
||||
args: [],
|
||||
constr(
|
||||
state: SpecialVisualizationState,
|
||||
tagSource: UIEventSource<Record<string, string>>,
|
||||
argument: string[],
|
||||
feature: Feature,
|
||||
layer: LayerConfig
|
||||
): BaseUIElement {
|
||||
return new VariableUiElement(
|
||||
tagSource
|
||||
.map((tags) => tags.id)
|
||||
.map(
|
||||
(id) =>
|
||||
new DeleteWizard(
|
||||
<OsmId>id,
|
||||
<UIEventSource<OsmTags>>tagSource,
|
||||
state,
|
||||
layer.deletion // Reading the configuration from the layerconfig is a bit cheating and should be factored out
|
||||
)
|
||||
)
|
||||
)
|
||||
},
|
||||
},
|
||||
new ShareLinkViz(),
|
||||
new UploadToOsmViz(),
|
||||
new MultiApplyViz(),
|
||||
|
|
|
@ -93,7 +93,7 @@
|
|||
|
||||
<div class="absolute top-0 right-0 mt-4 mr-4">
|
||||
<If condition={state.featureSwitches.featureSwitchSearch}>
|
||||
<Geosearch bounds={state.mapProperties.bounds} {selectedElement} {selectedLayer} {state}></Geosearch>
|
||||
<Geosearch bounds={state.mapProperties.bounds} {selectedElement} {selectedLayer} perLayer={state.perLayer}></Geosearch>
|
||||
</If>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -2,6 +2,12 @@
|
|||
"id": "last_click",
|
||||
"description": "This layer defines how to render the 'last click'-location. By default, it will show a marker with the possibility to add a new point (if there are some presets) and/or to add a new note (if the 'note' layer attribute is set). If none are possible, this layer won't show up",
|
||||
"source": "special",
|
||||
"isShown": {
|
||||
"or": [
|
||||
"has_presets=yes",
|
||||
"has_note_layer=yes"
|
||||
]
|
||||
},
|
||||
"name": null,
|
||||
"titleIcons": [],
|
||||
"title": {
|
||||
|
@ -153,4 +159,4 @@
|
|||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -116,7 +116,7 @@
|
|||
"mappings": [
|
||||
{
|
||||
"if": "theme=advertising",
|
||||
"then": "./assets/themes/advertising/poster_box.svg"
|
||||
"then": "./assets/themes/advertising/icon.svg"
|
||||
},
|
||||
{
|
||||
"if": "theme=aed",
|
||||
|
|
Loading…
Reference in a new issue