Reformat all files with prettier

This commit is contained in:
Pieter Vander Vennet 2022-09-08 21:40:48 +02:00
parent e22d189376
commit b541d3eab4
382 changed files with 50893 additions and 35566 deletions

View file

@ -1,107 +1,125 @@
import {GeoOperations} from "./GeoOperations";
import {Utils} from "../Utils";
import opening_hours from "opening_hours";
import Combine from "../UI/Base/Combine";
import BaseUIElement from "../UI/BaseUIElement";
import Title from "../UI/Base/Title";
import {FixedUiElement} from "../UI/Base/FixedUiElement";
import LayerConfig from "../Models/ThemeConfig/LayerConfig";
import {CountryCoder} from "latlon2country"
import Constants from "../Models/Constants";
import {TagUtils} from "./Tags/TagUtils";
import { GeoOperations } from "./GeoOperations"
import { Utils } from "../Utils"
import opening_hours from "opening_hours"
import Combine from "../UI/Base/Combine"
import BaseUIElement from "../UI/BaseUIElement"
import Title from "../UI/Base/Title"
import { FixedUiElement } from "../UI/Base/FixedUiElement"
import LayerConfig from "../Models/ThemeConfig/LayerConfig"
import { CountryCoder } from "latlon2country"
import Constants from "../Models/Constants"
import { TagUtils } from "./Tags/TagUtils"
export class SimpleMetaTagger {
public readonly keys: string[];
public readonly doc: string;
public readonly isLazy: boolean;
public readonly keys: string[]
public readonly doc: string
public readonly isLazy: boolean
public readonly includesDates: boolean
public readonly applyMetaTagsOnFeature: (feature: any, freshness: Date, layer: LayerConfig, state) => boolean;
public readonly applyMetaTagsOnFeature: (
feature: any,
freshness: Date,
layer: LayerConfig,
state
) => boolean
/***
* A function that adds some extra data to a feature
* @param docs: what does this extra data do?
* @param f: apply the changes. Returns true if something changed
*/
constructor(docs: { keys: string[], doc: string, includesDates?: boolean, isLazy?: boolean, cleanupRetagger?: boolean },
f: ((feature: any, freshness: Date, layer: LayerConfig, state) => boolean)) {
this.keys = docs.keys;
this.doc = docs.doc;
constructor(
docs: {
keys: string[]
doc: string
includesDates?: boolean
isLazy?: boolean
cleanupRetagger?: boolean
},
f: (feature: any, freshness: Date, layer: LayerConfig, state) => boolean
) {
this.keys = docs.keys
this.doc = docs.doc
this.isLazy = docs.isLazy
this.applyMetaTagsOnFeature = f;
this.includesDates = docs.includesDates ?? false;
this.applyMetaTagsOnFeature = f
this.includesDates = docs.includesDates ?? false
if (!docs.cleanupRetagger) {
for (const key of docs.keys) {
if (!key.startsWith('_') && key.toLowerCase().indexOf("theme") < 0) {
if (!key.startsWith("_") && key.toLowerCase().indexOf("theme") < 0) {
throw `Incorrect key for a calculated meta value '${key}': it should start with underscore (_)`
}
}
}
}
}
export class CountryTagger extends SimpleMetaTagger {
private static readonly coder = new CountryCoder(Constants.countryCoderEndpoint, Utils.downloadJson);
public runningTasks: Set<any>;
private static readonly coder = new CountryCoder(
Constants.countryCoderEndpoint,
Utils.downloadJson
)
public runningTasks: Set<any>
constructor() {
const runningTasks = new Set<any>();
super
(
const runningTasks = new Set<any>()
super(
{
keys: ["_country"],
doc: "The country code of the property (with latlon2country)",
includesDates: false
includesDates: false,
},
((feature, _, __, state) => {
let centerPoint: any = GeoOperations.centerpoint(feature);
const lat = centerPoint.geometry.coordinates[1];
const lon = centerPoint.geometry.coordinates[0];
(feature, _, __, state) => {
let centerPoint: any = GeoOperations.centerpoint(feature)
const lat = centerPoint.geometry.coordinates[1]
const lon = centerPoint.geometry.coordinates[0]
runningTasks.add(feature)
CountryTagger.coder.GetCountryCodeAsync(lon, lat).then(
countries => {
CountryTagger.coder
.GetCountryCodeAsync(lon, lat)
.then((countries) => {
runningTasks.delete(feature)
try {
const oldCountry = feature.properties["_country"];
feature.properties["_country"] = countries[0].trim().toLowerCase();
const oldCountry = feature.properties["_country"]
feature.properties["_country"] = countries[0].trim().toLowerCase()
if (oldCountry !== feature.properties["_country"]) {
const tagsSource = state?.allElements?.getEventSourceById(feature.properties.id);
tagsSource?.ping();
const tagsSource = state?.allElements?.getEventSourceById(
feature.properties.id
)
tagsSource?.ping()
}
} catch (e) {
console.warn(e)
}
}
).catch(_ => {
runningTasks.delete(feature)
})
return false;
})
})
.catch((_) => {
runningTasks.delete(feature)
})
return false
}
)
this.runningTasks = runningTasks;
this.runningTasks = runningTasks
}
}
export default class SimpleMetaTaggers {
public static readonly objectMetaInfo = new SimpleMetaTagger(
{
keys: ["_last_edit:contributor",
keys: [
"_last_edit:contributor",
"_last_edit:contributor:uid",
"_last_edit:changeset",
"_last_edit:timestamp",
"_version_number",
"_backend"],
doc: "Information about the last edit of this object."
"_backend",
],
doc: "Information about the last edit of this object.",
},
(feature) => {/*Note: also called by 'UpdateTagsFromOsmAPI'*/
(feature) => {
/*Note: also called by 'UpdateTagsFromOsmAPI'*/
const tgs = feature.properties;
const tgs = feature.properties
function move(src: string, target: string) {
if (tgs[src] === undefined) {
return;
return
}
tgs[target] = tgs[src]
delete tgs[src]
@ -112,7 +130,7 @@ export default class SimpleMetaTaggers {
move("changeset", "_last_edit:changeset")
move("timestamp", "_last_edit:timestamp")
move("version", "_version_number")
return true;
return true
}
)
public static country = new CountryTagger()
@ -122,32 +140,45 @@ export default class SimpleMetaTaggers {
doc: "Adds the geometry type as property. This is identical to the GoeJson geometry type and is one of `Point`,`LineString`, `Polygon` and exceptionally `MultiPolygon` or `MultiLineString`",
},
(feature, _) => {
const changed = feature.properties["_geometry:type"] === feature.geometry.type;
feature.properties["_geometry:type"] = feature.geometry.type;
const changed = feature.properties["_geometry:type"] === feature.geometry.type
feature.properties["_geometry:type"] = feature.geometry.type
return changed
}
)
private static readonly cardinalDirections = {
N: 0, NNE: 22.5, NE: 45, ENE: 67.5,
E: 90, ESE: 112.5, SE: 135, SSE: 157.5,
S: 180, SSW: 202.5, SW: 225, WSW: 247.5,
W: 270, WNW: 292.5, NW: 315, NNW: 337.5
N: 0,
NNE: 22.5,
NE: 45,
ENE: 67.5,
E: 90,
ESE: 112.5,
SE: 135,
SSE: 157.5,
S: 180,
SSW: 202.5,
SW: 225,
WSW: 247.5,
W: 270,
WNW: 292.5,
NW: 315,
NNW: 337.5,
}
private static latlon = new SimpleMetaTagger({
private static latlon = new SimpleMetaTagger(
{
keys: ["_lat", "_lon"],
doc: "The latitude and longitude of the point (or centerpoint in the case of a way/area)"
doc: "The latitude and longitude of the point (or centerpoint in the case of a way/area)",
},
(feature => {
const centerPoint = GeoOperations.centerpoint(feature);
const lat = centerPoint.geometry.coordinates[1];
const lon = centerPoint.geometry.coordinates[0];
feature.properties["_lat"] = "" + lat;
feature.properties["_lon"] = "" + lon;
feature._lon = lon; // This is dirty, I know
feature._lat = lat;
return true;
})
);
(feature) => {
const centerPoint = GeoOperations.centerpoint(feature)
const lat = centerPoint.geometry.coordinates[1]
const lon = centerPoint.geometry.coordinates[0]
feature.properties["_lat"] = "" + lat
feature.properties["_lon"] = "" + lon
feature._lon = lon // This is dirty, I know
feature._lat = lat
return true
}
)
private static layerInfo = new SimpleMetaTagger(
{
doc: "The layer-id to which this feature belongs. Note that this might be return any applicable if `passAllFeatures` is defined.",
@ -156,98 +187,101 @@ export default class SimpleMetaTaggers {
},
(feature, freshness, layer) => {
if (feature.properties._layer === layer.id) {
return false;
return false
}
feature.properties._layer = layer.id
return true;
return true
}
)
private static noBothButLeftRight = new SimpleMetaTagger(
{
keys: ["sidewalk:left", "sidewalk:right", "generic_key:left:property", "generic_key:right:property"],
keys: [
"sidewalk:left",
"sidewalk:right",
"generic_key:left:property",
"generic_key:right:property",
],
doc: "Rewrites tags from 'generic_key:both:property' as 'generic_key:left:property' and 'generic_key:right:property' (and similar for sidewalk tagging). Note that this rewritten tags _will be reuploaded on a change_. To prevent to much unrelated retagging, this is only enabled if the layer has at least some lineRenderings with offset defined",
includesDates: false,
cleanupRetagger: true
cleanupRetagger: true,
},
((feature, state, layer) => {
if (!layer.lineRendering.some(lr => lr.leftRightSensitive)) {
return;
(feature, state, layer) => {
if (!layer.lineRendering.some((lr) => lr.leftRightSensitive)) {
return
}
return SimpleMetaTaggers.removeBothTagging(feature.properties)
})
}
)
private static surfaceArea = new SimpleMetaTagger(
{
keys: ["_surface", "_surface:ha"],
doc: "The surface area of the feature, in square meters and in hectare. Not set on points and ways",
isLazy: true
isLazy: true,
},
(feature => {
(feature) => {
Object.defineProperty(feature.properties, "_surface", {
enumerable: false,
configurable: true,
get: () => {
const sqMeters = "" + GeoOperations.surfaceAreaInSqMeters(feature);
const sqMeters = "" + GeoOperations.surfaceAreaInSqMeters(feature)
delete feature.properties["_surface"]
feature.properties["_surface"] = sqMeters;
feature.properties["_surface"] = sqMeters
return sqMeters
}
},
})
Object.defineProperty(feature.properties, "_surface:ha", {
enumerable: false,
configurable: true,
get: () => {
const sqMeters = GeoOperations.surfaceAreaInSqMeters(feature);
const sqMetersHa = "" + Math.floor(sqMeters / 1000) / 10;
const sqMeters = GeoOperations.surfaceAreaInSqMeters(feature)
const sqMetersHa = "" + Math.floor(sqMeters / 1000) / 10
delete feature.properties["_surface:ha"]
feature.properties["_surface:ha"] = sqMetersHa;
feature.properties["_surface:ha"] = sqMetersHa
return sqMetersHa
}
},
})
return true;
})
);
return true
}
)
private static levels = new SimpleMetaTagger(
{
doc: "Extract the 'level'-tag into a normalized, ';'-separated value",
keys: ["_level"]
keys: ["_level"],
},
((feature) => {
(feature) => {
if (feature.properties["level"] === undefined) {
return false;
return false
}
const l = feature.properties["level"]
const newValue = TagUtils.LevelsParser(l).join(";")
if(l === newValue) {
return false;
if (l === newValue) {
return false
}
feature.properties["level"] = newValue
return true
})
}
)
private static canonicalize = new SimpleMetaTagger(
{
doc: "If 'units' is defined in the layoutConfig, then this metatagger will rewrite the specified keys to have the canonical form (e.g. `1meter` will be rewritten to `1m`; `1` will be rewritten to `1m` as well)",
keys: ["Theme-defined keys"],
},
((feature, _, __, state) => {
const units = Utils.NoNull([].concat(...state?.layoutToUse?.layers?.map(layer => layer.units) ?? []));
(feature, _, __, state) => {
const units = Utils.NoNull(
[].concat(...(state?.layoutToUse?.layers?.map((layer) => layer.units) ?? []))
)
if (units.length == 0) {
return;
return
}
let rewritten = false;
let rewritten = false
for (const key in feature.properties) {
if (!feature.properties.hasOwnProperty(key)) {
continue;
continue
}
for (const unit of units) {
if (unit === undefined) {
@ -258,56 +292,59 @@ export default class SimpleMetaTaggers {
continue
}
if (!unit.appliesToKeys.has(key)) {
continue;
continue
}
const value = feature.properties[key]
const denom = unit.findDenomination(value, () => feature.properties["_country"])
if (denom === undefined) {
// no valid value found
break;
break
}
const [, denomination] = denom;
const defaultDenom = unit.getDefaultDenomination(() => feature.properties["_country"])
let canonical = denomination?.canonicalValue(value, defaultDenom == denomination) ?? undefined;
const [, denomination] = denom
const defaultDenom = unit.getDefaultDenomination(
() => feature.properties["_country"]
)
let canonical =
denomination?.canonicalValue(value, defaultDenom == denomination) ??
undefined
if (canonical === value) {
break;
break
}
console.log("Rewritten ", key, ` from '${value}' into '${canonical}'`)
if (canonical === undefined && !unit.eraseInvalid) {
break;
break
}
feature.properties[key] = canonical;
rewritten = true;
break;
feature.properties[key] = canonical
rewritten = true
break
}
}
return rewritten
})
}
)
private static lngth = new SimpleMetaTagger(
{
keys: ["_length", "_length:km"],
doc: "The total length of a feature in meters (and in kilometers, rounded to one decimal for '_length:km'). For a surface, the length of the perimeter"
doc: "The total length of a feature in meters (and in kilometers, rounded to one decimal for '_length:km'). For a surface, the length of the perimeter",
},
(feature => {
(feature) => {
const l = GeoOperations.lengthInMeters(feature)
feature.properties["_length"] = "" + l
const km = Math.floor(l / 1000)
const kmRest = Math.round((l - km * 1000) / 100)
feature.properties["_length:km"] = "" + km + "." + kmRest
return true;
})
return true
}
)
private static isOpen = new SimpleMetaTagger(
{
keys: ["_isOpen"],
doc: "If 'opening_hours' is present, it will add the current state of the feature (being 'yes' or 'no')",
includesDates: true,
isLazy: true
isLazy: true,
},
((feature, _, __, state) => {
(feature, _, __, state) => {
if (Utils.runningFromConsole) {
// We are running from console, thus probably creating a cache
// isOpen is irrelevant
@ -315,7 +352,7 @@ export default class SimpleMetaTaggers {
}
if (feature.properties.opening_hours === "24/7") {
feature.properties._isOpen = "yes"
return true;
return true
}
// _isOpen is calculated dynamically on every call
@ -325,92 +362,92 @@ export default class SimpleMetaTaggers {
get: () => {
const tags = feature.properties
if (tags.opening_hours === undefined) {
return;
return
}
if (tags._country === undefined) {
return;
return
}
try {
const [lon, lat] = GeoOperations.centerpointCoordinates(feature)
const oh = new opening_hours(tags["opening_hours"], {
lat: lat,
lon: lon,
address: {
country_code: tags._country.toLowerCase(),
state: undefined
}
}, <any>{tag_key: "opening_hours"});
const oh = new opening_hours(
tags["opening_hours"],
{
lat: lat,
lon: lon,
address: {
country_code: tags._country.toLowerCase(),
state: undefined,
},
},
<any>{ tag_key: "opening_hours" }
)
// Recalculate!
return oh.getState() ? "yes" : "no";
return oh.getState() ? "yes" : "no"
} catch (e) {
console.warn("Error while parsing opening hours of ", tags.id, e);
console.warn("Error while parsing opening hours of ", tags.id, e)
delete tags._isOpen
tags["_isOpen"] = "parse_error";
tags["_isOpen"] = "parse_error"
}
}
});
},
})
const tagsSource = state.allElements.getEventSourceById(feature.properties.id);
})
const tagsSource = state.allElements.getEventSourceById(feature.properties.id)
}
)
private static directionSimplified = new SimpleMetaTagger(
{
keys: ["_direction:numerical", "_direction:leftright"],
doc: "_direction:numerical is a normalized, numerical direction based on 'camera:direction' or on 'direction'; it is only present if a valid direction is found (e.g. 38.5 or NE). _direction:leftright is either 'left' or 'right', which is left-looking on the map or 'right-looking' on the map"
doc: "_direction:numerical is a normalized, numerical direction based on 'camera:direction' or on 'direction'; it is only present if a valid direction is found (e.g. 38.5 or NE). _direction:leftright is either 'left' or 'right', which is left-looking on the map or 'right-looking' on the map",
},
(feature => {
const tags = feature.properties;
const direction = tags["camera:direction"] ?? tags["direction"];
(feature) => {
const tags = feature.properties
const direction = tags["camera:direction"] ?? tags["direction"]
if (direction === undefined) {
return false;
return false
}
const n = SimpleMetaTaggers.cardinalDirections[direction] ?? Number(direction);
const n = SimpleMetaTaggers.cardinalDirections[direction] ?? Number(direction)
if (isNaN(n)) {
return false;
return false
}
// The % operator has range (-360, 360). We apply a trick to get [0, 360).
const normalized = ((n % 360) + 360) % 360;
const normalized = ((n % 360) + 360) % 360
tags["_direction:numerical"] = normalized;
tags["_direction:leftright"] = normalized <= 180 ? "right" : "left";
return true;
})
tags["_direction:numerical"] = normalized
tags["_direction:leftright"] = normalized <= 180 ? "right" : "left"
return true
}
)
private static currentTime = new SimpleMetaTagger(
{
keys: ["_now:date", "_now:datetime", "_loaded:date", "_loaded:_datetime"],
doc: "Adds the time that the data got loaded - pretty much the time of downloading from overpass. The format is YYYY-MM-DD hh:mm, aka 'sortable' aka ISO-8601-but-not-entirely",
includesDates: true
includesDates: true,
},
(feature, freshness) => {
const now = new Date();
const now = new Date()
if (typeof freshness === "string") {
freshness = new Date(freshness)
}
function date(d: Date) {
return d.toISOString().slice(0, 10);
return d.toISOString().slice(0, 10)
}
function datetime(d: Date) {
return d.toISOString().slice(0, -5).replace("T", " ");
return d.toISOString().slice(0, -5).replace("T", " ")
}
feature.properties["_now:date"] = date(now);
feature.properties["_now:datetime"] = datetime(now);
feature.properties["_loaded:date"] = date(freshness);
feature.properties["_loaded:datetime"] = datetime(freshness);
return true;
feature.properties["_now:date"] = date(now)
feature.properties["_now:datetime"] = datetime(now)
feature.properties["_loaded:date"] = date(freshness)
feature.properties["_loaded:datetime"] = datetime(freshness)
return true
}
);
)
public static metatags: SimpleMetaTagger[] = [
SimpleMetaTaggers.latlon,
SimpleMetaTaggers.layerInfo,
@ -424,11 +461,11 @@ export default class SimpleMetaTaggers {
SimpleMetaTaggers.objectMetaInfo,
SimpleMetaTaggers.noBothButLeftRight,
SimpleMetaTaggers.geometryType,
SimpleMetaTaggers.levels
];
public static readonly lazyTags: string[] = [].concat(...SimpleMetaTaggers.metatags.filter(tagger => tagger.isLazy)
.map(tagger => tagger.keys));
SimpleMetaTaggers.levels,
]
public static readonly lazyTags: string[] = [].concat(
...SimpleMetaTaggers.metatags.filter((tagger) => tagger.isLazy).map((tagger) => tagger.keys)
)
/**
* Edits the given object to rewrite 'both'-tagging into a 'left-right' tagging scheme.
@ -451,36 +488,34 @@ export default class SimpleMetaTaggers {
}
if (tags["sidewalk"]) {
const v = tags["sidewalk"]
switch (v) {
case "none":
case "no":
set("sidewalk:left", "no");
set("sidewalk:right", "no");
set("sidewalk:left", "no")
set("sidewalk:right", "no")
break
case "both":
set("sidewalk:left", "yes");
set("sidewalk:right", "yes");
break;
set("sidewalk:left", "yes")
set("sidewalk:right", "yes")
break
case "left":
set("sidewalk:left", "yes");
set("sidewalk:right", "no");
break;
set("sidewalk:left", "yes")
set("sidewalk:right", "no")
break
case "right":
set("sidewalk:left", "no");
set("sidewalk:right", "yes");
break;
set("sidewalk:left", "no")
set("sidewalk:right", "yes")
break
default:
set("sidewalk:left", v);
set("sidewalk:right", v);
break;
set("sidewalk:left", v)
set("sidewalk:right", v)
break
}
delete tags["sidewalk"]
somethingChanged = true
}
const regex = /\([^:]*\):both:\(.*\)/
for (const key in tags) {
const v = tags[key]
@ -503,7 +538,6 @@ export default class SimpleMetaTaggers {
}
}
return somethingChanged
}
@ -512,13 +546,16 @@ export default class SimpleMetaTaggers {
new Combine([
"Metatags are extra tags available, in order to display more data or to give better questions.",
"They are calculated automatically on every feature when the data arrives in the webbrowser. This document gives an overview of the available metatags.",
"**Hint:** when using metatags, add the [query parameter](URL_Parameters.md) `debug=true` to the URL. This will include a box in the popup for features which shows all the properties of the object"
]).SetClass("flex-col")
];
"**Hint:** when using metatags, add the [query parameter](URL_Parameters.md) `debug=true` to the URL. This will include a box in the popup for features which shows all the properties of the object",
]).SetClass("flex-col"),
]
subElements.push(new Title("Metatags calculated by MapComplete", 2))
subElements.push(new FixedUiElement("The following values are always calculated, by default, by MapComplete and are available automatically on all elements in every theme"))
subElements.push(
new FixedUiElement(
"The following values are always calculated, by default, by MapComplete and are available automatically on all elements in every theme"
)
)
for (const metatag of SimpleMetaTaggers.metatags) {
subElements.push(
new Title(metatag.keys.join(", "), 3),
@ -529,5 +566,4 @@ export default class SimpleMetaTaggers {
return new Combine(subElements).SetClass("flex-col")
}
}