forked from MapComplete/MapComplete
Feature(distancePicker): revive geographical distance picker
This commit is contained in:
parent
e997653284
commit
5095bffc50
16 changed files with 245 additions and 107 deletions
|
@ -494,6 +494,7 @@
|
||||||
"id": "Cycle barrier type"
|
"id": "Cycle barrier type"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"id": "MaxWidth",
|
||||||
"render": {
|
"render": {
|
||||||
"en": "Maximum width: {maxwidth:physical} m",
|
"en": "Maximum width: {maxwidth:physical} m",
|
||||||
"nl": "Maximumbreedte: {maxwidth:physical} m",
|
"nl": "Maximumbreedte: {maxwidth:physical} m",
|
||||||
|
@ -532,12 +533,11 @@
|
||||||
"freeform": {
|
"freeform": {
|
||||||
"key": "maxwidth:physical",
|
"key": "maxwidth:physical",
|
||||||
"type": "distance",
|
"type": "distance",
|
||||||
"helperArgs": [
|
"helperArgs": {
|
||||||
"20",
|
"zoom": 20,
|
||||||
"map"
|
"background": "map"
|
||||||
]
|
}
|
||||||
},
|
}
|
||||||
"id": "MaxWidth"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"render": {
|
"render": {
|
||||||
|
@ -575,10 +575,10 @@
|
||||||
"freeform": {
|
"freeform": {
|
||||||
"key": "width:separation",
|
"key": "width:separation",
|
||||||
"type": "distance",
|
"type": "distance",
|
||||||
"helperArgs": [
|
"helperArgs": {
|
||||||
"21",
|
"zoom": 21,
|
||||||
"map"
|
"background": "map"
|
||||||
]
|
}
|
||||||
},
|
},
|
||||||
"id": "Space between barrier (cyclebarrier)"
|
"id": "Space between barrier (cyclebarrier)"
|
||||||
},
|
},
|
||||||
|
@ -618,10 +618,10 @@
|
||||||
"freeform": {
|
"freeform": {
|
||||||
"key": "width:opening",
|
"key": "width:opening",
|
||||||
"type": "distance",
|
"type": "distance",
|
||||||
"helperArgs": [
|
"helperArgs": {
|
||||||
"21",
|
"zoom": 21,
|
||||||
"map"
|
"background": "map"
|
||||||
]
|
}
|
||||||
},
|
},
|
||||||
"id": "Width of opening (cyclebarrier)"
|
"id": "Width of opening (cyclebarrier)"
|
||||||
},
|
},
|
||||||
|
@ -661,10 +661,10 @@
|
||||||
"freeform": {
|
"freeform": {
|
||||||
"key": "overlap",
|
"key": "overlap",
|
||||||
"type": "distance",
|
"type": "distance",
|
||||||
"helperArgs": [
|
"helperArgs": {
|
||||||
"21",
|
"zoom": 21,
|
||||||
"map"
|
"background": "map"
|
||||||
]
|
}
|
||||||
},
|
},
|
||||||
"id": "Overlap (cyclebarrier)"
|
"id": "Overlap (cyclebarrier)"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1283,10 +1283,10 @@
|
||||||
"freeform": {
|
"freeform": {
|
||||||
"key": "width",
|
"key": "width",
|
||||||
"type": "distance",
|
"type": "distance",
|
||||||
"helperArgs": [
|
"helperArgs": {
|
||||||
"20",
|
"zoom": 20,
|
||||||
"map"
|
"background": "map"
|
||||||
]
|
}
|
||||||
},
|
},
|
||||||
"question": {
|
"question": {
|
||||||
"en": "What is the carriage width of this road (in meters)?",
|
"en": "What is the carriage width of this road (in meters)?",
|
||||||
|
@ -1808,10 +1808,10 @@
|
||||||
"freeform": {
|
"freeform": {
|
||||||
"key": "cycleway:buffer",
|
"key": "cycleway:buffer",
|
||||||
"type": "distance",
|
"type": "distance",
|
||||||
"helperArgs": [
|
"helperArgs": {
|
||||||
"20",
|
"background": "map",
|
||||||
"map"
|
"zoom": 20
|
||||||
]
|
}
|
||||||
},
|
},
|
||||||
"id": "cycleways_and_roads-cycleway:buffer"
|
"id": "cycleways_and_roads-cycleway:buffer"
|
||||||
},
|
},
|
||||||
|
|
|
@ -69,10 +69,10 @@
|
||||||
"freeform": {
|
"freeform": {
|
||||||
"key": "width:carriageway",
|
"key": "width:carriageway",
|
||||||
"type": "distance",
|
"type": "distance",
|
||||||
"helperArgs": [
|
"helperArgs": {
|
||||||
21,
|
"zoom": 21,
|
||||||
"map"
|
"background": "map"
|
||||||
]
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -628,6 +628,11 @@
|
||||||
"recentThemes": "Recently visited themes",
|
"recentThemes": "Recently visited themes",
|
||||||
"title": "MapComplete"
|
"title": "MapComplete"
|
||||||
},
|
},
|
||||||
|
"input_helpers": {
|
||||||
|
"distance": {
|
||||||
|
"setFirst": "Measure from current location"
|
||||||
|
}
|
||||||
|
},
|
||||||
"inspector": {
|
"inspector": {
|
||||||
"aggregateView": "Aggregate",
|
"aggregateView": "Aggregate",
|
||||||
"answeredCountTimes": "Answered {count} times",
|
"answeredCountTimes": "Answered {count} times",
|
||||||
|
|
|
@ -32,6 +32,27 @@ export class AvailableRasterLayers {
|
||||||
|
|
||||||
public static readonly globalLayers: ReadonlyArray<RasterLayerPolygon> =
|
public static readonly globalLayers: ReadonlyArray<RasterLayerPolygon> =
|
||||||
AvailableRasterLayers.initGlobalLayers()
|
AvailableRasterLayers.initGlobalLayers()
|
||||||
|
public static bing = <RasterLayerPolygon>bingJson
|
||||||
|
public static readonly osmCartoProperties: RasterLayerProperties = {
|
||||||
|
id: "osm",
|
||||||
|
name: "OpenStreetMap",
|
||||||
|
url: "https://tile.openstreetmap.org/{z}/{x}/{y}.png",
|
||||||
|
attribution: {
|
||||||
|
text: "OpenStreetMap",
|
||||||
|
url: "https://openStreetMap.org/copyright"
|
||||||
|
},
|
||||||
|
best: true,
|
||||||
|
max_zoom: 19,
|
||||||
|
min_zoom: 0,
|
||||||
|
category: "osmbasedmap"
|
||||||
|
}
|
||||||
|
public static readonly osmCarto: RasterLayerPolygon = {
|
||||||
|
type: "Feature",
|
||||||
|
properties: AvailableRasterLayers.osmCartoProperties,
|
||||||
|
geometry: BBox.global.asGeometry()
|
||||||
|
}
|
||||||
|
|
||||||
|
public static allAvailableGlobalLayers = new Set([...AvailableRasterLayers.globalLayers, AvailableRasterLayers.osmCarto, AvailableRasterLayers.bing])
|
||||||
|
|
||||||
private static initGlobalLayers(): RasterLayerPolygon[] {
|
private static initGlobalLayers(): RasterLayerPolygon[] {
|
||||||
const gl: RasterLayerProperties[] = (globallayers["default"] ?? globallayers).layers.filter(
|
const gl: RasterLayerProperties[] = (globallayers["default"] ?? globallayers).layers.filter(
|
||||||
|
@ -54,26 +75,7 @@ export class AvailableRasterLayers {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bing = <RasterLayerPolygon>bingJson
|
|
||||||
public static readonly osmCartoProperties: RasterLayerProperties = {
|
|
||||||
id: "osm",
|
|
||||||
name: "OpenStreetMap",
|
|
||||||
url: "https://tile.openstreetmap.org/{z}/{x}/{y}.png",
|
|
||||||
attribution: {
|
|
||||||
text: "OpenStreetMap",
|
|
||||||
url: "https://openStreetMap.org/copyright",
|
|
||||||
},
|
|
||||||
best: true,
|
|
||||||
max_zoom: 19,
|
|
||||||
min_zoom: 0,
|
|
||||||
category: "osmbasedmap",
|
|
||||||
}
|
|
||||||
|
|
||||||
public static readonly osmCarto: RasterLayerPolygon = {
|
|
||||||
type: "Feature",
|
|
||||||
properties: AvailableRasterLayers.osmCartoProperties,
|
|
||||||
geometry: BBox.global.asGeometry(),
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The default background layer that any theme uses which does not explicitly define a background
|
* The default background layer that any theme uses which does not explicitly define a background
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
import { DesugaringStep } from "./Conversion"
|
import { DesugaringStep } from "./Conversion"
|
||||||
import { TagRenderingConfigJson } from "../Json/TagRenderingConfigJson"
|
import { TagRenderingConfigJson } from "../Json/TagRenderingConfigJson"
|
||||||
import { LayerConfigJson } from "../Json/LayerConfigJson"
|
import { LayerConfigJson } from "../Json/LayerConfigJson"
|
||||||
import {
|
import { MappingConfigJson, QuestionableTagRenderingConfigJson } from "../Json/QuestionableTagRenderingConfigJson"
|
||||||
MappingConfigJson,
|
|
||||||
QuestionableTagRenderingConfigJson,
|
|
||||||
} from "../Json/QuestionableTagRenderingConfigJson"
|
|
||||||
import { ConversionContext } from "./ConversionContext"
|
import { ConversionContext } from "./ConversionContext"
|
||||||
import { Translation } from "../../../UI/i18n/Translation"
|
import { Translation } from "../../../UI/i18n/Translation"
|
||||||
import { TagUtils } from "../../../Logic/Tags/TagUtils"
|
import { TagUtils } from "../../../Logic/Tags/TagUtils"
|
||||||
|
@ -216,6 +213,14 @@ export class MiscTagRenderingChecks extends DesugaringStep<TagRenderingConfigJso
|
||||||
"No need to explicitly set type to 'NSI', autodetected based on freeform type"
|
"No need to explicitly set type to 'NSI', autodetected based on freeform type"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (json.freeform["type"] && json.freeform["helperArgs"]) {
|
||||||
|
const validator = Validators.get(json.freeform["type"])
|
||||||
|
const feedback = validator?.validateArguments(json.freeform["helperArgs"])
|
||||||
|
if (feedback) {
|
||||||
|
context.enters("freeform", "helperArgs").err(feedback)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (json.render && json["question"] && json.freeform === undefined) {
|
if (json.render && json["question"] && json.freeform === undefined) {
|
||||||
context.err(
|
context.err(
|
||||||
|
|
100
src/UI/InputElement/Helpers/DistanceInput.svelte
Normal file
100
src/UI/InputElement/Helpers/DistanceInput.svelte
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
<script lang="ts">
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to quickly calculate a distance by dragging a map (and selecting start- and endpoints)
|
||||||
|
*/
|
||||||
|
|
||||||
|
import LocationInput from "./LocationInput.svelte"
|
||||||
|
import { UIEventSource, Store } from "../../../Logic/UIEventSource"
|
||||||
|
import type { MapProperties } from "../../../Models/MapProperties"
|
||||||
|
import ThemeViewState from "../../../Models/ThemeViewState"
|
||||||
|
import type { Feature } from "geojson"
|
||||||
|
import type { RasterLayerPolygon } from "../../../Models/RasterLayers"
|
||||||
|
import { RasterLayerUtils } from "../../../Models/RasterLayers"
|
||||||
|
import { eliCategory } from "../../../Models/RasterLayerProperties"
|
||||||
|
import { GeoOperations } from "../../../Logic/GeoOperations"
|
||||||
|
import OpenBackgroundSelectorButton from "../../BigComponents/OpenBackgroundSelectorButton.svelte"
|
||||||
|
import { Map as MlMap } from "maplibre-gl"
|
||||||
|
import StaticFeatureSource from "../../../Logic/FeatureSource/Sources/StaticFeatureSource"
|
||||||
|
import ShowDataLayer from "../../Map/ShowDataLayer"
|
||||||
|
import * as conflation from "../../../assets/generated/layers/conflation.json"
|
||||||
|
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"
|
||||||
|
import Translations from "../../i18n/Translations"
|
||||||
|
import Tr from "../../Base/Tr.svelte"
|
||||||
|
|
||||||
|
export let value: UIEventSource<number>
|
||||||
|
export let feature: Feature
|
||||||
|
export let args: { background?: string, zoom?: number }
|
||||||
|
export let state: ThemeViewState = undefined
|
||||||
|
export let map: UIEventSource<MlMap> = new UIEventSource<MlMap>(undefined)
|
||||||
|
|
||||||
|
let center = GeoOperations.centerpointCoordinates(feature)
|
||||||
|
export let initialCoordinate: { lon: number, lat: number } = { lon: center[0], lat: center[1] }
|
||||||
|
let mapLocation: UIEventSource<{ lon: number, lat: number }> = new UIEventSource(initialCoordinate)
|
||||||
|
let bg = args?.background
|
||||||
|
let rasterLayer = state?.mapProperties.rasterLayer
|
||||||
|
if (bg !== undefined) {
|
||||||
|
if (eliCategory.indexOf(bg) >= 0) {
|
||||||
|
const availableLayers = state.availableLayers.store.data
|
||||||
|
const startLayer: RasterLayerPolygon = RasterLayerUtils.SelectBestLayerAccordingTo(availableLayers, bg)
|
||||||
|
rasterLayer = new UIEventSource(startLayer)
|
||||||
|
state?.mapProperties.rasterLayer.addCallbackD(layer => rasterLayer.set(layer))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
let mapProperties: Partial<MapProperties> = {
|
||||||
|
rasterLayer: rasterLayer,
|
||||||
|
location: mapLocation,
|
||||||
|
zoom: new UIEventSource(args?.zoom ?? 18)
|
||||||
|
}
|
||||||
|
|
||||||
|
let start: UIEventSource<{ lon: number, lat: number }> = new UIEventSource(undefined)
|
||||||
|
|
||||||
|
function selectStart() {
|
||||||
|
start.set(mapLocation.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
let lengthFeature: Store<Feature[]> = start.map(start => {
|
||||||
|
if (!start) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
// A bit of a double task: calculate the actual value _and_ the map rendering
|
||||||
|
const end = mapLocation.data
|
||||||
|
const distance = GeoOperations.distanceBetween([start.lon, start.lat], [end.lon, end.lat])
|
||||||
|
value.set(distance.toFixed(2))
|
||||||
|
|
||||||
|
|
||||||
|
return <Feature[]>[
|
||||||
|
|
||||||
|
{
|
||||||
|
type: "Feature",
|
||||||
|
properties: {
|
||||||
|
id: "distance_line_" + distance,
|
||||||
|
distance: "" + distance
|
||||||
|
},
|
||||||
|
geometry: {
|
||||||
|
type: "LineString",
|
||||||
|
coordinates: [[start.lon, start.lat], [end.lon, end.lat]]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
}, [mapLocation])
|
||||||
|
|
||||||
|
new ShowDataLayer(map, {
|
||||||
|
layer: new LayerConfig(conflation),
|
||||||
|
features: new StaticFeatureSource(lengthFeature)
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="relative w-full h-64">
|
||||||
|
<LocationInput value={mapLocation} {mapProperties} {map} />
|
||||||
|
<div class="absolute bottom-0 left-0 p-4">
|
||||||
|
<OpenBackgroundSelectorButton {state} {map} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button class="primary" on:click={() => selectStart()}>
|
||||||
|
<Tr t={Translations.t.input_helpers.distance.setFirst} />
|
||||||
|
</button>
|
|
@ -19,12 +19,13 @@
|
||||||
import SlopeInput from "./Helpers/SlopeInput.svelte"
|
import SlopeInput from "./Helpers/SlopeInput.svelte"
|
||||||
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
||||||
import WikidataInputHelper from "./WikidataInputHelper.svelte"
|
import WikidataInputHelper from "./WikidataInputHelper.svelte"
|
||||||
|
import DistanceInput from "./Helpers/DistanceInput.svelte"
|
||||||
|
|
||||||
export let type: ValidatorType
|
export let type: ValidatorType
|
||||||
export let value: UIEventSource<string | object>
|
export let value: UIEventSource<string | object>
|
||||||
|
|
||||||
export let feature: Feature = undefined
|
export let feature: Feature = undefined
|
||||||
export let args: (string | number | boolean)[] = undefined
|
export let args: (string | number | boolean)[] | any = undefined
|
||||||
export let state: SpecialVisualizationState = undefined
|
export let state: SpecialVisualizationState = undefined
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -52,6 +53,8 @@
|
||||||
<SlopeInput {value} {feature} {state} />
|
<SlopeInput {value} {feature} {state} />
|
||||||
{:else if type === "wikidata"}
|
{:else if type === "wikidata"}
|
||||||
<WikidataInputHelper {value} {feature} {state} {args} />
|
<WikidataInputHelper {value} {feature} {state} {args} />
|
||||||
|
{:else if type === "distance"}
|
||||||
|
<DistanceInput {value} {state} {feature} {args} />
|
||||||
{:else}
|
{:else}
|
||||||
<slot name="fallback" />
|
<slot name="fallback" />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -27,7 +27,6 @@ export interface InputHelperProperties {
|
||||||
export default class InputHelpers {
|
export default class InputHelpers {
|
||||||
public static hideInputField: string[] = ["translation", "simple_tag", "tag"]
|
public static hideInputField: string[] = ["translation", "simple_tag", "tag"]
|
||||||
|
|
||||||
// noinspection JSUnusedLocalSymbols
|
|
||||||
/**
|
/**
|
||||||
* Constructs a mapProperties-object for the given properties.
|
* Constructs a mapProperties-object for the given properties.
|
||||||
* Assumes that the first helper-args contains the desired zoom-level
|
* Assumes that the first helper-args contains the desired zoom-level
|
||||||
|
|
|
@ -14,7 +14,8 @@ export abstract class Validator {
|
||||||
* */
|
* */
|
||||||
public readonly explanation: string
|
public readonly explanation: string
|
||||||
/**
|
/**
|
||||||
* What HTML-inputmode to use
|
* What HTML-inputmode to use?
|
||||||
|
* Note: some inputHelpers will completely hide the default text field. This is kept in InputHelpers.hideInputField
|
||||||
*/
|
*/
|
||||||
public readonly inputmode?:
|
public readonly inputmode?:
|
||||||
| "none"
|
| "none"
|
||||||
|
@ -81,4 +82,14 @@ export abstract class Validator {
|
||||||
public reformat(s: string, _?: () => string): string {
|
public reformat(s: string, _?: () => string): string {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks that the helper arguments are correct.
|
||||||
|
* This is called while preparing the themes.
|
||||||
|
* Returns 'undefined' if everything is fine, or feedback if an error is detected
|
||||||
|
* @param args the args for the input helper
|
||||||
|
*/
|
||||||
|
public validateArguments(args: string): undefined | string {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import TextValidator from "./Validators/TextValidator"
|
||||||
import DateValidator from "./Validators/DateValidator"
|
import DateValidator from "./Validators/DateValidator"
|
||||||
import NatValidator from "./Validators/NatValidator"
|
import NatValidator from "./Validators/NatValidator"
|
||||||
import IntValidator from "./Validators/IntValidator"
|
import IntValidator from "./Validators/IntValidator"
|
||||||
import LengthValidator from "./Validators/LengthValidator"
|
import DistanceValidator from "./Validators/DistanceValidator"
|
||||||
import DirectionValidator from "./Validators/DirectionValidator"
|
import DirectionValidator from "./Validators/DirectionValidator"
|
||||||
import WikidataValidator from "./Validators/WikidataValidator"
|
import WikidataValidator from "./Validators/WikidataValidator"
|
||||||
import PNatValidator from "./Validators/PNatValidator"
|
import PNatValidator from "./Validators/PNatValidator"
|
||||||
|
@ -71,7 +71,7 @@ export default class Validators {
|
||||||
new DateValidator(),
|
new DateValidator(),
|
||||||
new NatValidator(),
|
new NatValidator(),
|
||||||
new IntValidator(),
|
new IntValidator(),
|
||||||
new LengthValidator(),
|
new DistanceValidator(),
|
||||||
new DirectionValidator(),
|
new DirectionValidator(),
|
||||||
new WikidataValidator(),
|
new WikidataValidator(),
|
||||||
new PNatValidator(),
|
new PNatValidator(),
|
||||||
|
|
55
src/UI/InputElement/Validators/DistanceValidator.ts
Normal file
55
src/UI/InputElement/Validators/DistanceValidator.ts
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
import { Validator } from "../Validator"
|
||||||
|
import { Utils } from "../../../Utils"
|
||||||
|
import { eliCategory } from "../../../Models/RasterLayerProperties"
|
||||||
|
|
||||||
|
export default class DistanceValidator extends Validator {
|
||||||
|
private readonly docs: string = [
|
||||||
|
"#### Helper-arguments",
|
||||||
|
"Options are:",
|
||||||
|
["````json",
|
||||||
|
" \"background\": \"some_background_id or category, e.g. 'map'\"",
|
||||||
|
" \"zoom\": 20 # initial zoom level of the map",
|
||||||
|
"}",
|
||||||
|
"```"].join("\n")
|
||||||
|
].join("\n\n")
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super(
|
||||||
|
"distance",
|
||||||
|
"A geographical distance in meters (rounded at two points). Will give an extra minimap with a measurement tool. Arguments: [ zoomlevel, preferredBackgroundMapType (comma separated) ], e.g. `[\"21\", \"map,photo\"]",
|
||||||
|
"decimal"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
isValid = (str) => {
|
||||||
|
const t = Number(str)
|
||||||
|
return !isNaN(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
validateArguments(args: any): undefined | string {
|
||||||
|
if (args === undefined) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
if (typeof args !== "object" || Array.isArray(args)) {
|
||||||
|
return "Expected an object of type `{background?: string, zoom?: number}`"
|
||||||
|
}
|
||||||
|
|
||||||
|
const optionalKeys = ["background", "zoom"]
|
||||||
|
const keys = Object.keys(args).filter(k => optionalKeys.indexOf(k) < 0)
|
||||||
|
if (keys.length > 0) {
|
||||||
|
return "Unknown key " + keys.join("; ") + "; use " + optionalKeys.join("; ") + " instead"
|
||||||
|
}
|
||||||
|
const bg = args["background"]
|
||||||
|
if (bg && eliCategory.indexOf(bg) < 0) {
|
||||||
|
return "The given background layer is not a recognized ELI-type. Perhaps you meant one of " +
|
||||||
|
Utils.sortedByLevenshteinDistance(bg, eliCategory, x => x).slice(0, 5)
|
||||||
|
}
|
||||||
|
if (typeof args["zoom"] !== "number") {
|
||||||
|
return "zoom must be a number, got a " + typeof args["zoom"]
|
||||||
|
}
|
||||||
|
if (typeof args["zoom"] !== "number" || args["zoom"] <= 1 || args["zoom"] > 25) {
|
||||||
|
return "zoom must be a number between 2 and 25, got " + args["zoom"]
|
||||||
|
}
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,7 +4,7 @@ import { Validator } from "../Validator"
|
||||||
import { ValidatorType } from "../Validators"
|
import { ValidatorType } from "../Validators"
|
||||||
|
|
||||||
export default class FloatValidator extends Validator {
|
export default class FloatValidator extends Validator {
|
||||||
inputmode: "decimal" = "decimal"
|
inputmode: "decimal" = "decimal" as const
|
||||||
|
|
||||||
constructor(name?: ValidatorType, explanation?: string) {
|
constructor(name?: ValidatorType, explanation?: string) {
|
||||||
super(name ?? "float", explanation ?? "A decimal number", "decimal")
|
super(name ?? "float", explanation ?? "A decimal number", "decimal")
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
import { Validator } from "../Validator"
|
|
||||||
|
|
||||||
export default class LengthValidator extends Validator {
|
|
||||||
constructor() {
|
|
||||||
super(
|
|
||||||
"distance",
|
|
||||||
'A geographical distance in meters (rounded at two points). Will give an extra minimap with a measurement tool. Arguments: [ zoomlevel, preferredBackgroundMapType (comma separated) ], e.g. `["21", "map,photo"]',
|
|
||||||
"decimal"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
isValid = (str) => {
|
|
||||||
const t = Number(str)
|
|
||||||
return !isNaN(t)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,40 +1,14 @@
|
||||||
import Title from "../../Base/Title"
|
|
||||||
import Combine from "../../Base/Combine"
|
|
||||||
import { Validator } from "../Validator"
|
import { Validator } from "../Validator"
|
||||||
import Table from "../../Base/Table"
|
|
||||||
|
|
||||||
export default class NameSuggestionIndexValidator extends Validator {
|
export default class NameSuggestionIndexValidator extends Validator {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(
|
super(
|
||||||
"nsi",
|
"nsi",
|
||||||
new Combine([
|
"Gives a list of possible suggestions for a brand or operator tag. Note: this is detected automatically; there is no need to explicitly set this"
|
||||||
"Gives a list of possible suggestions for a brand or operator tag.",
|
|
||||||
new Title("Helper arguments"),
|
|
||||||
new Table(
|
|
||||||
["name", "doc"],
|
|
||||||
[
|
|
||||||
[
|
|
||||||
"options",
|
|
||||||
new Combine([
|
|
||||||
"A JSON-object of type `{ main: string, key: string }`. ",
|
|
||||||
new Table(
|
|
||||||
["subarg", "doc"],
|
|
||||||
[
|
|
||||||
[
|
|
||||||
"main",
|
|
||||||
"The main tag to give suggestions for, e.g. `amenity=restaurant`.",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"addExtraTags",
|
|
||||||
"Extra tags to add to the suggestions, e.g. `nobrand=yes`.",
|
|
||||||
],
|
|
||||||
]
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
],
|
|
||||||
]
|
|
||||||
),
|
|
||||||
])
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
validateArguments(args: string): string | undefined {
|
||||||
|
return "No arguments needed"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -352,7 +352,7 @@ class LineRenderingLayer {
|
||||||
// After waiting 'till the map has loaded, the data might have changed already
|
// After waiting 'till the map has loaded, the data might have changed already
|
||||||
// As such, we only now read the features from the featureSource and compare with the previously set data
|
// As such, we only now read the features from the featureSource and compare with the previously set data
|
||||||
const features = featureSource.data
|
const features = featureSource.data
|
||||||
if (features.length === 0) {
|
if (!features || features.length === 0) {
|
||||||
// This is a very ugly workaround for https://source.mapcomplete.org/MapComplete/MapComplete/issues/2312,
|
// This is a very ugly workaround for https://source.mapcomplete.org/MapComplete/MapComplete/issues/2312,
|
||||||
// but I couldn't find the root cause
|
// but I couldn't find the root cause
|
||||||
return
|
return
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue