forked from MapComplete/MapComplete
Chore: formatting
This commit is contained in:
parent
35eff07c80
commit
c08fe03ed0
422 changed files with 31594 additions and 43019 deletions
|
|
@ -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 })
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -71,6 +71,4 @@ export default abstract class ImageProvider {
|
|||
public abstract DownloadAttribution(providedImage: ProvidedImage): Promise<LicenseInfo>
|
||||
|
||||
public abstract apiUrls(): string[]
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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!
|
||||
|
|
|
|||
|
|
@ -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(","),
|
||||
|
|
|
|||
|
|
@ -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(/</g,'<')?.replace(/>/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(/</g, "<")?.replace(/>/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"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue