Merge branch 'master' into develop

This commit is contained in:
Pieter Vander Vennet 2024-10-01 23:46:17 +02:00
commit af174f7695
265 changed files with 11126 additions and 4505 deletions

View file

@ -205,10 +205,19 @@ export class ChangesetHandler {
try {
return await this.UploadWithNew(generateChangeXML, openChangeset, extraMetaTags)
} catch (e) {
const req = (<XMLHttpRequest>e)
if (req.status === 403) {
// Someone got the banhammer
// This is the message that OSM returned, will be something like "you have an important message, go to osm.org"
const msg = req.responseText
alert(msg+"\n\nWe'll take you to openstreetmap.org now")
window.location.replace(this.osmConnection.Backend())
return
}
if (this._reportError) {
this._reportError(e, "While opening a new changeset")
}
if ((<XMLHttpRequest>e).status === 400) {
if (req.status === 400) {
// This request is invalid. We simply drop the changes and hope that someone will analyze what went wrong with it in the upload; we pretend everything went fine
return
}

View file

@ -155,6 +155,10 @@ export default class ThemeViewState implements SpecialVisualizationState {
public readonly geocodedImages: UIEventSource<Feature[]> = new UIEventSource<Feature[]>([])
public readonly searchState: SearchState
/**
* Used to check in the download panel if used
*/
public readonly featureSummary: SummaryTileSourceRewriter
constructor(layout: LayoutConfig, mvtAvailableLayers: Set<string>) {
Utils.initDomPurify()
@ -356,6 +360,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
bounds: this.visualFeedbackViewportBounds,
},
)
this.featureSummary = this.setupSummaryLayer()
this.hasDataInView = new NoElementsInViewDetector(this).hasFeatureInView
this.imageUploadManager = new ImageUploadManager(
layout,
@ -785,7 +790,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
),
current_view: this.currentView,
favourite: this.favourites,
summary: this.setupSummaryLayer(),
summary: this.featureSummary,
last_click: this.lastClickObject,
search: this.searchState.locationResults,
}

View file

@ -22,7 +22,7 @@
let metaIsIncluded = false
let numberOfFeatures = state.featureSummary.totalNumberOfFeatures
let numberOfFeatures = state.featureSummary?.totalNumberOfFeatures
async function getGeojson() {
await state.indexedFeatures.downloadAll()

View file

@ -23,6 +23,7 @@
import type { LayerConfigJson } from "../../Models/ThemeConfig/Json/LayerConfigJson"
import { onDestroy } from "svelte"
import { BBox } from "../../Logic/BBox"
import PanoramaxLink from "../BigComponents/PanoramaxLink.svelte"
export let tags: UIEventSource<OsmTags>
@ -37,7 +38,7 @@
let imagesProvider = state.nearbyImageSearcher
let loadedImages = AllImageProviders.LoadImagesFor(tags).mapD(
(loaded) => new Set(loaded.map((img) => img.url))
(loaded) => new Set(loaded.map((img) => img.url)),
)
let imageState = imagesProvider.getImagesAround(lon, lat)
let result: Store<P4CPicture[]> = imageState.images.mapD(
@ -46,22 +47,22 @@
.filter(
(p: P4CPicture) =>
!loadedImages.data.has(p.pictureUrl) && // We don't show any image which is already linked
!p.details.isSpherical
!p.details.isSpherical,
)
.slice(0, 25),
[loadedImages]
[loadedImages],
)
let asFeatures = result.map(p4cs => p4cs.map(p4c => (<Feature<Point>>{
type: "Feature",
geometry: {
type: "Point",
coordinates: [p4c.coordinates.lng, p4c.coordinates.lat]
coordinates: [p4c.coordinates.lng, p4c.coordinates.lat],
},
properties: {
id: p4c.pictureUrl,
rotation: p4c.direction
}
rotation: p4c.direction,
},
})))
let selected = new UIEventSource<P4CPicture>(undefined)
@ -70,28 +71,28 @@
type: "Feature",
geometry: {
type: "Point",
coordinates: [s.coordinates.lng, s.coordinates.lat]
coordinates: [s.coordinates.lng, s.coordinates.lat],
},
properties: {
id: s.pictureUrl,
selected: "yes",
rotation: s.direction
}
rotation: s.direction,
},
}]
})
let someLoading = imageState.state.mapD((stateRecord) =>
Object.values(stateRecord).some((v) => v === "loading")
Object.values(stateRecord).some((v) => v === "loading"),
)
let errors = imageState.state.mapD((stateRecord) =>
Object.keys(stateRecord).filter((k) => stateRecord[k] === "error")
Object.keys(stateRecord).filter((k) => stateRecord[k] === "error"),
)
let highlighted = new UIEventSource<string>(undefined)
onDestroy(highlighted.addCallbackD(hl => {
const p4c = result.data?.find(i => i.pictureUrl === hl)
selected.set(p4c)
}
},
))
let map: UIEventSource<MlMap> = new UIEventSource<MlMap>(undefined)
@ -111,28 +112,27 @@
zoomToFeatures: true,
onClick: (feature) => {
highlighted.set(feature.properties.id)
}
},
})
ShowDataLayer.showMultipleLayers(
map,
new StaticFeatureSource([feature]),
state.layout.layers
state.layout.layers,
)
onDestroy(
asFeatures.addCallbackAndRunD(features => {
if(features.length == 0){
return
}
let bbox = BBox.get(features[0])
for (const f of features) {
bbox = bbox.unionWith(BBox.get(f))
}
mapProperties.maxbounds.set(bbox.pad(4))
})
asFeatures.addCallbackAndRunD(features => {
if (features.length == 0) {
return
}
let bbox = BBox.get(features[0])
for (const f of features) {
bbox = bbox.unionWith(BBox.get(f))
}
mapProperties.maxbounds.set(bbox.pad(4))
}),
)
new ShowDataLayer(map, {
@ -140,12 +140,10 @@
layer: geocodedImageLayer,
onClick: (feature) => {
highlighted.set(feature.properties.id)
}
},
})
</script>
<div class="flex flex-col">
@ -169,13 +167,16 @@
{/each}
</div>
{/if}
<span class="self-end pt-2">
<MapillaryLink
large={false}
mapProperties={{ zoom: new ImmutableStore(16), location: new ImmutableStore({ lon, lat }) }}
/>
</span>
<div class="w-full flex flex-wrap justify-end gap-x-8 pt-2">
<PanoramaxLink
large={false}
mapProperties={{ zoom: new ImmutableStore(16), location: new ImmutableStore({ lon, lat }) }}
/>
<MapillaryLink
large={false}
mapProperties={{ zoom: new ImmutableStore(16), location: new ImmutableStore({ lon, lat }) }}
/>
</div>
<div class="my-2 flex justify-between">

View file

