Chore: formatting

This commit is contained in:
Pieter Vander Vennet 2024-06-16 16:06:26 +02:00
parent 35eff07c80
commit c08fe03ed0
422 changed files with 31594 additions and 43019 deletions

View file

@ -73,7 +73,11 @@ export default class InitialMapPositioning {
if (initialHash?.match(/^(node|way|relation)\/[0-9]+$/)) {
const [type, id] = initialHash.split("/")
OsmObjectDownloader.RawDownloadObjectAsync(type, Number(id), Constants.osmAuthConfig.url + "/").then(osmObject => {
OsmObjectDownloader.RawDownloadObjectAsync(
type,
Number(id),
Constants.osmAuthConfig.url + "/"
).then((osmObject) => {
if (osmObject === "deleted") {
return
}
@ -83,6 +87,5 @@ export default class InitialMapPositioning {
this.location.setData({ lon, lat })
})
}
}
}

View file

@ -17,7 +17,7 @@ import questions from "../assets/generated/layers/questions.json"
import {
DoesImageExist,
PrevalidateTheme,
ValidateThemeAndLayers
ValidateThemeAndLayers,
} from "../Models/ThemeConfig/Conversion/Validation"
import { DesugaringContext } from "../Models/ThemeConfig/Conversion/Conversion"
import { TagRenderingConfigJson } from "../Models/ThemeConfig/Json/TagRenderingConfigJson"
@ -55,8 +55,10 @@ export default class DetermineLayout {
return undefined
}
private static async expandRemoteLayers(layoutConfig: LayoutConfigJson): Promise<LayoutConfigJson> {
if(!layoutConfig.layers){
private static async expandRemoteLayers(
layoutConfig: LayoutConfigJson
): Promise<LayoutConfigJson> {
if (!layoutConfig.layers) {
// This is probably a layer in 'layer-only-mode'
return layoutConfig
}
@ -73,7 +75,6 @@ export default class DetermineLayout {
} catch (_) {
continue
}
}
return layoutConfig
}
@ -115,7 +116,9 @@ export default class DetermineLayout {
return layout
}
public static async LoadLayoutFromHash(userLayoutParam: UIEventSource<string>): Promise<LayoutConfig | null> {
public static async LoadLayoutFromHash(
userLayoutParam: UIEventSource<string>
): Promise<LayoutConfig | null> {
let hash = location.hash.substr(1)
let json: any
@ -175,11 +178,11 @@ export default class DetermineLayout {
id: json.id,
description: json.description,
descriptionTail: {
en: "<div class='alert'>Layer only mode.</div> The loaded custom theme actually isn't a custom theme, but only contains a layer."
en: "<div class='alert'>Layer only mode.</div> The loaded custom theme actually isn't a custom theme, but only contains a layer.",
},
icon,
title: json.name,
layers: [json]
layers: [json],
}
}
@ -191,7 +194,7 @@ export default class DetermineLayout {
const convertState: DesugaringContext = {
tagRenderings: DetermineLayout.getSharedTagRenderings(),
sharedLayers: knownLayersDict,
publicLayers: new Set<string>()
publicLayers: new Set<string>(),
}
json = new FixLegacyTheme().convertStrict(json)
const raw = json
@ -215,7 +218,7 @@ export default class DetermineLayout {
}
return new LayoutConfig(json, false, {
definitionRaw: JSON.stringify(raw, null, " "),
definedAtUrl: sourceUrl
definedAtUrl: sourceUrl,
})
}

View file

@ -509,7 +509,7 @@ export class ExtraFunctions {
): Record<ExtraFuncType, (feature: Feature) => Function> {
const record: Record<string, (feature: Feature) => Function> = {}
for (const f of ExtraFunctions.allFuncs) {
if ((<readonly string[]> this.types).indexOf(f._name) < 0) {
if ((<readonly string[]>this.types).indexOf(f._name) < 0) {
throw "Invalid extraFunc-type: " + f._name
}
record[f._name] = (feat) => f._f(params, feat)

View file

@ -38,7 +38,7 @@ class SingleTileSaver {
if (this._registeredIds.has(id)) {
continue
}
if(id.match(/(node|way|relation)\/-.*/)){
if (id.match(/(node|way|relation)\/-.*/)) {
// We don't cache newly created points
continue
}

View file

@ -16,7 +16,7 @@ export class LastClickFeatureSource {
private i: number = 0
private readonly hasPresets: boolean
private readonly hasNoteLayer: boolean
public static readonly newPointElementId= "new_point_dialog"
public static readonly newPointElementId = "new_point_dialog"
constructor(layout: LayoutConfig) {
this.hasNoteLayer = layout.hasNoteLayer()

View file

@ -18,15 +18,13 @@ export default class AllImageProviders {
WikidataImageProvider.singleton,
WikimediaImageProvider.singleton,
// The 'genericImageProvider' is a fallback that scans various other tags for tags, unless the URL starts with one of the given prefixes
new GenericImageProvider(
[
...Imgur.defaultValuePrefix,
...WikimediaImageProvider.commonsPrefixes,
...Mapillary.valuePrefixes,
...AllImageProviders.dontLoadFromPrefixes,
"Category:"
]
),
new GenericImageProvider([
...Imgur.defaultValuePrefix,
...WikimediaImageProvider.commonsPrefixes,
...Mapillary.valuePrefixes,
...AllImageProviders.dontLoadFromPrefixes,
"Category:",
]),
]
public static apiUrls: string[] = [].concat(
...AllImageProviders.ImageAttributionSource.map((src) => src.apiUrls())

View file

@ -71,6 +71,4 @@ export default abstract class ImageProvider {
public abstract DownloadAttribution(providedImage: ProvidedImage): Promise<LicenseInfo>
public abstract apiUrls(): string[]
}

View file

@ -73,15 +73,15 @@ export class ImageUploadManager {
}
}
public canBeUploaded(file: File): true | {error: Translation} {
public canBeUploaded(file: File): true | { error: Translation } {
const sizeInBytes = file.size
const self = this
if (sizeInBytes > this._uploader.maxFileSizeInMegabytes * 1000000) {
const error = Translations.t.image.toBig.Subs({
const error = Translations.t.image.toBig.Subs({
actual_size: Math.floor(sizeInBytes / 1000000) + "MB",
max_size: self._uploader.maxFileSizeInMegabytes + "MB",
})
return {error}
return { error }
}
return true
}
@ -98,9 +98,8 @@ export class ImageUploadManager {
tagsStore: UIEventSource<OsmTags>,
targetKey?: string
): Promise<void> {
const canBeUploaded = this.canBeUploaded(file)
if(canBeUploaded !== true){
if (canBeUploaded !== true) {
throw canBeUploaded.error
}

View file

@ -1,7 +1,7 @@
import Constants from "../Models/Constants"
export interface MaprouletteTask {
name: string,
description: string,
name: string
description: string
instruction: string
}
export default class Maproulette {

View file

@ -27,16 +27,16 @@ export default class MetaTagging {
((feature: Feature, propertiesStore: UIEventSource<any>) => void)[]
>()
private state: {
readonly selectedElement: Store<Feature>;
readonly layout: LayoutConfig;
readonly osmObjectDownloader: OsmObjectDownloader;
readonly perLayer: ReadonlyMap<string, GeoIndexedStoreForLayer>;
readonly indexedFeatures: IndexedFeatureSource;
readonly selectedElement: Store<Feature>
readonly layout: LayoutConfig
readonly osmObjectDownloader: OsmObjectDownloader
readonly perLayer: ReadonlyMap<string, GeoIndexedStoreForLayer>
readonly indexedFeatures: IndexedFeatureSource
readonly featureProperties: FeaturePropertiesStore
}
private params: {
getFeatureById: (id) => Feature;
getFeaturesWithin: (layerId, bbox) => (Feature[][] | [Feature[]])
getFeatureById: (id) => Feature
getFeaturesWithin: (layerId, bbox) => Feature[][] | [Feature[]]
}
constructor(state: {
@ -48,7 +48,7 @@ export default class MetaTagging {
readonly featureProperties: FeaturePropertiesStore
}) {
this.state = state
const params = this.params = MetaTagging.createExtraFuncParams(state)
const params = (this.params = MetaTagging.createExtraFuncParams(state))
for (const layer of state.layout.layers) {
if (layer.source === null) {
continue
@ -78,7 +78,7 @@ export default class MetaTagging {
}
// Force update the tags of the currently selected element
state.selectedElement.addCallbackAndRunD(feature => {
state.selectedElement.addCallbackAndRunD((feature) => {
this.updateCurrentSelectedElement()
let lastUpdateMoment = new Date()
const tags = state?.featureProperties?.getStore(feature.properties.id)
@ -86,22 +86,19 @@ export default class MetaTagging {
tags?.addCallbackD(() => {
console.log("Received an update! Re-calculating the metatags")
if(feature !== state.selectedElement.data){
if (feature !== state.selectedElement.data) {
return true // Unregister, we are not the selected element anymore
}
if(new Date().getTime() - lastUpdateMoment.getTime() < 250){
if (new Date().getTime() - lastUpdateMoment.getTime() < 250) {
return
}
lastUpdateMoment = new Date()
window.requestIdleCallback(() => {
this.updateCurrentSelectedElement()
lastUpdateMoment = new Date()
})
})
})
}
/**
@ -125,7 +122,7 @@ export default class MetaTagging {
state.featureProperties,
{
includeDates: !lightUpdate,
evaluateStrict: !lightUpdate
evaluateStrict: !lightUpdate,
}
)
}
@ -209,8 +206,13 @@ export default class MetaTagging {
// All keys are defined - lets skip!
continue
}
const shouldPing = metatag.applyMetaTagsOnFeature(feature, layer, tags, state)
if(!shouldPing){
const shouldPing = metatag.applyMetaTagsOnFeature(
feature,
layer,
tags,
state
)
if (!shouldPing) {
continue
}
somethingChanged = true
@ -291,7 +293,7 @@ export default class MetaTagging {
return []
}
return [state.perLayer.get(layerId).GetFeaturesWithin(bbox)]
}
},
}
}
@ -336,8 +338,8 @@ export default class MetaTagging {
if (MetaTagging.errorPrintCount < MetaTagging.stopErrorOutputAt) {
console.warn(
"Could not calculate a " +
(isStrict ? "strict " : "") +
"calculated tag for key",
(isStrict ? "strict " : "") +
"calculated tag for key",
key,
"for feature",
feat.properties.id,
@ -345,9 +347,9 @@ export default class MetaTagging {
code,
"(in layer",
layerId +
") due to \n" +
e +
"\n. Are you the theme creator? Doublecheck your code. Note that the metatags might not be stable on new features",
") due to \n" +
e +
"\n. Are you the theme creator? Doublecheck your code. Note that the metatags might not be stable on new features",
e,
e.stack,
{ feat }

View file

@ -29,7 +29,11 @@ export class Changes {
public readonly pendingChanges: UIEventSource<ChangeDescription[]> =
LocalStorageSource.GetParsed<ChangeDescription[]>("pending-changes", [])
public readonly allChanges = new UIEventSource<ChangeDescription[]>(undefined)
public readonly state: { allElements?: IndexedFeatureSource; osmConnection: OsmConnection, featureSwitches?: FeatureSwitchState }
public readonly state: {
allElements?: IndexedFeatureSource
osmConnection: OsmConnection
featureSwitches?: FeatureSwitchState
}
public readonly extraComment: UIEventSource<string> = new UIEventSource(undefined)
public readonly backend: string
public readonly isUploading = new UIEventSource(false)
@ -46,7 +50,7 @@ export class Changes {
allElements?: IndexedFeatureSource
featurePropertiesStore?: FeaturePropertiesStore
osmConnection: OsmConnection
historicalUserLocations?: FeatureSource,
historicalUserLocations?: FeatureSource
featureSwitches?: FeatureSwitchState
},
leftRightSensitive: boolean = false
@ -433,7 +437,7 @@ export class Changes {
// Probably irrelevant, such as a new helper node
return
}
if(this.state.featureSwitches.featureSwitchMorePrivacy?.data){
if (this.state.featureSwitches.featureSwitchMorePrivacy?.data) {
return
}

View file

@ -224,7 +224,7 @@ export class ChangesetHandler {
if (newMetaTag === undefined) {
extraMetaTags.push({
key: key,
value: oldCsTags[key]
value: oldCsTags[key],
})
continue
}
@ -360,15 +360,12 @@ export class ChangesetHandler {
["created_by", `MapComplete ${Constants.vNumber}`],
["locale", Locale.language.data],
["host", `${window.location.origin}${window.location.pathname}`],
[
"source",
setSourceAsSurvey ? "survey" : undefined
],
["imagery", this.changes.state["backgroundLayer"]?.data?.id]
["source", setSourceAsSurvey ? "survey" : undefined],
["imagery", this.changes.state["backgroundLayer"]?.data?.id],
].map(([key, value]) => ({
key,
value,
aggregate: false
aggregate: false,
}))
}

View file

@ -143,7 +143,11 @@ export class OsmConnection {
options.oauth_token.setData(undefined)
}
if (!Utils.runningFromConsole && this.auth.authenticated() && options.attemptLogin !== false) {
if (
!Utils.runningFromConsole &&
this.auth.authenticated() &&
options.attemptLogin !== false
) {
this.AttemptLogin()
} else {
console.log("Not authenticated")
@ -210,7 +214,7 @@ export class OsmConnection {
this.auth.xhr(
{
method: "GET",
path: "/api/0.6/user/details"
path: "/api/0.6/user/details",
},
(err, details: XMLDocument) => {
if (err != null) {
@ -322,9 +326,9 @@ export class OsmConnection {
method,
headers: header,
content,
path: `/api/0.6/${path}`
path: `/api/0.6/${path}`,
},
function(err, response) {
function (err, response) {
if (err !== null) {
error(err)
} else {
@ -341,7 +345,7 @@ export class OsmConnection {
header?: Record<string, string>,
allowAnonymous: boolean = false
): Promise<T> {
return <T> await this.interact(path, "POST", header, content, allowAnonymous)
return <T>await this.interact(path, "POST", header, content, allowAnonymous)
}
public async put<T extends string>(
@ -349,7 +353,7 @@ export class OsmConnection {
content?: string,
header?: Record<string, string>
): Promise<T> {
return <T> await this.interact(path, "PUT", header, content)
return <T>await this.interact(path, "PUT", header, content)
}
public async get(
@ -377,7 +381,7 @@ export class OsmConnection {
public reopenNote(id: number | string, text?: string): Promise<string> {
if (this._dryRun.data) {
console.warn("Dryrun enabled - not actually reopening note ", id, " with text ", text)
return new Promise(resolve => {
return new Promise((resolve) => {
resolve("")
})
}
@ -404,7 +408,7 @@ export class OsmConnection {
"notes.json",
content,
{
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
},
true
)
@ -445,7 +449,7 @@ export class OsmConnection {
file: gpx,
description: options.description,
tags: options.labels?.join(",") ?? "",
visibility: options.visibility
visibility: options.visibility,
}
if (!contents.description) {
@ -453,9 +457,9 @@ export class OsmConnection {
}
const extras = {
file:
"; filename=\"" +
'; filename="' +
(options.filename ?? "gpx_track_mapcomplete_" + new Date().toISOString()) +
"\"\r\nContent-Type: application/gpx+xml"
'"\r\nContent-Type: application/gpx+xml',
}
const boundary = "987654"
@ -463,7 +467,7 @@ export class OsmConnection {
let body = ""
for (const key in contents) {
body += "--" + boundary + "\r\n"
body += "Content-Disposition: form-data; name=\"" + key + "\""
body += 'Content-Disposition: form-data; name="' + key + '"'
if (extras[key] !== undefined) {
body += extras[key]
}
@ -474,7 +478,7 @@ export class OsmConnection {
const response = await this.post("gpx/create", body, {
"Content-Type": "multipart/form-data; boundary=" + boundary,
"Content-Length": ""+body.length
"Content-Length": "" + body.length,
})
const parsed = JSON.parse(response)
console.log("Uploaded GPX track", parsed)
@ -497,9 +501,9 @@ export class OsmConnection {
{
method: "POST",
path: `/api/0.6/notes/${id}/comment?text=${encodeURIComponent(text)}`
path: `/api/0.6/notes/${id}/comment?text=${encodeURIComponent(text)}`,
},
function(err) {
function (err) {
if (err !== null) {
error(err)
} else {
@ -514,7 +518,7 @@ export class OsmConnection {
* To be called by land.html
*/
public finishLogin(callback: (previousURL: string) => void) {
this.auth.authenticate(function() {
this.auth.authenticate(function () {
// Fully authed at this point
console.log("Authentication successful!")
const previousLocation = LocalStorageSource.Get("location_before_login")
@ -531,7 +535,7 @@ export class OsmConnection {
? "https://mapcomplete.org/land.html"
: window.location.protocol + "//" + window.location.host + "/land.html",
singlepage: true, // We always use 'singlePage', it is the most stable - including in PWA
auto: true
auto: true,
})
}

View file

@ -62,7 +62,12 @@ export default class OsmObjectDownloader {
if (idN < 0) {
obj = this.constructObject(<"node" | "way" | "relation">type, idN)
} else {
obj = await OsmObjectDownloader.RawDownloadObjectAsync(type, idN, this.backend, maxCacheAgeInSecs)
obj = await OsmObjectDownloader.RawDownloadObjectAsync(
type,
idN,
this.backend,
maxCacheAgeInSecs
)
}
if (obj === "deleted") {
return obj

View file

@ -126,7 +126,7 @@ class CountryTagger extends SimpleMetaTagger {
applyMetaTagsOnFeature(feature: Feature, _, tagsSource) {
const runningTasks = this.runningTasks
if(runningTasks.has(feature) || !!feature.properties._country){
if (runningTasks.has(feature) || !!feature.properties._country) {
return
}
runningTasks.add(feature)
@ -432,8 +432,11 @@ export default class SimpleMetaTaggers {
() => feature.properties["_country"]
)
const canonical =
denomination?.canonicalValue(value, defaultDenom == denomination, unit.inverted) ??
undefined
denomination?.canonicalValue(
value,
defaultDenom == denomination,
unit.inverted
) ?? undefined
if (canonical === value) {
break
}
@ -511,7 +514,7 @@ export default class SimpleMetaTaggers {
state: undefined,
},
},
<any> { tag_key: "opening_hours" }
<any>{ tag_key: "opening_hours" }
)
// Recalculate!

View file

@ -165,14 +165,12 @@ export default class FeatureSwitchState extends OsmConnectionFeatureSwitches {
"If true, shows some extra debugging help such as all the available tags on every object"
)
this.featureSwitchMorePrivacy = QueryParameters.GetBooleanQueryParameter(
"moreprivacy",
layoutToUse.enableMorePrivacy,
"If true, the location distance indication will not be written to the changeset and other privacy enhancing measures might be taken."
)
this.overpassUrl = QueryParameters.GetQueryParameter(
"overpassUrl",
(layoutToUse?.overpassUrl ?? Constants.defaultOverpassUrls).join(","),

View file

@ -1,14 +1,42 @@
import { Utils } from "../../Utils"
/** This code is autogenerated - do not edit. Edit ./assets/layers/usersettings/usersettings.json instead */
export class ThemeMetaTagging {
public static readonly themeName = "usersettings"
public static readonly themeName = "usersettings"
public metaTaggging_for_usersettings(feat: {properties: Record<string, string>}) {
Utils.AddLazyProperty(feat.properties, '_mastodon_candidate_md', () => feat.properties._description.match(/\[[^\]]*\]\((.*(mastodon|en.osm.town).*)\).*/)?.at(1) )
Utils.AddLazyProperty(feat.properties, '_d', () => feat.properties._description?.replace(/&lt;/g,'<')?.replace(/&gt;/g,'>') ?? '' )
Utils.AddLazyProperty(feat.properties, '_mastodon_candidate_a', () => (feat => {const e = document.createElement('div');e.innerHTML = feat.properties._d;return Array.from(e.getElementsByTagName("a")).filter(a => a.href.match(/mastodon|en.osm.town/) !== null)[0]?.href }) (feat) )
Utils.AddLazyProperty(feat.properties, '_mastodon_link', () => (feat => {const e = document.createElement('div');e.innerHTML = feat.properties._d;return Array.from(e.getElementsByTagName("a")).filter(a => a.getAttribute("rel")?.indexOf('me') >= 0)[0]?.href})(feat) )
Utils.AddLazyProperty(feat.properties, '_mastodon_candidate', () => feat.properties._mastodon_candidate_md ?? feat.properties._mastodon_candidate_a )
feat.properties['__current_backgroun'] = 'initial_value'
}
}
public metaTaggging_for_usersettings(feat: { properties: Record<string, string> }) {
Utils.AddLazyProperty(feat.properties, "_mastodon_candidate_md", () =>
feat.properties._description
.match(/\[[^\]]*\]\((.*(mastodon|en.osm.town).*)\).*/)
?.at(1)
)
Utils.AddLazyProperty(
feat.properties,
"_d",
() => feat.properties._description?.replace(/&lt;/g, "<")?.replace(/&gt;/g, ">") ?? ""
)
Utils.AddLazyProperty(feat.properties, "_mastodon_candidate_a", () =>
((feat) => {
const e = document.createElement("div")
e.innerHTML = feat.properties._d
return Array.from(e.getElementsByTagName("a")).filter(
(a) => a.href.match(/mastodon|en.osm.town/) !== null
)[0]?.href
})(feat)
)
Utils.AddLazyProperty(feat.properties, "_mastodon_link", () =>
((feat) => {
const e = document.createElement("div")
e.innerHTML = feat.properties._d
return Array.from(e.getElementsByTagName("a")).filter(
(a) => a.getAttribute("rel")?.indexOf("me") >= 0
)[0]?.href
})(feat)
)
Utils.AddLazyProperty(
feat.properties,
"_mastodon_candidate",
() => feat.properties._mastodon_candidate_md ?? feat.properties._mastodon_candidate_a
)
feat.properties["__current_backgroun"] = "initial_value"
}
}

View file

@ -278,11 +278,11 @@ export class And extends TagsFilter {
}
const optimized = <TagsFilter[]>optimizedRaw
for (let i = 0; i <optimized.length; i++) {
for (let i = 0; i < optimized.length; i++) {
for (let j = i + 1; j < optimized.length; j++) {
const ti = optimized[i]
const tj = optimized[j]
if(ti.shadows(tj)){
if (ti.shadows(tj)) {
// if 'ti' is true, this implies 'tj' is always true as well.
// if 'ti' is false, then 'tj' might be true or false
// (e.g. let 'ti' be 'count>0' and 'tj' be 'count>10'.
@ -290,7 +290,7 @@ export class And extends TagsFilter {
// If 'ti' is true, then 'tj' will be true too and 'tj' can be ignored
// If 'ti' is false, then the entire expression will be false and it doesn't matter what 'tj' yields
optimized.splice(j, 1)
}else if (tj.shadows(ti)){
} else if (tj.shadows(ti)) {
optimized.splice(i, 1)
i--
continue
@ -298,7 +298,6 @@ export class And extends TagsFilter {
}
}
{
// Conflicting keys do return false
const properties: Record<string, string> = {}
@ -358,9 +357,9 @@ export class And extends TagsFilter {
i--
}
}
}else if(opt instanceof ComparingTag) {
} else if (opt instanceof ComparingTag) {
const ct = opt
if(properties[ct.key] !== undefined && !ct.matchesProperties(properties)){
if (properties[ct.key] !== undefined && !ct.matchesProperties(properties)) {
return false
}
}

View file

@ -136,15 +136,15 @@ export class Tag extends TagsFilter {
* new Tag("key","value").shadows(new And([new Tag("x","y"), new RegexTag("a","b", true)]) // => false
*/
shadows(other: TagsFilter): boolean {
if ((other["key"] !== this.key)) {
if (other["key"] !== this.key) {
return false
}
if(other instanceof Tag){
if (other instanceof Tag) {
// Other.key === this.key
return other.value === this.value
}
if(other instanceof RegexTag){
return other.matchesProperties({[this.key]: this.value})
if (other instanceof RegexTag) {
return other.matchesProperties({ [this.key]: this.value })
}
return false
}

View file

@ -835,7 +835,7 @@ export class UIEventSource<T> extends Store<T> implements Writable<T> {
}
public mapAsyncD<J>(f: (t: T) => Promise<J>): Store<J> {
return this.bindD(t => UIEventSource.FromPromise(f(t)))
return this.bindD((t) => UIEventSource.FromPromise(f(t)))
}
/**
@ -901,5 +901,4 @@ export class UIEventSource<T> extends Store<T> implements Writable<T> {
update(f: Updater<T> & ((value: T) => T)): void {
this.setData(f(this.data))
}
}

View file

@ -27,23 +27,23 @@ export default class LinkedDataLoader {
opening_hours: { "@id": "http://schema.org/openingHoursSpecification" },
openingHours: { "@id": "http://schema.org/openingHours", "@container": "@set" },
geo: { "@id": "http://schema.org/geo" },
alt_name: { "@id": "http://schema.org/alternateName" }
alt_name: { "@id": "http://schema.org/alternateName" },
}
private static COMPACTING_CONTEXT_OH = {
dayOfWeek: { "@id": "http://schema.org/dayOfWeek", "@container": "@set" },
closes: {
"@id": "http://schema.org/closes",
"@type": "http://www.w3.org/2001/XMLSchema#time"
"@type": "http://www.w3.org/2001/XMLSchema#time",
},
opens: {
"@id": "http://schema.org/opens",
"@type": "http://www.w3.org/2001/XMLSchema#time"
}
"@type": "http://www.w3.org/2001/XMLSchema#time",
},
}
private static formatters: Record<"phone" | "email" | "website", Validator> = {
phone: new PhoneValidator(),
email: new EmailValidator(),
website: new UrlValidator(undefined, undefined, true)
website: new UrlValidator(undefined, undefined, true),
}
private static ignoreKeys = [
"http://schema.org/logo",
@ -56,7 +56,7 @@ export default class LinkedDataLoader {
"http://schema.org/description",
"http://schema.org/hasMap",
"http://schema.org/priceRange",
"http://schema.org/contactPoint"
"http://schema.org/contactPoint",
]
private static shapeToPolygon(str: string): Polygon {
@ -69,8 +69,8 @@ export default class LinkedDataLoader {
.trim()
.split(" ")
.map((n) => Number(n))
)
]
),
],
}
}
@ -92,18 +92,18 @@ export default class LinkedDataLoader {
const context = {
lat: {
"@id": "http://schema.org/latitude",
"@type": "http://www.w3.org/2001/XMLSchema#double"
"@type": "http://www.w3.org/2001/XMLSchema#double",
},
lon: {
"@id": "http://schema.org/longitude",
"@type": "http://www.w3.org/2001/XMLSchema#double"
}
"@type": "http://www.w3.org/2001/XMLSchema#double",
},
}
const flattened = await jsonld.compact(geo, context)
return {
type: "Point",
coordinates: [Number(flattened.lon), Number(flattened.lat)]
coordinates: [Number(flattened.lon), Number(flattened.lat)],
}
}
@ -288,7 +288,7 @@ export default class LinkedDataLoader {
if (properties["latitude"] && properties["longitude"]) {
geometry = {
type: "Point",
coordinates: [Number(properties["longitude"]), Number(properties["latitude"])]
coordinates: [Number(properties["longitude"]), Number(properties["latitude"])],
}
delete properties["latitude"]
delete properties["longitude"]
@ -300,7 +300,7 @@ export default class LinkedDataLoader {
const geo: GeoJSON = {
type: "Feature",
properties,
geometry
geometry,
}
delete linkedData.geo
delete properties.shape
@ -323,7 +323,7 @@ export default class LinkedDataLoader {
if (output["type"]?.[0] === "https://data.velopark.be/openvelopark/terms#BicycleLocker") {
output["bicycle_parking"] = ["lockers"]
}
if(output["type"] === undefined){
if (output["type"] === undefined) {
console.error("No type given for", output)
}
delete output["type"]
@ -333,7 +333,7 @@ export default class LinkedDataLoader {
return
}
output[key] = output[key].map((v) => applyF(v))
if (!output[key].some(v => v !== undefined)) {
if (!output[key].some((v) => v !== undefined)) {
delete output[key]
}
}
@ -418,7 +418,7 @@ export default class LinkedDataLoader {
"brede publiek",
"iedereen",
"bezoekers",
"iedereen - vooral bezoekers gemeentehuis of bibliotheek."
"iedereen - vooral bezoekers gemeentehuis of bibliotheek.",
].indexOf(audience.toLowerCase()) >= 0
) {
return "yes"
@ -501,7 +501,7 @@ export default class LinkedDataLoader {
mv: "http://schema.mobivoc.org/",
gr: "http://purl.org/goodrelations/v1#",
vp: "https://data.velopark.be/openvelopark/vocabulary#",
vpt: "https://data.velopark.be/openvelopark/terms#"
vpt: "https://data.velopark.be/openvelopark/terms#",
},
[url],
undefined,
@ -522,7 +522,7 @@ export default class LinkedDataLoader {
mv: "http://schema.mobivoc.org/",
gr: "http://purl.org/goodrelations/v1#",
vp: "https://data.velopark.be/openvelopark/vocabulary#",
vpt: "https://data.velopark.be/openvelopark/terms#"
vpt: "https://data.velopark.be/openvelopark/terms#",
},
[url],
"g",
@ -654,7 +654,10 @@ export default class LinkedDataLoader {
* The id will be saved as `ref:velopark`
* @param url
*/
public static async fetchVeloparkEntry(url: string, includeExtras: boolean = false): Promise<Feature[]> {
public static async fetchVeloparkEntry(
url: string,
includeExtras: boolean = false
): Promise<Feature[]> {
const cacheKey = includeExtras + url
if (this.veloparkCache[cacheKey]) {
return this.veloparkCache[cacheKey]
@ -662,20 +665,20 @@ export default class LinkedDataLoader {
const withProxyUrl = Constants.linkedDataProxy.replace("{url}", encodeURIComponent(url))
const optionalPaths: Record<string, string | Record<string, string>> = {
"schema:interactionService": {
"schema:url": "website"
"schema:url": "website",
},
"mv:operatedBy": {
"gr:legalName": "operator"
"gr:legalName": "operator",
},
"schema:contactPoint": {
"schema:email": "email",
"schema:telephone": "phone"
"schema:telephone": "phone",
},
"schema:dateModified": "_last_edit_timestamp"
"schema:dateModified": "_last_edit_timestamp",
}
if (includeExtras) {
optionalPaths["schema:address"] = {
"schema:streetAddress": "addr"
"schema:streetAddress": "addr",
}
optionalPaths["schema:name"] = "name"
optionalPaths["schema:description"] = "description"
@ -693,19 +696,19 @@ export default class LinkedDataLoader {
"schema:geo": {
"schema:latitude": "latitude",
"schema:longitude": "longitude",
"schema:polygon": "shape"
"schema:polygon": "shape",
},
"schema:priceSpecification": {
"mv:freeOfCharge": "fee",
"schema:price": "charge"
}
"schema:price": "charge",
},
}
const extra = [
"schema:priceSpecification [ mv:dueForTime [ mv:timeStartValue ?chargeStart; mv:timeEndValue ?chargeEnd; mv:timeUnit ?timeUnit ] ]",
"vp:allows [vp:bicycleType <https://data.velopark.be/openvelopark/terms#CargoBicycle>; vp:bicyclesAmount ?capacityCargobike; vp:bicycleType ?cargoBikeType]",
"vp:allows [vp:bicycleType <https://data.velopark.be/openvelopark/terms#ElectricBicycle>; vp:bicyclesAmount ?capacityElectric; vp:bicycleType ?electricBikeType]",
"vp:allows [vp:bicycleType <https://data.velopark.be/openvelopark/terms#TandemBicycle>; vp:bicyclesAmount ?capacityTandem; vp:bicycleType ?tandemBikeType]"
"vp:allows [vp:bicycleType <https://data.velopark.be/openvelopark/terms#TandemBicycle>; vp:bicyclesAmount ?capacityTandem; vp:bicycleType ?tandemBikeType]",
]
const unpatched = await this.fetchEntry(

View file

@ -110,12 +110,12 @@ export class MangroveIdentity {
return []
}
const allReviews = await MangroveReviews.getReviews({
kid: pem
kid: pem,
})
this.allReviewsById.setData(
allReviews.reviews.map((r) => ({
...r,
...r.payload
...r.payload,
}))
)
})
@ -182,10 +182,10 @@ export default class FeatureReviews {
feature.geometry.type === "Polygon"
) {
coordss = feature.geometry.coordinates
}else if(feature.geometry.type === "MultiPolygon"){
} else if (feature.geometry.type === "MultiPolygon") {
coordss = feature.geometry.coordinates[0]
}else{
throw "Invalid feature type: "+feature.geometry.type
} else {
throw "Invalid feature type: " + feature.geometry.type
}
let maxDistance = 0
for (const coords of coordss) {
@ -288,7 +288,7 @@ export default class FeatureReviews {
}
const r: Review = {
sub: this.subjectUri.data,
...review
...review,
}
const keypair: CryptoKeyPair = await this._identity.getKeypair()
const jwt = await MangroveReviews.signReview(keypair, r)
@ -303,7 +303,7 @@ export default class FeatureReviews {
...r,
kid,
signature: jwt,
madeByLoggedInUser: new ImmutableStore(true)
madeByLoggedInUser: new ImmutableStore(true),
}
this._reviews.data.push(reviewWithKid)
this._reviews.ping()
@ -350,7 +350,7 @@ export default class FeatureReviews {
signature: reviewData.signature,
madeByLoggedInUser: this._identity.getKeyId().map((user_key_id) => {
return reviewData.kid === user_key_id
})
}),
})
hasNew = true
}
@ -369,12 +369,16 @@ export default class FeatureReviews {
private ConstructSubjectUri(dontEncodeName: boolean = false): Store<string> {
// https://www.rfc-editor.org/rfc/rfc5870#section-3.4.2
// `u` stands for `uncertainty`, https://www.rfc-editor.org/rfc/rfc5870#section-3.4.3
return this._name.map(name => {
return this._name.map((name) => {
let uri = `geo:${this._lat},${this._lon}?u=${Math.round(this._uncertainty)}`
if (name) {
uri += "&q=" + (dontEncodeName ? name : encodeURIComponent(name))
}else if(this._uncertainty > 1000){
console.error("Not fetching reviews. Only got a point and a very big uncertainty range ("+this._uncertainty+"), so you'd probably only get garbage. Specify a name")
} else if (this._uncertainty > 1000) {
console.error(
"Not fetching reviews. Only got a point and a very big uncertainty range (" +
this._uncertainty +
"), so you'd probably only get garbage. Specify a name"
)
return undefined
}
return uri

View file

@ -48,7 +48,7 @@ export interface NSIItem {
displayName: string
id: string
locationSet: {
include: string[],
include: string[]
exclude: string[]
}
tags: Record<string, string>
@ -56,11 +56,15 @@ export interface NSIItem {
}
export default class NameSuggestionIndex {
private static readonly nsiFile: Readonly<NSIFile> = <any>nsi
private static readonly nsiWdFile: Readonly<Record<string, {
logos: { wikidata?: string, facebook?: string }
}>> = <any>nsiWD["wikidata"]
private static readonly nsiWdFile: Readonly<
Record<
string,
{
logos: { wikidata?: string; facebook?: string }
}
>
> = <any>nsiWD["wikidata"]
private static loco = new LocationConflation(nsiFeatures) // Some additional boundaries
@ -71,9 +75,11 @@ export default class NameSuggestionIndex {
return this._supportedTypes
}
const keys = Object.keys(NameSuggestionIndex.nsiFile.nsi)
const all = keys.map(k => NameSuggestionIndex.nsiFile.nsi[k].properties.path.split("/")[0])
this._supportedTypes = Utils.Dedup(all).map(s => {
if(s.endsWith("s")){
const all = keys.map(
(k) => NameSuggestionIndex.nsiFile.nsi[k].properties.path.split("/")[0]
)
this._supportedTypes = Utils.Dedup(all).map((s) => {
if (s.endsWith("s")) {
s = s.substring(0, s.length - 1)
}
return s
@ -81,7 +87,6 @@ export default class NameSuggestionIndex {
return this._supportedTypes
}
/**
* Fetches the data files for a single country. Note that it contains _all_ entries having this brand, not for a single type of object
* @param type
@ -89,19 +94,24 @@ export default class NameSuggestionIndex {
* @private
*/
private static async fetchFrequenciesFor(type: string, countries: string[]) {
let stats = await Promise.all(countries.map(c => {
try {
return Utils.downloadJsonCached<Record<string, number>>(`./assets/data/nsi/stats/${type}.${c.toUpperCase()}.json`, 24 * 60 * 60 * 1000)
} catch (e) {
console.error("Could not fetch " + type + " statistics due to", e)
return undefined
}
}))
let stats = await Promise.all(
countries.map((c) => {
try {
return Utils.downloadJsonCached<Record<string, number>>(
`./assets/data/nsi/stats/${type}.${c.toUpperCase()}.json`,
24 * 60 * 60 * 1000
)
} catch (e) {
console.error("Could not fetch " + type + " statistics due to", e)
return undefined
}
})
)
stats = Utils.NoNull(stats)
if (stats.length === 1) {
return stats[0]
}
if(stats.length === 0){
if (stats.length === 0) {
return {}
}
const merged = stats[0]
@ -128,7 +138,12 @@ export default class NameSuggestionIndex {
return false
}
public static async generateMappings(type: string, tags: Record<string, string>, country: string[], location?: [number, number]): Promise<Mapping[]> {
public static async generateMappings(
type: string,
tags: Record<string, string>,
country: string[],
location?: [number, number]
): Promise<Mapping[]> {
const mappings: Mapping[] = []
const frequencies = await NameSuggestionIndex.fetchFrequenciesFor(type, country)
for (const key in tags) {
@ -136,8 +151,14 @@ export default class NameSuggestionIndex {
continue
}
const value = tags[key]
const actualBrands = NameSuggestionIndex.getSuggestionsForKV(type, key, value, country.join(";"), location)
if(!actualBrands){
const actualBrands = NameSuggestionIndex.getSuggestionsForKV(
type,
key,
value,
country.join(";"),
location
)
if (!actualBrands) {
continue
}
for (const nsiItem of actualBrands) {
@ -156,7 +177,9 @@ export default class NameSuggestionIndex {
}
mappings.push({
if: new Tag(type, tags[type]),
addExtraTags: Object.keys(tags).filter(k => k !== type).map(k => new Tag(k, tags[k])),
addExtraTags: Object.keys(tags)
.filter((k) => k !== type)
.map((k) => new Tag(k, tags[k])),
then: new TypedTranslation<Record<string, never>>({ "*": nsiItem.displayName }),
hideInAnswer: false,
ifnot: undefined,
@ -164,22 +187,23 @@ export default class NameSuggestionIndex {
icon,
iconClass: "medium",
priorityIf: frequency > 0 ? new RegexTag("id", /.*/) : undefined,
searchTerms: { "*": [nsiItem.displayName, nsiItem.id] }
searchTerms: { "*": [nsiItem.displayName, nsiItem.id] },
})
}
}
return mappings
}
public static supportedTags(type: "operator" | "brand" | "flag" | "transit" | string): Record<string, string[]> {
const tags: Record<string, string []> = {}
public static supportedTags(
type: "operator" | "brand" | "flag" | "transit" | string
): Record<string, string[]> {
const tags: Record<string, string[]> = {}
const keys = Object.keys(NameSuggestionIndex.nsiFile.nsi)
for (const key of keys) {
const nsiItem = NameSuggestionIndex.nsiFile.nsi[key]
const path = nsiItem.properties.path
const [osmType, osmkey, osmvalue] = path.split("/")
if (type !== osmType && (type + "s" !== osmType)) {
if (type !== osmType && type + "s" !== osmType) {
continue
}
if (!tags[osmkey]) {
@ -204,7 +228,7 @@ export default class NameSuggestionIndex {
options.push(...suggestions)
}
}
return (options)
return options
}
/**
@ -212,8 +236,15 @@ export default class NameSuggestionIndex {
* @param country: a string containing one or more country codes, separated by ";"
* @param location: center point of the feature, should be [lon, lat]
*/
public static getSuggestionsFor(type: string, tags: {key: string, value: string}[], country: string = undefined, location: [number, number] = undefined): NSIItem[] {
return tags.flatMap(tag => this.getSuggestionsForKV(type, tag.key, tag.value, country, location))
public static getSuggestionsFor(
type: string,
tags: { key: string; value: string }[],
country: string = undefined,
location: [number, number] = undefined
): NSIItem[] {
return tags.flatMap((tag) =>
this.getSuggestionsForKV(type, tag.key, tag.value, country, location)
)
}
/**
@ -221,18 +252,26 @@ export default class NameSuggestionIndex {
* @param country: a string containing one or more country codes, separated by ";"
* @param location: center point of the feature, should be [lon, lat]
*/
public static getSuggestionsForKV(type: string, key: string, value: string, country: string = undefined, location: [number, number] = undefined): NSIItem[] {
public static getSuggestionsForKV(
type: string,
key: string,
value: string,
country: string = undefined,
location: [number, number] = undefined
): NSIItem[] {
const path = `${type}s/${key}/${value}`
const entry = NameSuggestionIndex.nsiFile.nsi[path]
return entry?.items?.filter(i => {
return entry?.items?.filter((i) => {
if (i.locationSet.include.indexOf("001") >= 0) {
return true
}
if (country === undefined ||
if (
country === undefined ||
// We prefer the countries provided by lonlat2country, they are more precise
// Country might contain multiple countries, separated by ';'
i.locationSet.include.some(c => country.indexOf(c) >= 0)) {
i.locationSet.include.some((c) => country.indexOf(c) >= 0)
) {
return true
}

View file

@ -50,10 +50,8 @@ export default class NearbyImagesSearch {
constructor(options: NearbyImageOptions, features: IndexedFeatureSource) {
this.individualStores = NearbyImagesSearch.services
.filter(s => s !== "kartaview" /*DEAD*/)
.map((s) =>
NearbyImagesSearch.buildPictureFetcher(options, s)
)
.filter((s) => s !== "kartaview" /*DEAD*/)
.map((s) => NearbyImagesSearch.buildPictureFetcher(options, s))
const allDone = new UIEventSource(false)
this.allDone = allDone

View file

@ -19,10 +19,10 @@ export interface TagInfoStats {
}
interface GeofabrikCountryProperties {
id: string,
parent: string | "europe" | "asia",
urls: string[],
name: string,
id: string
parent: string | "europe" | "asia"
urls: string[]
name: string
"iso3166-1:alpha2": string[]
}
@ -38,7 +38,9 @@ export default class TagInfo {
public async getStats(key: string, value?: string): Promise<TagInfoStats> {
let url: string
if (value) {
url = `${this._backend}api/4/tag/stats?key=${encodeURIComponent(key)}&value=${encodeURIComponent(value)}`
url = `${this._backend}api/4/tag/stats?key=${encodeURIComponent(
key
)}&value=${encodeURIComponent(value)}`
} else {
url = `${this._backend}api/4/key/stats?key=${encodeURIComponent(key)}`
}
@ -65,8 +67,13 @@ export default class TagInfo {
if (TagInfo._geofabrikCountries) {
return TagInfo._geofabrikCountries
}
const countriesFC: FeatureCollection = await Utils.downloadJsonCached<FeatureCollection>("https://download.geofabrik.de/index-v1-nogeom.json", 24 * 1000 * 60 * 60)
TagInfo._geofabrikCountries = countriesFC.features.map(f => <GeofabrikCountryProperties>f.properties)
const countriesFC: FeatureCollection = await Utils.downloadJsonCached<FeatureCollection>(
"https://download.geofabrik.de/index-v1-nogeom.json",
24 * 1000 * 60 * 60
)
TagInfo._geofabrikCountries = countriesFC.features.map(
(f) => <GeofabrikCountryProperties>f.properties
)
return TagInfo._geofabrikCountries
}
@ -80,7 +87,7 @@ export default class TagInfo {
public static async getInstanceFor(countryCode: string) {
const countries = await this.geofabrikCountries()
countryCode = countryCode.toUpperCase()
const country = countries.find(c => c["iso3166-1:alpha2"]?.indexOf(countryCode) >= 0)
const country = countries.find((c) => c["iso3166-1:alpha2"]?.indexOf(countryCode) >= 0)
if (!country || !country?.parent || !country?.id) {
return undefined
}
@ -88,7 +95,11 @@ export default class TagInfo {
return new TagInfo(url)
}
private static async getDistributionsFor(countryCode: string, key: string, value?: string): Promise<TagInfoStats>{
private static async getDistributionsFor(
countryCode: string,
key: string,
value?: string
): Promise<TagInfoStats> {
if (!countryCode) {
return undefined
}
@ -99,24 +110,30 @@ export default class TagInfo {
try {
return await ti.getStats(key, value)
} catch (e) {
console.warn("Could not fetch info for", countryCode,key,value, "due to", e)
console.warn("Could not fetch info for", countryCode, key, value, "due to", e)
return undefined
}
}
private static readonly blacklist =["VI","GF","PR"]
private static readonly blacklist = ["VI", "GF", "PR"]
public static async getGlobalDistributionsFor(key: string, value?: string): Promise<Record<string, TagInfoStats>> {
public static async getGlobalDistributionsFor(
key: string,
value?: string
): Promise<Record<string, TagInfoStats>> {
const countriesAll = await this.geofabrikCountries()
const countries = countriesAll.map(c => c["iso3166-1:alpha2"]?.[0]).filter(c => !!c && TagInfo.blacklist.indexOf(c) < 0)
const countries = countriesAll
.map((c) => c["iso3166-1:alpha2"]?.[0])
.filter((c) => !!c && TagInfo.blacklist.indexOf(c) < 0)
const perCountry: Record<string, TagInfoStats> = {}
const results = await Promise.all(countries.map(country => TagInfo.getDistributionsFor(country, key, value)))
for (let i = 0; i < countries.length; i++){
const results = await Promise.all(
countries.map((country) => TagInfo.getDistributionsFor(country, key, value))
)
for (let i = 0; i < countries.length; i++) {
const countryCode = countries[i]
if(results[i]){
perCountry[countryCode] = results[i]
if (results[i]) {
perCountry[countryCode] = results[i]
}
}
return perCountry
}
}