Merge master

This commit is contained in:
Pieter Vander Vennet 2023-10-14 23:29:16 +02:00
commit c358a4c415
72 changed files with 5113 additions and 1023 deletions

View file

@ -1,174 +1,118 @@
import { Feature, Polygon } from "geojson";
import * as editorlayerindex from "../assets/editor-layer-index.json";
import * as globallayers from "../assets/global-raster-layers.json";
import { BBox } from "../Logic/BBox";
import { Store, Stores } from "../Logic/UIEventSource";
import { GeoOperations } from "../Logic/GeoOperations";
import { RasterLayerProperties } from "./RasterLayerProperties";
import { Feature, Polygon } from "geojson"
import * as editorlayerindex from "../assets/editor-layer-index.json"
import * as globallayers from "../assets/global-raster-layers.json"
import { BBox } from "../Logic/BBox"
import { Store, Stores } from "../Logic/UIEventSource"
import { GeoOperations } from "../Logic/GeoOperations"
import { RasterLayerProperties } from "./RasterLayerProperties"
export class AvailableRasterLayers {
public static EditorLayerIndex: (Feature<Polygon, EditorLayerIndexProperties> &
RasterLayerPolygon)[] = <any>editorlayerindex.features;
public static globalLayers: RasterLayerPolygon[] = globallayers.layers.map(
(properties) =>
<RasterLayerPolygon>{
public static EditorLayerIndex: (Feature<Polygon, EditorLayerIndexProperties> &
RasterLayerPolygon)[] = <any>editorlayerindex.features
public static globalLayers: RasterLayerPolygon[] = globallayers.layers.map(
(properties) =>
<RasterLayerPolygon>{
type: "Feature",
properties,
geometry: BBox.global.asGeometry(),
}
)
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,
geometry: BBox.global.asGeometry()
}
);
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"
};
properties: AvailableRasterLayers.osmCartoProperties,
geometry: BBox.global.asGeometry(),
}
public static readonly osmCarto: RasterLayerPolygon = {
type: "Feature",
properties: AvailableRasterLayers.osmCartoProperties,
geometry: BBox.global.asGeometry()
};
public static readonly maptilerDefaultLayer: RasterLayerPolygon = {
type: "Feature",
properties: {
name: "MapTiler",
url: "https://api.maptiler.com/maps/15cc8f61-0353-4be6-b8da-13daea5f7432/style.json?key=GvoVAJgu46I5rZapJuAy",
category: "osmbasedmap",
id: "maptiler",
type: "vector",
attribution: {
text: "Maptiler",
url: "https://www.maptiler.com/copyright/",
},
},
geometry: BBox.global.asGeometry(),
}
public static readonly maptilerDefaultLayer: RasterLayerPolygon = {
type: "Feature",
properties: {
name: "MapTiler",
url: "https://api.maptiler.com/maps/15cc8f61-0353-4be6-b8da-13daea5f7432/style.json?key=GvoVAJgu46I5rZapJuAy",
category: "osmbasedmap",
id: "maptiler",
type: "vector",
attribution: {
text: "Maptiler",
url: "https://www.maptiler.com/copyright/"
}
},
geometry: BBox.global.asGeometry()
};
public static readonly maptilerCarto: RasterLayerPolygon = {
type: "Feature",
properties: {
name: "MapTiler Carto",
url: "https://api.maptiler.com/maps/openstreetmap/style.json?key=GvoVAJgu46I5rZapJuAy",
category: "osmbasedmap",
id: "maptiler.carto",
type: "vector",
attribution: {
text: "Maptiler",
url: "https://www.maptiler.com/copyright/"
}
},
geometry: BBox.global.asGeometry()
};
public static readonly maptilerBackdrop: RasterLayerPolygon = {
type: "Feature",
properties: {
name: "MapTiler Backdrop",
url: "https://api.maptiler.com/maps/backdrop/style.json?key=GvoVAJgu46I5rZapJuAy",
category: "osmbasedmap",
id: "maptiler.backdrop",
type: "vector",
attribution: {
text: "Maptiler",
url: "https://www.maptiler.com/copyright/"
}
},
geometry: BBox.global.asGeometry()
};
public static readonly americana: RasterLayerPolygon = {
type: "Feature",
properties: {
name: "Americana",
url: "https://zelonewolf.github.io/openstreetmap-americana/style.json",
category: "osmbasedmap",
id: "americana",
type: "vector",
attribution: {
text: "Americana",
url: "https://github.com/ZeLonewolf/openstreetmap-americana/"
}
},
geometry: BBox.global.asGeometry()
};
public static readonly vectorLayers = [
AvailableRasterLayers.maptilerDefaultLayer,
AvailableRasterLayers.osmCarto,
AvailableRasterLayers.maptilerCarto,
AvailableRasterLayers.maptilerBackdrop,
AvailableRasterLayers.americana
];
public static layersAvailableAt(
location: Store<{ lon: number; lat: number }>
): Store<RasterLayerPolygon[]> {
const availableLayersBboxes = Stores.ListStabilized(
location.mapD((loc) => {
const lonlat: [number, number] = [loc.lon, loc.lat];
return AvailableRasterLayers.EditorLayerIndex.filter((eliPolygon) =>
BBox.get(eliPolygon).contains(lonlat)
);
})
);
const available = Stores.ListStabilized(
availableLayersBboxes.map((eliPolygons) => {
const loc = location.data;
const lonlat: [number, number] = [loc.lon, loc.lat];
const matching: RasterLayerPolygon[] = eliPolygons.filter((eliPolygon) => {
if (eliPolygon.geometry === null) {
return true; // global ELI-layer
}
return GeoOperations.inside(lonlat, eliPolygon);
});
matching.push(...AvailableRasterLayers.globalLayers);
matching.unshift(...AvailableRasterLayers.vectorLayers);
return matching;
})
);
return available;
}
public static layersAvailableAt(
location: Store<{ lon: number; lat: number }>
): Store<RasterLayerPolygon[]> {
const availableLayersBboxes = Stores.ListStabilized(
location.mapD((loc) => {
const lonlat: [number, number] = [loc.lon, loc.lat]
return AvailableRasterLayers.EditorLayerIndex.filter((eliPolygon) =>
BBox.get(eliPolygon).contains(lonlat)
)
})
)
return Stores.ListStabilized(
availableLayersBboxes.map((eliPolygons) => {
const loc = location.data
const lonlat: [number, number] = [loc.lon, loc.lat]
const matching: RasterLayerPolygon[] = eliPolygons.filter((eliPolygon) => {
if (eliPolygon.geometry === null) {
return true // global ELI-layer
}
return GeoOperations.inside(lonlat, eliPolygon)
})
matching.push(AvailableRasterLayers.maptilerDefaultLayer)
matching.push(...AvailableRasterLayers.globalLayers)
return matching
})
)
}
}
export class RasterLayerUtils {
/**
* Selects, from the given list of available rasterLayerPolygons, a rasterLayer.
* This rasterlayer will be of type 'preferredCategory' and will be of the 'best'-layer (if available).
* Returns 'undefined' if no such layer is available
* @param available
* @param preferredCategory
* @param ignoreLayer
*/
public static SelectBestLayerAccordingTo(
available: RasterLayerPolygon[],
preferredCategory: string,
ignoreLayer?: RasterLayerPolygon
): RasterLayerPolygon {
let secondBest: RasterLayerPolygon = undefined;
for (const rasterLayer of available) {
if (rasterLayer === ignoreLayer) {
continue;
}
const p = rasterLayer.properties;
if (p.category === preferredCategory) {
if (p.best) {
return rasterLayer;
/**
* Selects, from the given list of available rasterLayerPolygons, a rasterLayer.
* This rasterlayer will be of type 'preferredCategory' and will be of the 'best'-layer (if available).
* Returns 'undefined' if no such layer is available
* @param available
* @param preferredCategory
* @param ignoreLayer
*/
public static SelectBestLayerAccordingTo(
available: RasterLayerPolygon[],
preferredCategory: string,
ignoreLayer?: RasterLayerPolygon
): RasterLayerPolygon {
let secondBest: RasterLayerPolygon = undefined
for (const rasterLayer of available) {
if (rasterLayer === ignoreLayer) {
continue
}
const p = rasterLayer.properties
if (p.category === preferredCategory) {
if (p.best) {
return rasterLayer
}
if (!secondBest) {
secondBest = rasterLayer
}
}
}
if (!secondBest) {
secondBest = rasterLayer;
}
}
return secondBest
}
return secondBest;
}
}
export type RasterLayerPolygon = Feature<Polygon, RasterLayerProperties>
@ -180,165 +124,165 @@ export type RasterLayerPolygon = Feature<Polygon, RasterLayerProperties>
* which was then converted with http://borischerny.com/json-schema-to-typescript-browser/
*/
export interface EditorLayerIndexProperties extends RasterLayerProperties {
/**
* The name of the imagery source
*/
readonly name: string;
/**
* Whether the imagery name should be translated
*/
readonly i18n?: boolean;
readonly type:
| "tms"
| "wms"
| "bing"
| "scanex"
| "wms_endpoint"
| "wmts"
| "vector"; /* Vector is not actually part of the ELI-spec, we add it for vector layers */
/**
* A rough categorisation of different types of layers. See https://github.com/osmlab/editor-layer-index/blob/gh-pages/CONTRIBUTING.md#categories for a description of the individual categories.
*/
readonly category?:
| "photo"
| "map"
| "historicmap"
| "osmbasedmap"
| "historicphoto"
| "qa"
| "elevation"
| "other";
/**
* A URL template for imagery tiles
*/
readonly url: string;
readonly min_zoom?: number;
readonly max_zoom?: number;
/**
* explicit/implicit permission by the owner for use in OSM
*/
readonly permission_osm?: "explicit" | "implicit" | "no";
/**
* A URL for the license or permissions for the imagery
*/
readonly license_url?: string;
/**
* A URL for the privacy policy of the operator or false if there is no existing privacy policy for tis imagery.
*/
readonly privacy_policy_url?: string | boolean;
/**
* A unique identifier for the source; used in imagery_used changeset tag
*/
readonly id: string;
/**
* A short English-language description of the source
*/
readonly description?: string;
/**
* The ISO 3166-1 alpha-2 two letter country code in upper case. Use ZZ for unknown or multiple.
*/
readonly country_code?: string;
/**
* Whether this imagery should be shown in the default world-wide menu
*/
readonly default?: boolean;
/**
* Whether this imagery is the best source for the region
*/
readonly best?: boolean;
/**
* The age of the oldest imagery or data in the source, as an RFC3339 date or leading portion of one
*/
readonly start_date?: string;
/**
* The age of the newest imagery or data in the source, as an RFC3339 date or leading portion of one
*/
readonly end_date?: string;
/**
* HTTP header to check for information if the tile is invalid
*/
readonly no_tile_header?: {
/**
* This interface was referenced by `undefined`'s JSON-Schema definition
* via the `patternProperty` "^.*$".
* The name of the imagery source
*/
[k: string]: string[] | null
};
/**
* 'true' if tiles are transparent and can be overlaid on another source
*/
readonly overlay?: boolean & string;
readonly available_projections?: string[];
readonly attribution?: {
readonly url?: string
readonly text?: string
readonly html?: string
readonly required?: boolean
};
/**
* A URL for an image, that can be displayed in the list of imagery layers next to the name
*/
readonly icon?: string;
/**
* A link to an EULA text that has to be accepted by the user, before the imagery source is added. Can contain {lang} to be replaced by a current user language wiki code (like FR:) or an empty string for the default English text.
*/
readonly eula?: string;
/**
* A URL for an image, that is displayed in the mapview for attribution
*/
readonly "logo-image"?: string;
/**
* Customized text for the terms of use link (default is "Background Terms of Use")
*/
readonly "terms-of-use-text"?: string;
/**
* Specify a checksum for tiles, which aren't real tiles. `type` is the digest type and can be MD5, SHA-1, SHA-256, SHA-384 and SHA-512, value is the hex encoded checksum in lower case. To create a checksum save the tile as file and upload it to e.g. https://defuse.ca/checksums.htm.
*/
readonly "no-tile-checksum"?: string;
/**
* header-name attribute specifies a header returned by tile server, that will be shown as `metadata-key` attribute in Show Tile Info dialog
*/
readonly "metadata-header"?: string;
/**
* Set to `true` if imagery source is properly aligned and does not need imagery offset adjustments. This is used for OSM based sources too.
*/
readonly "valid-georeference"?: boolean;
/**
* Size of individual tiles delivered by a TMS service
*/
readonly "tile-size"?: number;
/**
* Whether tiles status can be accessed by appending /status to the tile URL and can be submitted for re-rendering by appending /dirty.
*/
readonly "mod-tile-features"?: string;
/**
* HTTP headers to be sent to server. It has two attributes header-name and header-value. May be specified multiple times.
*/
readonly "custom-http-headers"?: {
readonly "header-name"?: string
readonly "header-value"?: string
};
/**
* Default layer to open (when using WMS_ENDPOINT type). Contains list of layer tag with two attributes - name and style, e.g. `"default-layers": ["layer": { name="Basisdata_NP_Basiskart_JanMayen_WMTS_25829" "style":"default" } ]` (not allowed in `mirror` attribute)
*/
readonly "default-layers"?: {
layer?: {
"layer-name"?: string
"layer-style"?: string
[k: string]: unknown
readonly name: string
/**
* Whether the imagery name should be translated
*/
readonly i18n?: boolean
readonly type:
| "tms"
| "wms"
| "bing"
| "scanex"
| "wms_endpoint"
| "wmts"
| "vector" /* Vector is not actually part of the ELI-spec, we add it for vector layers */
/**
* A rough categorisation of different types of layers. See https://github.com/osmlab/editor-layer-index/blob/gh-pages/CONTRIBUTING.md#categories for a description of the individual categories.
*/
readonly category?:
| "photo"
| "map"
| "historicmap"
| "osmbasedmap"
| "historicphoto"
| "qa"
| "elevation"
| "other"
/**
* A URL template for imagery tiles
*/
readonly url: string
readonly min_zoom?: number
readonly max_zoom?: number
/**
* explicit/implicit permission by the owner for use in OSM
*/
readonly permission_osm?: "explicit" | "implicit" | "no"
/**
* A URL for the license or permissions for the imagery
*/
readonly license_url?: string
/**
* A URL for the privacy policy of the operator or false if there is no existing privacy policy for tis imagery.
*/
readonly privacy_policy_url?: string | boolean
/**
* A unique identifier for the source; used in imagery_used changeset tag
*/
readonly id: string
/**
* A short English-language description of the source
*/
readonly description?: string
/**
* The ISO 3166-1 alpha-2 two letter country code in upper case. Use ZZ for unknown or multiple.
*/
readonly country_code?: string
/**
* Whether this imagery should be shown in the default world-wide menu
*/
readonly default?: boolean
/**
* Whether this imagery is the best source for the region
*/
readonly best?: boolean
/**
* The age of the oldest imagery or data in the source, as an RFC3339 date or leading portion of one
*/
readonly start_date?: string
/**
* The age of the newest imagery or data in the source, as an RFC3339 date or leading portion of one
*/
readonly end_date?: string
/**
* HTTP header to check for information if the tile is invalid
*/
readonly no_tile_header?: {
/**
* This interface was referenced by `undefined`'s JSON-Schema definition
* via the `patternProperty` "^.*$".
*/
[k: string]: string[] | null
}
[k: string]: unknown
}[];
/**
* format to use when connecting tile server (when using WMS_ENDPOINT type)
*/
readonly format?: string;
/**
* If `true` transparent tiles will be requested from WMS server
*/
readonly transparent?: boolean & string;
/**
* minimum expiry time for tiles in seconds. The larger the value, the longer entry in cache will be considered valid
*/
readonly "minimum-tile-expire"?: number;
/**
* 'true' if tiles are transparent and can be overlaid on another source
*/
readonly overlay?: boolean & string
readonly available_projections?: string[]
readonly attribution?: {
readonly url?: string
readonly text?: string
readonly html?: string
readonly required?: boolean
}
/**
* A URL for an image, that can be displayed in the list of imagery layers next to the name
*/
readonly icon?: string
/**
* A link to an EULA text that has to be accepted by the user, before the imagery source is added. Can contain {lang} to be replaced by a current user language wiki code (like FR:) or an empty string for the default English text.
*/
readonly eula?: string
/**
* A URL for an image, that is displayed in the mapview for attribution
*/
readonly "logo-image"?: string
/**
* Customized text for the terms of use link (default is "Background Terms of Use")
*/
readonly "terms-of-use-text"?: string
/**
* Specify a checksum for tiles, which aren't real tiles. `type` is the digest type and can be MD5, SHA-1, SHA-256, SHA-384 and SHA-512, value is the hex encoded checksum in lower case. To create a checksum save the tile as file and upload it to e.g. https://defuse.ca/checksums.htm.
*/
readonly "no-tile-checksum"?: string
/**
* header-name attribute specifies a header returned by tile server, that will be shown as `metadata-key` attribute in Show Tile Info dialog
*/
readonly "metadata-header"?: string
/**
* Set to `true` if imagery source is properly aligned and does not need imagery offset adjustments. This is used for OSM based sources too.
*/
readonly "valid-georeference"?: boolean
/**
* Size of individual tiles delivered by a TMS service
*/
readonly "tile-size"?: number
/**
* Whether tiles status can be accessed by appending /status to the tile URL and can be submitted for re-rendering by appending /dirty.
*/
readonly "mod-tile-features"?: string
/**
* HTTP headers to be sent to server. It has two attributes header-name and header-value. May be specified multiple times.
*/
readonly "custom-http-headers"?: {
readonly "header-name"?: string
readonly "header-value"?: string
}
/**
* Default layer to open (when using WMS_ENDPOINT type). Contains list of layer tag with two attributes - name and style, e.g. `"default-layers": ["layer": { name="Basisdata_NP_Basiskart_JanMayen_WMTS_25829" "style":"default" } ]` (not allowed in `mirror` attribute)
*/
readonly "default-layers"?: {
layer?: {
"layer-name"?: string
"layer-style"?: string
[k: string]: unknown
}
[k: string]: unknown
}[]
/**
* format to use when connecting tile server (when using WMS_ENDPOINT type)
*/
readonly format?: string
/**
* If `true` transparent tiles will be requested from WMS server
*/
readonly transparent?: boolean & string
/**
* minimum expiry time for tiles in seconds. The larger the value, the longer entry in cache will be considered valid
*/
readonly "minimum-tile-expire"?: number
}

View file

@ -1,98 +0,0 @@
import { InputElement } from "./InputElement"
import { UIEventSource } from "../../Logic/UIEventSource"
import { Utils } from "../../Utils"
import BaseUIElement from "../BaseUIElement"
import InputElementMap from "./InputElementMap"
import Translations from "../i18n/Translations"
/**
* @deprecated
*/
export class CheckBox extends InputElementMap<number[], boolean> {
constructor(el: BaseUIElement | string, defaultValue?: boolean) {
super(
new CheckBoxes([Translations.W(el)]),
(x0, x1) => x0 === x1,
(t) => t.length > 0,
(x) => (x ? [0] : [])
)
if (defaultValue !== undefined) {
this.GetValue().setData(defaultValue)
}
}
}
/**
* A list of individual checkboxes
* The value will contain the indexes of the selected checkboxes
*/
export default class CheckBoxes extends InputElement<number[]> {
private static _nextId = 0
private readonly value: UIEventSource<number[]>
private readonly _elements: BaseUIElement[]
constructor(elements: BaseUIElement[], value = new UIEventSource<number[]>([])) {
super()
this.value = value
this._elements = Utils.NoNull(elements)
this.SetClass("flex flex-col")
}
IsValid(ts: number[]): boolean {
return ts !== undefined
}
GetValue(): UIEventSource<number[]> {
return this.value
}
protected InnerConstructElement(): HTMLElement {
const formTag = document.createElement("form")
const value = this.value
const elements = this._elements
for (let i = 0; i < elements.length; i++) {
let inputI = elements[i]
const input = document.createElement("input")
const id = CheckBoxes._nextId
CheckBoxes._nextId++
input.id = "checkbox" + id
input.type = "checkbox"
input.classList.add("p-1", "cursor-pointer", "m-3", "pl-3", "mr-0")
const label = document.createElement("label")
label.htmlFor = input.id
label.appendChild(input)
label.appendChild(inputI.ConstructElement())
label.classList.add("block", "w-full", "p-2", "cursor-pointer")
formTag.appendChild(label)
value.addCallbackAndRunD((selectedValues) => {
input.checked = selectedValues.indexOf(i) >= 0
if (input.checked) {
label.classList.add("checked")
} else {
label.classList.remove("checked")
}
})
input.onchange = () => {
// Index = index in the list of already checked items
const index = value.data.indexOf(i)
if (input.checked && index < 0) {
value.data.push(i)
value.ping()
} else if (index >= 0) {
value.data.splice(index, 1)
value.ping()
}
}
}
return formTag
}
}

View file

@ -1,61 +0,0 @@
import { InputElement } from "./InputElement"
import { Store, UIEventSource } from "../../Logic/UIEventSource"
/**
* @deprecated
*/
export default class InputElementMap<T, X> extends InputElement<X> {
private readonly _inputElement: InputElement<T>
private isSame: (x0: X, x1: X) => boolean
private readonly fromX: (x: X) => T
private readonly toX: (t: T) => X
private readonly _value: UIEventSource<X>
constructor(
inputElement: InputElement<T>,
isSame: (x0: X, x1: X) => boolean,
toX: (t: T) => X,
fromX: (x: X) => T,
extraSources: Store<any>[] = []
) {
super()
this.isSame = isSame
this.fromX = fromX
this.toX = toX
this._inputElement = inputElement
const self = this
this._value = inputElement.GetValue().sync(
(t) => {
const newX = toX(t)
const currentX = self.GetValue()?.data
if (isSame(currentX, newX)) {
return currentX
}
return newX
},
extraSources,
(x) => {
return fromX(x)
}
)
}
GetValue(): UIEventSource<X> {
return this._value
}
IsValid(x: X): boolean {
if (x === undefined) {
return false
}
const t = this.fromX(x)
if (t === undefined) {
return false
}
return this._inputElement.IsValid(t)
}
protected InnerConstructElement(): HTMLElement {
return this._inputElement.ConstructElement()
}
}

View file

@ -422,6 +422,9 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
map.addSource(background.id, MapLibreAdaptor.prepareWmsSource(background))
}
if (!map.getLayer(background.id)) {
addLayerBeforeId ??= map
.getStyle()
.layers.find((l) => l.id.startsWith("mapcomplete_"))?.id
console.log(
"Adding background layer",
background.id,

View file

@ -513,7 +513,7 @@ export default class ShowDataLayer {
const l = new LineRenderingLayer(
map,
features,
this._options.layer.id + "_linerendering_" + i,
"mapcomplete_" + this._options.layer.id + "_linerendering_" + i,
lineRenderingConfig,
doShowLayer,
fetchStore,

View file

@ -74,7 +74,7 @@ import NearbyImagesSearch from "../Logic/Web/NearbyImagesSearch"
import AllReviews from "./Reviews/AllReviews.svelte"
import StarsBarIcon from "./Reviews/StarsBarIcon.svelte"
import ReviewForm from "./Reviews/ReviewForm.svelte"
import Questionbox from "./Popup/TagRendering/Questionbox.svelte";
import Questionbox from "./Popup/TagRendering/Questionbox.svelte"
class NearbyImageVis implements SpecialVisualization {
// Class must be in SpecialVisualisations due to weird cyclical import that breaks the tests
@ -181,7 +181,6 @@ class StealViz implements SpecialVisualization {
}
}
/**
* Thin wrapper around QuestionBox.svelte to include it into the special Visualisations
*/
@ -189,7 +188,7 @@ export class QuestionViz implements SpecialVisualization {
funcName = "questions"
needsUrls = []
docs =
"The special element which shows the questions which are unkown. Added by default if not yet there"
"The special element which shows the questions which are unkown. Added by default if not yet there"
args = [
{
name: "labels",
@ -202,20 +201,20 @@ export class QuestionViz implements SpecialVisualization {
]
constr(
state: SpecialVisualizationState,
tags: UIEventSource<Record<string, string>>,
args: string[],
feature: Feature,
layer: LayerConfig
state: SpecialVisualizationState,
tags: UIEventSource<Record<string, string>>,
args: string[],
feature: Feature,
layer: LayerConfig
): BaseUIElement {
const labels = args[0]
?.split(";")
?.map((s) => s.trim())
?.filter((s) => s !== "")
?.split(";")
?.map((s) => s.trim())
?.filter((s) => s !== "")
const blacklist = args[1]
?.split(";")
?.map((s) => s.trim())
?.filter((s) => s !== "")
?.split(";")
?.map((s) => s.trim())
?.filter((s) => s !== "")
return new SvelteUIElement(Questionbox, {
layer,
tags,

View file

@ -1098,7 +1098,16 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
}
return { content: data }
} catch (e) {
console.error("Could not parse ", data, "due to", e, "\n", e.stack)
console.error(
"Could not parse the response of",
url,
"which contains",
data,
"due to",
e,
"\n",
e.stack
)
return { error: "malformed", url }
}
}

View file

@ -1,7 +1,7 @@
{
"contributors": [
{
"commits": 6085,
"commits": 6092,
"contributor": "Pieter Vander Vennet"
},
{

View file

@ -1,97 +1,169 @@
{
"layers": [
{
"id": "Stamen.TonerLite",
"name": "Toner Lite (by Stamen)",
"url": "https://stamen-tiles-{switch:a,b,c,d}.a.ssl.fastly.net/toner-lite/{z}/{x}/{y}.png",
"name": "Americana",
"url": "https://zelonewolf.github.io/openstreetmap-americana/style.json",
"category": "osmbasedmap",
"id": "americana",
"type": "vector",
"attribution": {
"html": "Map tiles by <a href=\"http://stamen.com\">Stamen Design</a>, <a href=\"http://creativecommons.org/licenses/by/3.0\">CC BY 3.0</a> &mdash; Map data {attribution.OpenStreetMap}"
},
"min_zoom": 0,
"max_zoom": 20
"text": "Americana",
"url": "https://github.com/ZeLonewolf/openstreetmap-americana/"
}
},
{
"id": "Stamen.TonerBackground",
"name": "Toner Background - no labels (by Stamen)",
"name": "MapTiler Backdrop",
"url": "https://api.maptiler.com/maps/backdrop/style.json?key=GvoVAJgu46I5rZapJuAy",
"category": "osmbasedmap",
"url": "https://stamen-tiles-{switch:a,b,c,d}.a.ssl.fastly.net/toner-background/{z}/{x}/{y}.png",
"id": "maptiler.backdrop",
"type": "vector",
"attribution": {
"html": "Map tiles by <a href=\"http://stamen.com\">Stamen Design</a>, <a href=\"http://creativecommons.org/licenses/by/3.0\">CC BY 3.0</a> &mdash; Map data {attribution.OpenStreetMap}"
},
"min_zoom": 0,
"max_zoom": 20
"text": "Maptiler",
"url": "https://www.maptiler.com/copyright/"
}
},
{
"id": "Stamen.Watercolor",
"name": "Watercolor (by Stamen)",
"name": "MapTiler Carto",
"url": "https://api.maptiler.com/maps/openstreetmap/style.json?key=GvoVAJgu46I5rZapJuAy",
"category": "osmbasedmap",
"url": "https://stamen-tiles-{switch:a,b,c,d}.a.ssl.fastly.net/watercolor/{z}/{x}/{y}.png",
"id": "maptiler.carto",
"type": "vector",
"attribution": {
"html": "Map tiles by <a href=\"http://stamen.com\">Stamen Design</a>, <a href=\"http://creativecommons.org/licenses/by/3.0\">CC BY 3.0</a> &mdash; Map data {attribution.OpenStreetMap}"
},
"min_zoom": 0,
"max_zoom": 20
"text": "Maptiler",
"url": "https://www.maptiler.com/copyright/"
}
},
{
"id": "CartoDB.Positron",
"name": "Positron (by CartoDB)",
"url": "https://{switch:a,b,c,d}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}@2x.png",
"name": "Alidade Smooth",
"url": "https://tiles-eu.stadiamaps.com/styles/alidade_smooth.json?key=14c5a900-7137-42f7-9cb9-fff0f4696f75",
"category": "osmbasedmap",
"id": "alidade.smooth",
"type": "vector",
"attribution": {
"html": "<a href=\"https://carto.com/attributions\">CARTO</a>"
},
"max_zoom": 20,
"category": "osmbasedmap"
"text": "Alidade",
"url": "https://stadiamaps.com/"
}
},
{
"id": "CartoDB.PositronNoLabels",
"name": "Positron - no labels (by CartoDB)",
"url": "https://{switch:a,b,c,d}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}.png",
"name": "Alidade Smooth Dark",
"url": "https://tiles-eu.stadiamaps.com/styles/alidade_smooth_dark.json?key=14c5a900-7137-42f7-9cb9-fff0f4696f75",
"category": "osmbasedmap",
"id": "alidade.smooth_dark",
"type": "vector",
"attribution": {
"html": "<a href=\"https://carto.com/attributions\">CARTO</a>"
},
"max_zoom": 20
"text": "Alidade/Stadiamaps",
"url": "https://stadiamaps.com/"
}
},
{
"id": "CartoDB.Voyager",
"name": "Voyager (by CartoDB)",
"url": "https://{switch:a,b,c,d}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}.png",
"name": "Stamen Terrain",
"url": "https://tiles-eu.stadiamaps.com/styles/stamen_terrain.json?key=14c5a900-7137-42f7-9cb9-fff0f4696f75",
"category": "osmbasedmap",
"id": "stamen.terrain",
"type": "vector",
"attribution": {
"html": "<a href=\"https://carto.com/attributions\">CARTO</a>"
},
"max_zoom": 20
"text": "Stamen/Stadiamaps",
"url": "https://stadiamaps.com/"
}
},
{
"id": "CartoDB.VoyagerNoLabels",
"name": "Voyager - no labels (by CartoDB)",
"url": "https://{switch:a,b,c,d}.basemaps.cartocdn.com/rastertiles/voyager_nolabels/{z}/{x}/{y}.png",
"name": "Stamen Toner",
"url": "https://tiles-eu.stadiamaps.com/styles/stamen_toner.json?key=14c5a900-7137-42f7-9cb9-fff0f4696f75",
"category": "osmbasedmap",
"id": "stamen.toner",
"type": "vector",
"attribution": {
"html": "<a href=\"https://carto.com/attributions\">CARTO</a>"
},
"max_zoom": 20
"text": "Stamen/Stadiamaps",
"url": "https://stadiamaps.com/"
}
}, {
"name": "Stamen Watercolor",
"url": "https://tiles.stadiamaps.com/tiles/stamen_watercolor/{z}/{x}/{y}.jpg?key=14c5a900-7137-42f7-9cb9-fff0f4696f75",
"category": "osmbasedmap",
"id": "stamen.watercolor",
"attribution": {
"text": "Stamen/Stadiamaps",
"url": "https://stadiamaps.com/"
}
},
{
"id": "CartoDB.DarkMatter",
"name": "Dark Matter (by CartoDB)",
"url": "https://{switch:a,b,c,d}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png",
"url": "https://tiles-eu.stadiamaps.com/styles/osm_bright.json",
"name": "StadiaMaps OSM Bright",
"category": "osmbasedmap",
"id": "stadia.bright",
"type": "vector",
"attribution": {
"html": "<a href=\"https://carto.com/attributions\">CARTO</a>"
},
"max_zoom": 20
"text": "Stadiamaps",
"url": "https://stadiamaps.com/"
}
},
{
"id": "CartoDB.DarkMatterNoLabels",
"name": "Dark Matter - no labels (by CartoDB)",
"url": "https://{switch:a,b,c,d}.basemaps.cartocdn.com/dark_nolabels/{z}/{x}/{y}.png",
"url": "https://basemaps.cartocdn.com/gl/positron-gl-style/style.json?key=eyJhbGciOiJIUzI1NiJ9.eyJhIjoiYWNfdW4ybmhlbTciLCJqdGkiOiIwZGQxNjJmNyJ9.uATJpa6QcrtXhph3Bzvk2nX3QsxEw-Q8dj5khUG6hGk",
"name": "Carto Positron",
"category": "osmbasedmap",
"id": "carto.positron",
"type": "vector",
"attribution": {
"html": "<a href=\"https://carto.com/attributions\">CARTO</a>"
},
"max_zoom": 20
"text": "<a href=\"https://carto.com/about-carto/\" target=\"_blank\" rel=\"noopener\">CARTO</a>",
"url": "https://carto.com/"
}
},
{
"url": "https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json?key=eyJhbGciOiJIUzI1NiJ9.eyJhIjoiYWNfdW4ybmhlbTciLCJqdGkiOiIwZGQxNjJmNyJ9.uATJpa6QcrtXhph3Bzvk2nX3QsxEw-Q8dj5khUG6hGk",
"name": "Carto Dark Matter",
"category": "osmbasedmap",
"id": "carto.dark_matter",
"type": "vector",
"attribution": {
"text": "<a href=\"https://carto.com/about-carto/\" target=\"_blank\" rel=\"noopener\">CARTO</a>",
"url": "https://carto.com/"
}
},
{
"url": "https://basemaps.cartocdn.com/gl/voyager-gl-style/style.json?key=eyJhbGciOiJIUzI1NiJ9.eyJhIjoiYWNfdW4ybmhlbTciLCJqdGkiOiIwZGQxNjJmNyJ9.uATJpa6QcrtXhph3Bzvk2nX3QsxEw-Q8dj5khUG6hGk",
"name": "Carto Voyager",
"category": "osmbasedmap",
"id": "carto.voyager",
"type": "vector",
"attribution": {
"text": "<a href=\"https://carto.com/about-carto/\" target=\"_blank\" rel=\"noopener\">CARTO</a>",
"url": "https://carto.com/"
}
},
{
"url": "https://basemaps.cartocdn.com/gl/positron-nolabels-gl-style/style.json?key=eyJhbGciOiJIUzI1NiJ9.eyJhIjoiYWNfdW4ybmhlbTciLCJqdGkiOiIwZGQxNjJmNyJ9.uATJpa6QcrtXhph3Bzvk2nX3QsxEw-Q8dj5khUG6hGk",
"name": "Carto Positron (no labels)",
"category": "osmbasedmap",
"id": "carto.positron_no_labels",
"type": "vector",
"attribution": {
"text": "<a href=\"https://carto.com/about-carto/\" target=\"_blank\" rel=\"noopener\">CARTO</a>",
"url": "https://carto.com/"
}
},
{
"url": "https://basemaps.cartocdn.com/gl/dark-matter-nolabels-gl-style/style.json?key=eyJhbGciOiJIUzI1NiJ9.eyJhIjoiYWNfdW4ybmhlbTciLCJqdGkiOiIwZGQxNjJmNyJ9.uATJpa6QcrtXhph3Bzvk2nX3QsxEw-Q8dj5khUG6hGk",
"name": "Carto Dark Matter (no labels)",
"category": "osmbasedmap",
"id": "carto.dark_matter_no_labels",
"type": "vector",
"attribution": {
"text": "<a href=\"https://carto.com/about-carto/\" target=\"_blank\" rel=\"noopener\">CARTO</a>",
"url": "https://carto.com/"
}
},
{
"url": "https://basemaps.cartocdn.com/gl/voyager-nolabels-gl-style/style.json?key=eyJhbGciOiJIUzI1NiJ9.eyJhIjoiYWNfdW4ybmhlbTciLCJqdGkiOiIwZGQxNjJmNyJ9.uATJpa6QcrtXhph3Bzvk2nX3QsxEw-Q8dj5khUG6hGk",
"name": "Carto Voyager (no labels)",
"category": "osmbasedmap",
"id": "carto.voyager_no_labels",
"type": "vector",
"attribution": {
"text": "<a href=\"https://carto.com/about-carto/\" target=\"_blank\" rel=\"noopener\">CARTO</a>",
"url": "https://carto.com/"
}
}
]
}

View file

@ -1,7 +1,7 @@
{
"contributors": [
{
"commits": 310,
"commits": 311,
"contributor": "kjon"
},
{
@ -348,6 +348,10 @@
"commits": 3,
"contributor": "SiegbjornSitumeang"
},
{
"commits": 2,
"contributor": "macpac"
},
{
"commits": 2,
"contributor": "Peter Brodersen"
@ -444,6 +448,10 @@
"commits": 2,
"contributor": "Leo Alcaraz"
},
{
"commits": 1,
"contributor": "Michal Čermák"
},
{
"commits": 1,
"contributor": "Kelson Vibber"