@ -0,0 +1,103 @@
<script lang="ts">
import type { SpecialVisualizationState } from "../../SpecialVisualization"
import Translations from "../../i18n/Translations"
import Note from "../../../assets/svg/Note.svelte"
import Resolved from "../../../assets/svg/Resolved.svelte"
import Speech_bubble from "../../../assets/svg/Speech_bubble.svelte"
import { ImmutableStore, Stores } from "../../../Logic/UIEventSource"
import { Utils } from "../../../Utils"
import Img from "../../Base/Img"
import { SlideShow } from "../../Image/SlideShow"
import ToSvelte from "../../Base/ToSvelte.svelte"
import Tr from "../../Base/Tr.svelte"
export let comment: {
date: string
uid: number
user: string
user_url: string
action: "closed" | "opened" | "reopened" | "commented"
text: string
html: string
highlighted: boolean
}
export let state: SpecialVisualizationState = undefined
const t = Translations.t.notes
// Info about the user who made the comment
let userinfo = Stores.FromPromise(
Utils.downloadJsonCached<{ user: { img: { href: string } } }>(
"https://api.openstreetmap.org/api/0.6/user/" + comment.uid,
24 * 60 * 60 * 1000,
),
)
const htmlElement = document.createElement("div")
htmlElement.innerHTML = Utils.purify(comment.html)
let images: string[] = Array.from(htmlElement.getElementsByTagName("a"))
.map((link) => link.href)
.filter((link) => {
link = link.toLowerCase()
const lastDotIndex = link.lastIndexOf(".")
const extension = link.substring(lastDotIndex + 1, link.length)
return Utils.imageExtensions.has(extension)
})
.filter((link) => !link.startsWith("https://wiki.openstreetmap.org/wiki/File:"))
let imgStore = new ImmutableStore(
images.map((i) =>
new Img(i).SetClass("w-full block cursor-pointer")
.onClick(() =>
state?.previewedImage?.setData(
<any>{
url_hd: i,
url: i,
}),
)))
</script>
<div class="flex flex-col py-2 my-2 border-gray-500 border-b" class:border-interactive={comment.highlighted}>
<div class="flex">
<!-- Action icon, e.g. 'created', 'commented', 'closed' -->
{#if comment.action === "opened" || comment.action === "reopened"}
<Note class="shrink-0 mr-4 w-6" />
{:else if comment.action === "closed"}
<Resolved class="shrink-0 mr-4 w-6" />
{:else}
<Speech_bubble class="shrink-0 mr-4 w-6" />
{/if}
<div class="flex flex-col gap-y-2">
{@html comment.html}
</div>
</div>
{#if images.length > 0}
<ToSvelte
construct={() => new SlideShow(imgStore) .SetClass("mb-1").SetStyle("min-width: 50px; background: grey;")} />
{/if}
<div class="flex justify-end items-center subtle pt-4 pb-2">
<!-- commenter info -->
{#if $userinfo?.user?.img?.href}
<img alt="avatar" aria-hidden="true" src={$userinfo?.user?.img?.href} class="rounded-full w-8 h-8 mr-4" />
{/if}
<span class="mr-2">
{#if comment.user === undefined}
<Tr t={t.anonymous} />
{:else}
<a href={comment.user_url} target="_blank">{comment.user}</a>
{/if}
{comment.date}
</span>
</div>
</div>

View file

@ -1,117 +1,7 @@
import Combine from "../../Base/Combine"
import BaseUIElement from "../../BaseUIElement"
import Link from "../../Base/Link"
import { FixedUiElement } from "../../Base/FixedUiElement"
import Translations from "../../i18n/Translations"
import { Utils } from "../../../Utils"
import Img from "../../Base/Img"
import { SlideShow } from "../../Image/SlideShow"
import { Store, Stores, UIEventSource } from "../../../Logic/UIEventSource"
import { VariableUiElement } from "../../Base/VariableUIElement"
import { SpecialVisualizationState } from "../../SpecialVisualization"
import SvelteUIElement from "../../Base/SvelteUIElement"
import Note from "../../../assets/svg/Note.svelte"
import Resolved from "../../../assets/svg/Resolved.svelte"
import Speech_bubble from "../../../assets/svg/Speech_bubble.svelte"
import { Store, UIEventSource } from "../../../Logic/UIEventSource"
export default class NoteCommentElement extends Combine {
constructor(
comment: {
date: string
uid: number
user: string
user_url: string
action: "closed" | "opened" | "reopened" | "commented"
text: string
html: string
highlighted: boolean
},
state?: SpecialVisualizationState,
index?: number,
totalNumberOfComments?: number
) {
const t = Translations.t.notes
export default class NoteCommentElement {
let actionIcon: BaseUIElement
if (comment.action === "opened" || comment.action === "reopened") {
actionIcon = new SvelteUIElement(Note)
} else if (comment.action === "closed") {
actionIcon = new SvelteUIElement(Resolved)
} else {
actionIcon = new SvelteUIElement(Speech_bubble)
}
let user: BaseUIElement
if (comment.user === undefined) {
user = t.anonymous
} else {
user = new Link(comment.user, comment.user_url ?? "", true)
}
const userinfo = Stores.FromPromise(
Utils.downloadJsonCached<{ user: { img: { href: string } } }>(
"https://api.openstreetmap.org/api/0.6/user/" + comment.uid,
24 * 60 * 60 * 1000
)
)
const userImg = new VariableUiElement(
userinfo.map((userinfo) => {
const href = userinfo?.user?.img?.href
if (href !== undefined) {
return new Img(href).SetClass("rounded-full w-8 h-8 mr-4")
}
return undefined
})
)
const htmlElement = document.createElement("div")
htmlElement.innerHTML = Utils.purify(comment.html)
const images = Array.from(htmlElement.getElementsByTagName("a"))
.map((link) => link.href)
.filter((link) => {
link = link.toLowerCase()
const lastDotIndex = link.lastIndexOf(".")
const extension = link.substring(lastDotIndex + 1, link.length)
return Utils.imageExtensions.has(extension)
})
.filter((link) => !link.startsWith("https://wiki.openstreetmap.org/wiki/File:"))
let imagesEl: BaseUIElement = undefined
if (images.length > 0) {
const imageEls = images.map((i) =>
new Img(i)
.SetClass("w-full block cursor-pointer")
.onClick(() =>
state?.previewedImage?.setData(<any>{
url_hd: i,
url: i,
})
)
.SetStyle("min-width: 50px; background: grey;")
)
imagesEl = new SlideShow(new UIEventSource<BaseUIElement[]>(imageEls)).SetClass("mb-1")
}
super([
new Combine([
actionIcon.SetClass("mr-4 w-6").SetStyle("flex-shrink: 0"),
new FixedUiElement(comment.html).SetClass("flex flex-col").SetStyle("margin: 0"),
]).SetClass("flex"),
imagesEl,
new Combine([userImg, user.SetClass("mr-2"), comment.date]).SetClass(
"flex justify-end items-center subtle"
),
])
this.SetClass("flex flex-col pb-2 mb-2 border-gray-500 border-b")
if (comment.highlighted) {
this.SetClass("focus")
if (index + 2 === totalNumberOfComments) {
console.log("Scrolling into view")
requestAnimationFrame(() => {
this.ScrollIntoView()
})
}
}
}
/**
* Adds the comment to the _visualisation_ of the given note; doesn't _actually_ upload

View file

@ -2,11 +2,7 @@ import Combine from "./Base/Combine"
import { FixedUiElement } from "./Base/FixedUiElement"
import BaseUIElement from "./BaseUIElement"
import Title from "./Base/Title"
import {
RenderingSpecification,
SpecialVisualization,
SpecialVisualizationState,
} from "./SpecialVisualization"
import { RenderingSpecification, SpecialVisualization, SpecialVisualizationState } from "./SpecialVisualization"
import { HistogramViz } from "./Popup/HistogramViz"
import MinimapViz from "./Popup/MinimapViz.svelte"
import { ShareLinkViz } from "./Popup/ShareLinkViz"
@ -27,7 +23,6 @@ import { Translation } from "./i18n/Translation"
import Translations from "./i18n/Translations"
import OpeningHoursVisualization from "./OpeningHours/OpeningHoursVisualization"
import { SubtleButton } from "./Base/SubtleButton"
import NoteCommentElement from "./Popup/Notes/NoteCommentElement"
import List from "./Base/List"
import StatisticsPanel from "./BigComponents/StatisticsPanel"
import AutoApplyButton from "./Popup/AutoApplyButton"
@ -101,6 +96,7 @@ import QrCode from "./Popup/QrCode.svelte"
import ClearCaches from "./Popup/ClearCaches.svelte"
import GroupedView from "./Popup/GroupedView.svelte"
import { QuestionableTagRenderingConfigJson } from "../Models/ThemeConfig/Json/QuestionableTagRenderingConfigJson"
import NoteCommentElement from "./Popup/Notes/NoteCommentElement.svelte"
class NearbyImageVis implements SpecialVisualization {
// Class must be in SpecialVisualisations due to weird cyclical import that breaks the tests
@ -126,7 +122,7 @@ class NearbyImageVis implements SpecialVisualization {
tags: UIEventSource<Record<string, string>>,
args: string[],
feature: Feature,
layer: LayerConfig
layer: LayerConfig,
): SvelteUIElement {
const isOpen = args[0] === "open"
const readonly = args[1] === "readonly"
@ -193,7 +189,7 @@ class StealViz implements SpecialVisualization {
selectedElement: otherFeature,
state,
layer,
})
}),
)
}
if (elements.length === 1) {
@ -201,8 +197,8 @@ class StealViz implements SpecialVisualization {
}
return new Combine(elements).SetClass("flex flex-col")
},
[state.indexedFeatures.featuresById]
)
[state.indexedFeatures.featuresById],
),
)
}
@ -254,11 +250,11 @@ class CloseNoteViz implements SpecialVisualization {
public constr(
state: SpecialVisualizationState,
tags: UIEventSource<Record<string, string>>,
args: string[]
args: string[],
): SvelteUIElement {
const { text, icon, idkey, comment, minZoom, zoomButton } = Utils.ParseVisArgs(
this.args,
args
args,
)
return new SvelteUIElement(CloseNoteButton, {
@ -299,7 +295,7 @@ export class QuestionViz implements SpecialVisualization {
tags: UIEventSource<Record<string, string>>,
args: string[],
feature: Feature,
layer: LayerConfig
layer: LayerConfig,
): SvelteUIElement {
const labels = args[0]
?.split(";")
@ -331,7 +327,7 @@ export default class SpecialVisualizations {
for (const specialVisualization of SpecialVisualizations.specialVisualizations) {
SpecialVisualizations.specialVisualisationsDict.set(
specialVisualization.funcName,
specialVisualization
specialVisualization,
)
}
}
@ -351,15 +347,15 @@ export default class SpecialVisualizations {
viz.docs,
viz.args.length > 0
? MarkdownUtils.table(
["name", "default", "description"],
viz.args.map((arg) => {
let defaultArg = arg.defaultValue ?? "_undefined_"
if (defaultArg == "") {
defaultArg = "_empty string_"
}
return [arg.name, defaultArg, arg.doc]
})
)
["name", "default", "description"],
viz.args.map((arg) => {
let defaultArg = arg.defaultValue ?? "_undefined_"
if (defaultArg == "") {
defaultArg = "_empty string_"
}
return [arg.name, defaultArg, arg.doc]
}),
)
: undefined,
"#### Example usage of " + viz.funcName,
"<code>" + example + "</code>",
@ -368,18 +364,18 @@ export default class SpecialVisualizations {
public static constructSpecification(
template: string,
extraMappings: SpecialVisualization[] = []
extraMappings: SpecialVisualization[] = [],
): RenderingSpecification[] {
return SpecialVisualisationUtils.constructSpecification(
template,
SpecialVisualizations.specialVisualisationsDict,
extraMappings
extraMappings,
)
}
public static HelpMessage(): string {
const helpTexts: string[] = SpecialVisualizations.specialVisualizations.map((viz) =>
SpecialVisualizations.DocumentationFor(viz)
SpecialVisualizations.DocumentationFor(viz),
)
const firstPart = new Combine([
@ -412,10 +408,10 @@ export default class SpecialVisualizations {
},
},
null,
" "
)
" ",
),
).SetClass("code"),
'In other words: use `{ "before": ..., "after": ..., "special": {"type": ..., "argname": ...argvalue...}`. The args are in the `special` block; an argvalue can be a string, a translation or another value. (Refer to class `RewriteSpecial` in case of problems)',
"In other words: use `{ \"before\": ..., \"after\": ..., \"special\": {\"type\": ..., \"argname\": ...argvalue...}`. The args are in the `special` block; an argvalue can be a string, a translation or another value. (Refer to class `RewriteSpecial` in case of problems)",
])
.SetClass("flex flex-col")
.AsMarkdown()
@ -453,10 +449,10 @@ export default class SpecialVisualizations {
assignTo: state.userRelatedState.language,
availableLanguages: languages,
preferredLanguages: state.osmConnection.userDetails.map(
(ud) => ud.languages
(ud) => ud.languages,
),
})
})
}),
)
},
},
@ -495,7 +491,7 @@ export default class SpecialVisualizations {
state: SpecialVisualizationState,
tagSource: UIEventSource<Record<string, string>>,
args: string[],
feature: Feature
feature: Feature,
): SvelteUIElement {
return new SvelteUIElement(MinimapViz, { state, args, feature, tagSource })
},
@ -507,7 +503,7 @@ export default class SpecialVisualizations {
constr(
state: SpecialVisualizationState,
tagSource: UIEventSource<Record<string, string>>
tagSource: UIEventSource<Record<string, string>>,
): BaseUIElement {
return new VariableUiElement(
tagSource
@ -517,7 +513,7 @@ export default class SpecialVisualizations {
return new SvelteUIElement(SplitRoadWizard, { id, state })
}
return undefined
})
}),
)
},
},
@ -531,7 +527,7 @@ export default class SpecialVisualizations {
tagSource: UIEventSource<Record<string, string>>,
argument: string[],
feature: Feature,
layer: LayerConfig
layer: LayerConfig,
): BaseUIElement {
if (feature.geometry.type !== "Point") {
return undefined
@ -554,7 +550,7 @@ export default class SpecialVisualizations {
tagSource: UIEventSource<Record<string, string>>,
argument: string[],
feature: Feature,
layer: LayerConfig
layer: LayerConfig,
): BaseUIElement {
if (!layer.deletion) {
return undefined
@ -582,7 +578,7 @@ export default class SpecialVisualizations {
state: SpecialVisualizationState,
tagSource: UIEventSource<Record<string, string>>,
argument: string[],
feature: Feature
feature: Feature,
): BaseUIElement {
const [lon, lat] = GeoOperations.centerpointCoordinates(feature)
return new SvelteUIElement(CreateNewNote, {
@ -645,7 +641,7 @@ export default class SpecialVisualizations {
.map((tags) => tags[args[0]])
.map((wikidata) => {
wikidata = Utils.NoEmpty(
wikidata?.split(";")?.map((wd) => wd.trim()) ?? []
wikidata?.split(";")?.map((wd) => wd.trim()) ?? [],
)[0]
const entry = Wikidata.LoadWikidataEntry(wikidata)
return new VariableUiElement(
@ -655,9 +651,9 @@ export default class SpecialVisualizations {
}
const response = <WikidataResponse>e["success"]
return Translation.fromMap(response.labels)
})
}),
)
})
}),
),
},
new MapillaryLinkVis(),
@ -671,7 +667,7 @@ export default class SpecialVisualizations {
tags: UIEventSource<Record<string, string>>,
_,
__,
layer: LayerConfig
layer: LayerConfig,
) => new SvelteUIElement(AllTagsPanel, { tags, layer }),
},
{
@ -693,7 +689,7 @@ export default class SpecialVisualizations {
return new ImageCarousel(
AllImageProviders.LoadImagesFor(tags, imagePrefixes),
tags,
state
state,
)
},
},
@ -714,7 +710,7 @@ export default class SpecialVisualizations {
},
],
constr: (state, tags, args) => {
const targetKey = args[0] === "" ? undefined : args[0]
const targetKey = args[0] === "" ? undefined : args[0]
return new SvelteUIElement(UploadImage, {
state,
tags,
@ -750,7 +746,7 @@ export default class SpecialVisualizations {
nameKey: nameKey,
fallbackName,
},
state.featureSwitchIsTesting
state.featureSwitchIsTesting,
)
return new SvelteUIElement(StarsBarIcon, {
score: reviews.average,
@ -784,7 +780,7 @@ export default class SpecialVisualizations {
nameKey: nameKey,
fallbackName,
},
state.featureSwitchIsTesting
state.featureSwitchIsTesting,
)
return new SvelteUIElement(ReviewForm, { reviews, state, tags, feature, layer })
},
@ -815,7 +811,7 @@ export default class SpecialVisualizations {
nameKey: nameKey,
fallbackName,
},
state.featureSwitchIsTesting
state.featureSwitchIsTesting,
)
return new SvelteUIElement(AllReviews, { reviews, state, tags, feature, layer })
},
@ -841,7 +837,7 @@ export default class SpecialVisualizations {
tagSource: UIEventSource<Record<string, string>>,
args: string[],
feature: Feature,
layer: LayerConfig
layer: LayerConfig,
): BaseUIElement {
return new Combine([
SpecialVisualizations.specialVisualisationsDict["create_review"].constr(
@ -849,14 +845,14 @@ export default class SpecialVisualizations {
tagSource,
args,
feature,
layer
layer,
),
SpecialVisualizations.specialVisualisationsDict["list_reviews"].constr(
state,
tagSource,
args,
feature,
layer
layer,
),
])
},
@ -874,7 +870,7 @@ export default class SpecialVisualizations {
constr(
state: SpecialVisualizationState,
_: UIEventSource<Record<string, string>>,
argument: string[]
argument: string[],
): BaseUIElement {
const [text] = argument
return new SvelteUIElement(ImportReviewIdentity, { state, text })
@ -931,7 +927,7 @@ export default class SpecialVisualizations {
constr(
state: SpecialVisualizationState,
tags: UIEventSource<Record<string, string>>,
args: string[]
args: string[],
): SvelteUIElement {
const keyToUse = args[0]
const prefix = args[1]
@ -968,17 +964,17 @@ export default class SpecialVisualizations {
return undefined
}
const allUnits: Unit[] = [].concat(
...(state?.layout?.layers?.map((lyr) => lyr.units) ?? [])
...(state?.layout?.layers?.map((lyr) => lyr.units) ?? []),
)
const unit = allUnits.filter((unit) =>
unit.isApplicableToKey(key)
unit.isApplicableToKey(key),
)[0]
if (unit === undefined) {
return value
}
const getCountry = () => tagSource.data._country
return unit.asHumanLongValue(value, getCountry)
})
}),
)
},
},
@ -995,7 +991,7 @@ export default class SpecialVisualizations {
new Combine([
t.downloadFeatureAsGeojson.SetClass("font-bold text-lg"),
t.downloadGeoJsonHelper.SetClass("subtle"),
]).SetClass("flex flex-col")
]).SetClass("flex flex-col"),
)
.onClick(() => {
console.log("Exporting as Geojson")
@ -1008,7 +1004,7 @@ export default class SpecialVisualizations {
title + "_mapcomplete_export.geojson",
{
mimetype: "application/vnd.geo+json",
}
},
)
})
.SetClass("w-full")
@ -1044,7 +1040,7 @@ export default class SpecialVisualizations {
constr: (state) => {
return new SubtleButton(
new SvelteUIElement(Trash).SetClass("h-6"),
Translations.t.general.removeLocationHistory
Translations.t.general.removeLocationHistory,
).onClick(() => {
state.historicalUserLocations.features.setData([])
state.selectedElement.setData(undefined)
@ -1081,11 +1077,11 @@ export default class SpecialVisualizations {
comments
.filter((c) => c.text !== "")
.map(
(c, i) =>
new NoteCommentElement(c, state, i, comments.length)
)
(comment) =>
new SvelteUIElement(NoteCommentElement, { comment, state }),
),
).SetClass("flex flex-col")
})
}),
),
},
{
@ -1103,7 +1099,7 @@ export default class SpecialVisualizations {
constr: (state, tags, args, feature, layer) => {
const id = tags.data[args[0] ?? "id"]
tags = state.featureProperties.getStore(id)
return new SvelteUIElement(UploadImage, { state, tags, layer })
return new SvelteUIElement(UploadImage, { state, tags, layer })
},
},
{
@ -1118,7 +1114,7 @@ export default class SpecialVisualizations {
tagsSource: UIEventSource<Record<string, string>>,
_: string[],
feature: Feature,
layer: LayerConfig
layer: LayerConfig,
) =>
new VariableUiElement(
tagsSource.map((tags) => {
@ -1138,7 +1134,7 @@ export default class SpecialVisualizations {
})
.SetClass("px-1")
.setSpan()
})
}),
),
},
{
@ -1154,8 +1150,8 @@ export default class SpecialVisualizations {
const challenge = Stores.FromPromise(
Utils.downloadJsonCached<MaprouletteTask>(
`${Maproulette.defaultEndpoint}/challenge/${parentId}`,
24 * 60 * 60 * 1000
)
24 * 60 * 60 * 1000,
),
)
return new VariableUiElement(
@ -1180,7 +1176,7 @@ export default class SpecialVisualizations {
} else {
return [title, new List(listItems)]
}
})
}),
)
},
docs: "Fetches the metadata of MapRoulette campaign that this task is part of and shows those details (namely `title`, `description` and `instruction`).\n\nThis reads the property `mr_challengeId` to detect the parent campaign.",
@ -1194,15 +1190,15 @@ export default class SpecialVisualizations {
"\n" +
"```json\n" +
"{\n" +
' "id": "mark_duplicate",\n' +
' "render": {\n' +
' "special": {\n' +
' "type": "maproulette_set_status",\n' +
' "message": {\n' +
' "en": "Mark as not found or false positive"\n' +
" \"id\": \"mark_duplicate\",\n" +
" \"render\": {\n" +
" \"special\": {\n" +
" \"type\": \"maproulette_set_status\",\n" +
" \"message\": {\n" +
" \"en\": \"Mark as not found or false positive\"\n" +
" },\n" +
' "status": "2",\n' +
' "image": "close"\n' +
" \"status\": \"2\",\n" +
" \"image\": \"close\"\n" +
" }\n" +
" }\n" +
"}\n" +
@ -1278,7 +1274,7 @@ export default class SpecialVisualizations {
(l) =>
l.name !== null &&
l.title &&
state.perLayer.get(l.id) !== undefined
state.perLayer.get(l.id) !== undefined,
)
.map(
(l) => {
@ -1288,8 +1284,8 @@ export default class SpecialVisualizations {
const fsBboxed = new BBoxFeatureSourceForLayer(fs, bbox)
return new StatisticsPanel(fsBboxed)
},
[state.mapProperties.bounds]
)
[state.mapProperties.bounds],
),
)
},
},
@ -1359,7 +1355,7 @@ export default class SpecialVisualizations {
constr(
state: SpecialVisualizationState,
tagSource: UIEventSource<Record<string, string>>,
args: string[]
args: string[],
): SvelteUIElement {
let [text, href, classnames, download, ariaLabel, icon] = args
if (download === "") {
@ -1397,7 +1393,7 @@ export default class SpecialVisualizations {
},
},
null,
" "
" ",
) +
"\n```",
args: [
@ -1421,7 +1417,7 @@ export default class SpecialVisualizations {
featureTags: UIEventSource<Record<string, string>>,
args: string[],
feature: Feature,
layer: LayerConfig
layer: LayerConfig,
) {
const [key, tr, classesRaw] = args
let classes = classesRaw ?? ""
@ -1439,7 +1435,7 @@ export default class SpecialVisualizations {
"Could not create a special visualization for multi(",
args.join(", ") + ")",
"no properties found for object",
feature.properties.id
feature.properties.id,
)
return undefined
}
@ -1455,7 +1451,7 @@ export default class SpecialVisualizations {
elements.push(subsTr)
}
return elements
})
}),
)
},
},
@ -1475,7 +1471,7 @@ export default class SpecialVisualizations {
tagSource: UIEventSource<Record<string, string>>,
argument: string[],
feature: Feature,
layer: LayerConfig
layer: LayerConfig,
): BaseUIElement {
return new VariableUiElement(
tagSource.map((tags) => {
@ -1487,7 +1483,7 @@ export default class SpecialVisualizations {
console.error("Cannot create a translation for", v, "due to", e)
return JSON.stringify(v)
}
})
}),
)
},
},
@ -1507,7 +1503,7 @@ export default class SpecialVisualizations {
tagSource: UIEventSource<Record<string, string>>,
argument: string[],
feature: Feature,
layer: LayerConfig
layer: LayerConfig,
): BaseUIElement {
const key = argument[0]
const validator = new FediverseValidator()
@ -1517,7 +1513,7 @@ export default class SpecialVisualizations {
.map((fediAccount) => {
fediAccount = validator.reformat(fediAccount)
const [_, username, host] = fediAccount.match(
FediverseValidator.usernameAtServer
FediverseValidator.usernameAtServer,
)
const normalLink = new SvelteUIElement(Link, {
@ -1529,10 +1525,10 @@ export default class SpecialVisualizations {
const loggedInContributorMastodon =
state.userRelatedState?.preferencesAsTags?.data?.[
"_mastodon_link"
]
]
console.log(
"LoggedinContributorMastodon",
loggedInContributorMastodon
loggedInContributorMastodon,
)
if (!loggedInContributorMastodon) {
return normalLink
@ -1548,7 +1544,7 @@ export default class SpecialVisualizations {
newTab: true,
}).SetClass("button"),
])
})
}),
)
},
},
@ -1568,7 +1564,7 @@ export default class SpecialVisualizations {
tagSource: UIEventSource<Record<string, string>>,
args: string[],
feature: Feature,
layer: LayerConfig
layer: LayerConfig,
): BaseUIElement {
return new FixedUiElement("{" + args[0] + "}")
},
@ -1589,7 +1585,7 @@ export default class SpecialVisualizations {
tagSource: UIEventSource<Record<string, string>>,
argument: string[],
feature: Feature,
layer: LayerConfig
layer: LayerConfig,
): BaseUIElement {
const key = argument[0] ?? "value"
return new VariableUiElement(
@ -1607,12 +1603,12 @@ export default class SpecialVisualizations {
} catch (e) {
return new FixedUiElement(
"Could not parse this tag: " +
JSON.stringify(value) +
" due to " +
e
JSON.stringify(value) +
" due to " +
e,
).SetClass("alert")
}
})
}),
)
},
},
@ -1633,7 +1629,7 @@ export default class SpecialVisualizations {
tagSource: UIEventSource<Record<string, string>>,
argument: string[],
feature: Feature,
layer: LayerConfig
layer: LayerConfig,
): BaseUIElement {
const giggityUrl = argument[0]
return new SvelteUIElement(Giggity, { tags: tagSource, state, giggityUrl })
@ -1649,12 +1645,12 @@ export default class SpecialVisualizations {
_: UIEventSource<Record<string, string>>,
argument: string[],
feature: Feature,
layer: LayerConfig
layer: LayerConfig,
): BaseUIElement {
const tags = (<ThemeViewState>(
state
)).geolocation.currentUserLocation.features.map(
(features) => features[0]?.properties
(features) => features[0]?.properties,
)
return new Combine([
new SvelteUIElement(OrientationDebugPanel, {}),
@ -1676,7 +1672,7 @@ export default class SpecialVisualizations {
tagSource: UIEventSource<Record<string, string>>,
argument: string[],
feature: Feature,
layer: LayerConfig
layer: LayerConfig,
): BaseUIElement {
return new SvelteUIElement(MarkAsFavourite, {
tags: tagSource,
@ -1696,7 +1692,7 @@ export default class SpecialVisualizations {
tagSource: UIEventSource<Record<string, string>>,
argument: string[],
feature: Feature,
layer: LayerConfig
layer: LayerConfig,
): BaseUIElement {
return new SvelteUIElement(MarkAsFavouriteMini, {
tags: tagSource,
@ -1716,7 +1712,7 @@ export default class SpecialVisualizations {
tagSource: UIEventSource<Record<string, string>>,
argument: string[],
feature: Feature,
layer: LayerConfig
layer: LayerConfig,
): BaseUIElement {
return new SvelteUIElement(DirectionIndicator, { state, feature })
},
@ -1729,7 +1725,7 @@ export default class SpecialVisualizations {
state: SpecialVisualizationState,
tags: UIEventSource<Record<string, string>>,
argument: string[],
feature: Feature
feature: Feature,
): SvelteUIElement {
return new SvelteUIElement(QrCode, { state, tags, feature })
},
@ -1748,7 +1744,7 @@ export default class SpecialVisualizations {
constr(
state: SpecialVisualizationState,
tagSource: UIEventSource<Record<string, string>>,
args: string[]
args: string[],
): BaseUIElement {
const key = args[0] === "" ? "_direction:centerpoint" : args[0]
return new VariableUiElement(
@ -1759,11 +1755,11 @@ export default class SpecialVisualizations {
})
.mapD((value) => {
const dir = GeoOperations.bearingToHuman(
GeoOperations.parseBearing(value)
GeoOperations.parseBearing(value),
)
console.log("Human dir", dir)
return Translations.t.general.visualFeedback.directionsAbsolute[dir]
})
}),
)
},
},
@ -1793,7 +1789,7 @@ export default class SpecialVisualizations {
tagSource: UIEventSource<Record<string, string>>,
args: string[],
feature: Feature,
layer: LayerConfig
layer: LayerConfig,
): BaseUIElement {
const url = args[0]
const readonly = args[3] === "yes"
@ -1819,12 +1815,12 @@ export default class SpecialVisualizations {
tagSource: UIEventSource<Record<string, string>>,
args: string[],
feature: Feature,
layer: LayerConfig
layer: LayerConfig,
): BaseUIElement {
return new Toggle(
undefined,
new SvelteUIElement(LoginButton, { osmConnection: state.osmConnection }),
state.osmConnection.isLoggedIn
state.osmConnection.isLoggedIn,
)
},
},
@ -1862,7 +1858,7 @@ export default class SpecialVisualizations {
tags: UIEventSource<Record<string, string>>,
argument: string[],
feature: Feature,
layer: LayerConfig
layer: LayerConfig,
): BaseUIElement {
const key = argument[0] ?? "website"
const useProxy = argument[1] !== "no"
@ -1889,11 +1885,11 @@ export default class SpecialVisualizations {
const features =
await LinkedDataLoader.fetchVeloparkEntry(
url,
loadAll
loadAll,
)
const feature =
features.find(
(f) => f.properties["ref:velopark"] === url
(f) => f.properties["ref:velopark"] === url,
) ?? features[0]
const properties = feature.properties
properties["ref:velopark"] = url
@ -1903,7 +1899,7 @@ export default class SpecialVisualizations {
console.error(e)
throw e
}
})()
})(),
)
}
return Stores.FromPromiseWithErr(
@ -1912,27 +1908,27 @@ export default class SpecialVisualizations {
return await LinkedDataLoader.fetchJsonLd(
url,
{ country },
useProxy ? "proxy" : "fetch-lod"
useProxy ? "proxy" : "fetch-lod",
)
} catch (e) {
console.log(
"Could not get with proxy/download LOD, attempting to download directly. Error for ",
url,
"is",
e
e,
)
return await LinkedDataLoader.fetchJsonLd(
url,
{ country },
"fetch-raw"
"fetch-raw",
)
}
})()
})(),
)
})
externalData.addCallbackAndRunD((lod) =>
console.log("linked_data_from_website received the following data:", lod)
console.log("linked_data_from_website received the following data:", lod),
)
return new Toggle(
@ -1947,7 +1943,7 @@ export default class SpecialVisualizations {
collapsed: isClosed,
}),
undefined,
url.map((url) => !!url)
url.map((url) => !!url),
)
},
},
@ -1967,7 +1963,7 @@ export default class SpecialVisualizations {
tagSource: UIEventSource<Record<string, string>>,
argument: string[],
feature: Feature,
layer: LayerConfig
layer: LayerConfig,
): BaseUIElement {
const text = argument[0]
const cssClasses = argument[1]
@ -1989,7 +1985,7 @@ export default class SpecialVisualizations {
tagSource: UIEventSource<Record<string, string>>,
argument: string[],
feature: Feature,
layer: LayerConfig
layer: LayerConfig,
): BaseUIElement {
const translation = tagSource.map((tags) => {
const layer = state.layout.getMatchingLayer(tags)
@ -2007,7 +2003,7 @@ export default class SpecialVisualizations {
tagSource: UIEventSource<Record<string, string>>,
argument: string[],
feature: Feature,
layer: LayerConfig
layer: LayerConfig,
): BaseUIElement {
return new SvelteUIElement(PendingChangesIndicator, { state, compact: false })
},
@ -2027,7 +2023,7 @@ export default class SpecialVisualizations {
tagSource: UIEventSource<Record<string, string>>,
argument: string[],
feature: Feature,
layer: LayerConfig
layer: LayerConfig,
): SvelteUIElement {
return new SvelteUIElement<any, any, any>(ClearCaches, {
msg: argument[0] ?? "Clear local caches",
@ -2052,7 +2048,7 @@ export default class SpecialVisualizations {
tags: UIEventSource<Record<string, string>>,
argument: string[],
selectedElement: Feature,
layer: LayerConfig
layer: LayerConfig,
): SvelteUIElement {
const [header, labelsStr] = argument
const labels = labelsStr.split(";").map((x) => x.trim())
@ -2075,7 +2071,7 @@ export default class SpecialVisualizations {
tags: UIEventSource<Record<string, string>>,
argument: string[],
selectedElement: Feature,
layer: LayerConfig
layer: LayerConfig,
): SvelteUIElement {
const t = Translations.t.preset_type
const question: QuestionableTagRenderingConfigJson = {
@ -2101,22 +2097,22 @@ export default class SpecialVisualizations {
},
},
{
funcName:"clear_all",
funcName: "clear_all",
docs: "Clears all user preferences",
needsUrls: [],
args: [
{
name: "text",
doc: "Text to show on the button"
}
doc: "Text to show on the button",
},
],
constr(state: SpecialVisualizationState, tagSource: UIEventSource<Record<string, string>>, argument: string[], feature: Feature, layer: LayerConfig): BaseUIElement {
const text = argument[0]
return new SubtleButton(undefined, text).onClick(() => {
state.osmConnection.preferencesHandler.ClearPreferences()
})
}
}
},
},
]
specialVisualizations.push(new AutoApplyButton(specialVisualizations))
@ -2131,7 +2127,7 @@ export default class SpecialVisualizations {
"Invalid special visualisation found: funcName is undefined or doesn't match " +
regex +
invalid.map((sp) => sp.i).join(", ") +
'. Did you perhaps type \n funcName: "funcname" // type declaration uses COLON\ninstead of:\n funcName = "funcName" // value definition uses EQUAL'
". Did you perhaps type \n funcName: \"funcname\" // type declaration uses COLON\ninstead of:\n funcName = \"funcName\" // value definition uses EQUAL"
)
}

View file

@ -58,6 +58,17 @@
],
"description": "A description for the features shown in this layer.\nThis often resembles the introduction of the wiki.osm.org-page for this feature."
},
{
"path": [
"searchTerms"
],
"required": false,
"hints": {
"question": "What are some other terms used to describe these objects?"
},
"type": "object",
"description": "This is used in the search functionality"
},
{
"path": [
"source"
@ -552,10 +563,20 @@
},
"filter": {
"description": "This tagRendering can introduce this builtin filter",
"type": "array",
"items": {
"type": "string"
}
"anyOf": [
{
"type": "array",
"items": {
"type": "string"
}
},
{
"enum": [
true
],
"type": "boolean"
}
]
}
},
"additionalProperties": false
@ -1043,7 +1064,20 @@
],
"required": false,
"hints": {},
"type": "array",
"type": [
{
"type": "array",
"items": {
"type": "string"
}
},
{
"enum": [
true
],
"type": "boolean"
}
],
"description": "This tagRendering can introduce this builtin filter"
},
{
@ -1607,7 +1641,20 @@
],
"required": false,
"hints": {},
"type": "array",
"type": [
{
"type": "array",
"items": {
"type": "string"
}
},
{
"enum": [
true
],
"type": "boolean"
}
],
"description": "This tagRendering can introduce this builtin filter"
},
{
@ -1698,6 +1745,11 @@
"then": "addSmall",
"icon": "addSmall"
},
{
"if": "value=airport",
"then": "airport",
"icon": "airport"
},
{
"if": "value=brick_wall_round",
"then": "brick_wall_round",
@ -1708,6 +1760,16 @@
"then": "brick_wall_square",
"icon": "brick_wall_square"
},
{
"if": "value=building_office_2",
"then": "building_office_2",
"icon": "building_office_2"
},
{
"if": "value=building_storefront",
"then": "building_storefront",
"icon": "building_storefront"
},
{
"if": "value=bug",
"then": "bug",
@ -1778,6 +1840,11 @@
"then": "gear",
"icon": "gear"
},
{
"if": "value=globe_alt",
"then": "globe_alt",
"icon": "globe_alt"
},
{
"if": "value=gps_arrow",
"then": "gps_arrow",
@ -1808,6 +1875,11 @@
"then": "home",
"icon": "home"
},
{
"if": "value=house",
"then": "house",
"icon": "house"
},
{
"if": "value=key",
"then": "key",
@ -1913,11 +1985,21 @@
"then": "teardrop_with_hole_green",
"icon": "teardrop_with_hole_green"
},
{
"if": "value=train",
"then": "train",
"icon": "train"
},
{
"if": "value=triangle",
"then": "triangle",
"icon": "triangle"
},
{
"if": "value=user_circle",
"then": "user_circle",
"icon": "user_circle"
},
{
"if": "value=wifi",
"then": "wifi",
@ -2552,10 +2634,20 @@
},
"filter": {
"description": "This tagRendering can introduce this builtin filter",
"type": "array",
"items": {
"type": "string"
}
"anyOf": [
{
"type": "array",
"items": {
"type": "string"
}
},
{
"enum": [
true
],
"type": "boolean"
}
]
}
},
"additionalProperties": false
@ -3064,7 +3156,20 @@
],
"required": false,
"hints": {},
"type": "array",
"type": [
{
"type": "array",
"items": {
"type": "string"
}
},
{
"enum": [
true
],
"type": "boolean"
}
],
"description": "This tagRendering can introduce this builtin filter"
},
{
@ -3272,10 +3377,20 @@
},
"filter": {
"description": "This tagRendering can introduce this builtin filter",
"type": "array",
"items": {
"type": "string"
}
"anyOf": [
{
"type": "array",
"items": {
"type": "string"
}
},
{
"enum": [
true
],
"type": "boolean"
}
]
}
},
"additionalProperties": false
@ -3784,7 +3899,20 @@
],
"required": false,
"hints": {},
"type": "array",
"type": [
{
"type": "array",
"items": {
"type": "string"
}
},
{
"enum": [
true
],
"type": "boolean"
}
],
"description": "This tagRendering can introduce this builtin filter"
},
{
@ -3989,10 +4117,20 @@
},
"filter": {
"description": "This tagRendering can introduce this builtin filter",
"type": "array",
"items": {
"type": "string"
}
"anyOf": [
{
"type": "array",
"items": {
"type": "string"
}
},
{
"enum": [
true
],
"type": "boolean"
}
]
}
},
"additionalProperties": false
@ -4501,7 +4639,20 @@
],
"required": false,
"hints": {},
"type": "array",
"type": [
{
"type": "array",
"items": {
"type": "string"
}
},
{
"enum": [
true
],
"type": "boolean"
}
],
"description": "This tagRendering can introduce this builtin filter"
},
{
@ -4689,10 +4840,20 @@
},
"filter": {
"description": "This tagRendering can introduce this builtin filter",
"type": "array",
"items": {
"type": "string"
}
"anyOf": [
{
"type": "array",
"items": {
"type": "string"
}
},
{
"enum": [
true
],
"type": "boolean"
}
]
}
},
"additionalProperties": false
@ -5201,7 +5362,20 @@
],
"required": false,
"hints": {},
"type": "array",
"type": [
{
"type": "array",
"items": {
"type": "string"
}
},
{
"enum": [
true
],
"type": "boolean"
}
],
"description": "This tagRendering can introduce this builtin filter"
},
{
@ -5390,10 +5564,20 @@
},
"filter": {
"description": "This tagRendering can introduce this builtin filter",
"type": "array",
"items": {
"type": "string"
}
"anyOf": [
{
"type": "array",
"items": {
"type": "string"
}
},
{
"enum": [
true
],
"type": "boolean"
}
]
}
},
"additionalProperties": false
@ -5902,7 +6086,20 @@
],
"required": false,
"hints": {},
"type": "array",
"type": [
{
"type": "array",
"items": {
"type": "string"
}
},
{
"enum": [
true
],
"type": "boolean"
}
],
"description": "This tagRendering can introduce this builtin filter"
},
{
@ -6096,10 +6293,20 @@
},
"filter": {
"description": "This tagRendering can introduce this builtin filter",
"type": "array",
"items": {
"type": "string"
}
"anyOf": [
{
"type": "array",
"items": {
"type": "string"
}
},
{
"enum": [
true
],
"type": "boolean"
}
]
}
},
"additionalProperties": false
@ -6608,7 +6815,20 @@
],
"required": false,
"hints": {},
"type": "array",
"type": [
{
"type": "array",
"items": {
"type": "string"
}
},
{
"enum": [
true
],
"type": "boolean"
}
],
"description": "This tagRendering can introduce this builtin filter"
},
{
@ -6797,10 +7017,20 @@
},
"filter": {
"description": "This tagRendering can introduce this builtin filter",
"type": "array",
"items": {
"type": "string"
}
"anyOf": [
{
"type": "array",
"items": {
"type": "string"
}
},
{
"enum": [
true
],
"type": "boolean"
}
]
}
},
"additionalProperties": false
@ -7309,7 +7539,20 @@
],
"required": false,
"hints": {},
"type": "array",
"type": [
{
"type": "array",
"items": {
"type": "string"
}
},
{
"enum": [
true
],
"type": "boolean"
}
],
"description": "This tagRendering can introduce this builtin filter"
},
{
@ -7498,10 +7741,20 @@
},
"filter": {
"description": "This tagRendering can introduce this builtin filter",
"type": "array",
"items": {
"type": "string"
}
"anyOf": [
{
"type": "array",
"items": {
"type": "string"
}
},
{
"enum": [
true
],
"type": "boolean"
}
]
}
},
"additionalProperties": false
@ -8010,7 +8263,20 @@
],
"required": false,
"hints": {},
"type": "array",
"type": [
{
"type": "array",
"items": {
"type": "string"
}
},
{
"enum": [
true
],
"type": "boolean"
}
],
"description": "This tagRendering can introduce this builtin filter"
},
{
@ -8207,10 +8473,20 @@
},
"filter": {
"description": "This tagRendering can introduce this builtin filter",
"type": "array",
"items": {
"type": "string"
}
"anyOf": [
{
"type": "array",
"items": {
"type": "string"
}
},
{
"enum": [
true
],
"type": "boolean"
}
]
}
},
"additionalProperties": false
@ -8723,7 +8999,20 @@
],
"required": false,
"hints": {},
"type": "array",
"type": [
{
"type": "array",
"items": {
"type": "string"
}
},
{
"enum": [
true
],
"type": "boolean"
}
],
"description": "This tagRendering can introduce this builtin filter"
},
{
@ -8920,10 +9209,20 @@
},
"filter": {
"description": "This tagRendering can introduce this builtin filter",
"type": "array",
"items": {
"type": "string"
}
"anyOf": [
{
"type": "array",
"items": {
"type": "string"
}
},
{
"enum": [
true
],
"type": "boolean"
}
]
}
},
"additionalProperties": false
@ -9436,7 +9735,20 @@
],
"required": false,
"hints": {},
"type": "array",
"type": [
{
"type": "array",
"items": {
"type": "string"
}
},
{
"enum": [
true
],
"type": "boolean"
}
],
"description": "This tagRendering can introduce this builtin filter"
},
{
@ -10383,6 +10695,10 @@
"if": "value=bench_at_pt",
"then": "bench_at_pt - A layer showing all public-transport-stops which do have a bench"
},
{
"if": "value=bicycle_counter",
"then": "bicycle_counter - Layer showing monitoring stations for bicycle traffic"
},
{
"if": "value=bicycle_library",
"then": "bicycle_library - A facility where bicycles can be lent for longer period of times"
@ -10583,10 +10899,18 @@
"if": "value=food_courts",
"then": "food_courts - Food courts with a variety of food options."
},
{
"if": "value=geocoded_image",
"then": "geocoded_image - undefined"
},
{
"if": "value=ghost_bike",
"then": "ghost_bike - A layer showing memorials for cyclists, killed in road accidents"
},
{
"if": "value=ghostsign",
"then": "ghostsign - Layer showing disused signs on buildings"
},
{
"if": "value=governments",
"then": "governments - This layer show governmental buildings. It was setup as commissioned layer for the client of OSOC '22"
@ -10767,6 +11091,10 @@
"if": "value=playground_equipment",
"then": "playground_equipment - Layer showing playground equipment"
},
{
"if": "value=police",
"then": "police - Many types of police facilities "
},
{
"if": "value=postboxes",
"then": "postboxes - The layer showing postboxes."
@ -10811,6 +11139,10 @@
"if": "value=school",
"then": "school - Schools giving primary and secondary education and post-secondary, non-tertiary education. Note that this level of education does not imply an age of the pupiles"
},
{
"if": "value=search",
"then": "search - Priviliged layer showing the search results"
},
{
"if": "value=selected_element",
"then": "selected_element - Highlights the currently selected element. Override this layer to have different colors"
@ -11125,7 +11457,7 @@
]
},
"labels": {
"description": "A list of labels. These are strings that are used for various purposes, e.g. to only include a subset of the tagRenderings when reusing a layer",
"description": "What labels should be applied on this tagRendering?\n\nA list of labels. These are strings that are used for various purposes, e.g. to only include a subset of the tagRenderings when reusing a layer\n\nSpecial values:\n- \"hidden\": do not show this tagRendering. Useful in it is used by e.g. an accordion\n- \"description\": this label is a description used in the search",
"type": "array",
"items": {
"type": "string"
@ -11237,10 +11569,20 @@
},
"filter": {
"description": "This tagRendering can introduce this builtin filter",
"type": "array",
"items": {
"type": "string"
}
"anyOf": [
{
"type": "array",
"items": {
"type": "string"
}
},
{
"enum": [
true
],
"type": "boolean"
}
]
}
},
"required": [
@ -11991,6 +12333,10 @@
{
"if": "value=currency",
"then": "<b>currency</b> Validates monetary amounts"
},
{
"if": "value=regex",
"then": "<b>regex</b> Validates a regex"
}
]
},
@ -12251,7 +12597,7 @@
"required": false,
"hints": {},
"type": "array",
"description": "A list of labels. These are strings that are used for various purposes, e.g. to only include a subset of the tagRenderings when reusing a layer"
"description": "What labels should be applied on this tagRendering?\nA list of labels. These are strings that are used for various purposes, e.g. to only include a subset of the tagRenderings when reusing a layer\nSpecial values:\n- \"hidden\": do not show this tagRendering. Useful in it is used by e.g. an accordion\n- \"description\": this label is a description used in the search"
},
{
"path": [
@ -12578,7 +12924,20 @@
],
"required": false,
"hints": {},
"type": "array",
"type": [
{
"type": "array",
"items": {
"type": "string"
}
},
{
"enum": [
true
],
"type": "boolean"
}
],
"description": "This tagRendering can introduce this builtin filter"
},
{
@ -13297,6 +13656,10 @@
{
"if": "value=currency",
"then": "<b>currency</b> Validates monetary amounts"
},
{
"if": "value=regex",
"then": "<b>regex</b> Validates a regex"
}
]
},
@ -13570,7 +13933,7 @@
"required": false,
"hints": {},
"type": "array",
"description": "A list of labels. These are strings that are used for various purposes, e.g. to only include a subset of the tagRenderings when reusing a layer"
"description": "What labels should be applied on this tagRendering?\nA list of labels. These are strings that are used for various purposes, e.g. to only include a subset of the tagRenderings when reusing a layer\nSpecial values:\n- \"hidden\": do not show this tagRendering. Useful in it is used by e.g. an accordion\n- \"description\": this label is a description used in the search"
},
{
"path": [
@ -13910,7 +14273,20 @@
],
"required": false,
"hints": {},
"type": "array",
"type": [
{
"type": "array",
"items": {
"type": "string"
}
},
{
"enum": [
true
],
"type": "boolean"
}
],
"description": "This tagRendering can introduce this builtin filter"
},
{
@ -14650,6 +15026,10 @@
{
"if": "value=currency",
"then": "<b>currency</b> Validates monetary amounts"
},
{
"if": "value=regex",
"then": "<b>regex</b> Validates a regex"
}
]
},
@ -14923,7 +15303,7 @@
"required": false,
"hints": {},
"type": "array",
"description": "A list of labels. These are strings that are used for various purposes, e.g. to only include a subset of the tagRenderings when reusing a layer"
"description": "What labels should be applied on this tagRendering?\nA list of labels. These are strings that are used for various purposes, e.g. to only include a subset of the tagRenderings when reusing a layer\nSpecial values:\n- \"hidden\": do not show this tagRendering. Useful in it is used by e.g. an accordion\n- \"description\": this label is a description used in the search"
},
{
"path": [
@ -15263,7 +15643,20 @@
],
"required": false,
"hints": {},
"type": "array",
"type": [
{
"type": "array",
"items": {
"type": "string"
}
},
{
"enum": [
true
],
"type": "boolean"
}
],
"description": "This tagRendering can introduce this builtin filter"
},
{
@ -16007,6 +16400,10 @@
{
"if": "value=currency",
"then": "<b>currency</b> Validates monetary amounts"
},
{
"if": "value=regex",
"then": "<b>regex</b> Validates a regex"
}
]
},
@ -16293,7 +16690,7 @@
"required": false,
"hints": {},
"type": "array",
"description": "A list of labels. These are strings that are used for various purposes, e.g. to only include a subset of the tagRenderings when reusing a layer"
"description": "What labels should be applied on this tagRendering?\nA list of labels. These are strings that are used for various purposes, e.g. to only include a subset of the tagRenderings when reusing a layer\nSpecial values:\n- \"hidden\": do not show this tagRendering. Useful in it is used by e.g. an accordion\n- \"description\": this label is a description used in the search"
},
{
"path": [
@ -16646,7 +17043,20 @@
],
"required": false,
"hints": {},
"type": "array",
"type": [
{
"type": "array",
"items": {
"type": "string"
}
},
{
"enum": [
true
],
"type": "boolean"
}
],
"description": "This tagRendering can introduce this builtin filter"
},
{
@ -17365,6 +17775,10 @@
{
"if": "value=currency",
"then": "<b>currency</b> Validates monetary amounts"
},
{
"if": "value=regex",
"then": "<b>regex</b> Validates a regex"
}
]
},
@ -17638,7 +18052,7 @@
"required": false,
"hints": {},
"type": "array",
"description": "A list of labels. These are strings that are used for various purposes, e.g. to only include a subset of the tagRenderings when reusing a layer"
"description": "What labels should be applied on this tagRendering?\nA list of labels. These are strings that are used for various purposes, e.g. to only include a subset of the tagRenderings when reusing a layer\nSpecial values:\n- \"hidden\": do not show this tagRendering. Useful in it is used by e.g. an accordion\n- \"description\": this label is a description used in the search"
},
{
"path": [
@ -17978,7 +18392,20 @@
],
"required": false,
"hints": {},
"type": "array",
"type": [
{
"type": "array",
"items": {
"type": "string"
}
},
{
"enum": [
true
],
"type": "boolean"
}
],
"description": "This tagRendering can introduce this builtin filter"
},
{
@ -18722,6 +19149,10 @@
{
"if": "value=currency",
"then": "<b>currency</b> Validates monetary amounts"
},
{
"if": "value=regex",
"then": "<b>regex</b> Validates a regex"
}
]
},
@ -19008,7 +19439,7 @@
"required": false,
"hints": {},
"type": "array",
"description": "A list of labels. These are strings that are used for various purposes, e.g. to only include a subset of the tagRenderings when reusing a layer"
"description": "What labels should be applied on this tagRendering?\nA list of labels. These are strings that are used for various purposes, e.g. to only include a subset of the tagRenderings when reusing a layer\nSpecial values:\n- \"hidden\": do not show this tagRendering. Useful in it is used by e.g. an accordion\n- \"description\": this label is a description used in the search"
},
{
"path": [
@ -19361,7 +19792,20 @@
],
"required": false,
"hints": {},
"type": "array",
"type": [
{
"type": "array",
"items": {
"type": "string"
}
},
{
"enum": [
true
],
"type": "boolean"
}
],
"description": "This tagRendering can introduce this builtin filter"
},
{
@ -19398,7 +19842,7 @@
]
}
],
"description": "All the extra questions for filtering.\nIf a string is given, mapComplete will search in\n1. The tagrenderings for a match on ID and use the mappings as options\n2. search 'filters.json' for the appropriate filter or\n3. will try to parse it as `layername.filterid` and us that one."
"description": "All the extra questions for filtering.\nIf a string is given, mapComplete will search in\n1. The tagrenderings for a match on ID and use the mappings as options\n2. search 'filters.json' for the appropriate filter or\n3. will try to parse it as `layername.filterid` and us that one.\nNote: adding \"#filter\":\"no-auto\" will disable the filters added by tagRenderings"
},
{
"path": [
@ -19535,6 +19979,15 @@
"type": "string",
"description": "Used for comments or to disable a check\n\"ignore-possible-duplicate\": disables a check in `DetectDuplicateFilters` which complains that a filter can be replaced by a filter from the `filters`-library-layer"
},
{
"path": [
"#filter"
],
"required": false,
"hints": {},
"type": "string",
"description": "Set this to disable the feature that tagRenderings can introduce filters"
},
{
"path": [
"deletion"
@ -20223,5 +20676,24 @@
},
"type": "boolean",
"description": "When a changeset is made, a 'distance to object'-class is written to the changeset.\nFor some particular themes and layers, this might leak too much information, and we want to obfuscate this"
},
{
"path": [
"snapName"
],
"required": false,
"hints": {
"group": "hidden",
"question": "When a feature is snapped to this name, how should this item be called?"
},
"type": [
{
"$ref": "#/definitions/Record<string,string>"
},
{
"type": "string"
}
],
"description": "In the move wizard, the option `snap object onto {snapName}` is shown"
}
]

File diff suppressed because it is too large Load diff

View file

@ -672,6 +672,10 @@
{
"if": "value=currency",
"then": "<b>currency</b> Validates monetary amounts"
},
{
"if": "value=regex",
"then": "<b>regex</b> Validates a regex"
}
]
},
@ -919,7 +923,7 @@
"required": false,
"hints": {},
"type": "array",
"description": "A list of labels. These are strings that are used for various purposes, e.g. to only include a subset of the tagRenderings when reusing a layer"
"description": "What labels should be applied on this tagRendering?\nA list of labels. These are strings that are used for various purposes, e.g. to only include a subset of the tagRenderings when reusing a layer\nSpecial values:\n- \"hidden\": do not show this tagRendering. Useful in it is used by e.g. an accordion\n- \"description\": this label is a description used in the search"
},
{
"path": [
@ -1233,7 +1237,20 @@
],
"required": false,
"hints": {},
"type": "array",
"type": [
{
"type": "array",
"items": {
"type": "string"
}
},
{
"enum": [
true
],
"type": "boolean"
}
],
"description": "This tagRendering can introduce this builtin filter"
}
]

View file

@ -462,7 +462,20 @@
],
"required": false,
"hints": {},
"type": "array",
"type": [
{
"type": "array",
"items": {
"type": "string"
}
},
{
"enum": [
true
],
"type": "boolean"
}
],
"description": "This tagRendering can introduce this builtin filter"
}
]

File diff suppressed because one or more lines are too long