forked from MapComplete/MapComplete
Merge branch 'feature/android-capacitator' of github.com:pietervdvn/MapComplete into feature/android-capacitator
This commit is contained in:
commit
a9b6b6148e
370 changed files with 5765 additions and 3198 deletions
|
|
@ -13,7 +13,6 @@ import StaticFeatureSource, {
|
|||
} from "../FeatureSource/Sources/StaticFeatureSource"
|
||||
import { MapProperties } from "../../Models/MapProperties"
|
||||
import { Orientation } from "../../Sensors/Orientation"
|
||||
|
||||
;("use strict")
|
||||
/**
|
||||
* The geolocation-handler takes a map-location and a geolocation state.
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import Constants from "../../Models/Constants"
|
|||
import { Utils } from "../../Utils"
|
||||
import { GeoLocationState } from "../State/GeoLocationState"
|
||||
import { OsmConnection } from "../Osm/OsmConnection"
|
||||
|
||||
;("use strict")
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { FeatureSource, WritableFeatureSource } from "../FeatureSource"
|
||||
import { ImmutableStore, Store, UIEventSource } from "../../UIEventSource"
|
||||
import { Feature } from "geojson"
|
||||
|
||||
;("use strict")
|
||||
/**
|
||||
* A simple, read only feature store.
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ import {
|
|||
import { Tiles } from "../Models/TileRange"
|
||||
import { Utils } from "../Utils"
|
||||
import { NearestPointOnLine } from "@turf/nearest-point-on-line"
|
||||
|
||||
;("use strict")
|
||||
|
||||
export class GeoOperations {
|
||||
|
|
|
|||
|
|
@ -91,7 +91,6 @@ export default class AllImageProviders {
|
|||
However, we override them if a custom image tag is set, e.g. 'image:menu'
|
||||
*/
|
||||
const prefixes = tagKey ?? imageProvider.defaultKeyPrefixes
|
||||
console.log("Prefixes are", tagKey, prefixes)
|
||||
const singleSource = tags.bindD((tags) => imageProvider.getRelevantUrls(tags, prefixes))
|
||||
allSources.push(singleSource)
|
||||
singleSource.addCallbackAndRunD((_) => {
|
||||
|
|
|
|||
|
|
@ -127,7 +127,9 @@ export default class OsmObjectDownloader {
|
|||
* Beware: their geometry will be incomplete!
|
||||
*/
|
||||
public async DownloadReferencingWays(id: string): Promise<OsmWay[]> {
|
||||
const data = await Utils.downloadJsonCached(`${this.backend}api/0.6/${id}/ways`, 60 * 1000)
|
||||
const data = await Utils.downloadJsonCached<{
|
||||
elements: { id: number }[]
|
||||
}>(`${this.backend}api/0.6/${id}/ways`, 60 * 1000)
|
||||
return data.elements.map((wayInfo) => new OsmWay(wayInfo.id, wayInfo))
|
||||
}
|
||||
|
||||
|
|
@ -136,7 +138,7 @@ export default class OsmObjectDownloader {
|
|||
* Beware: their geometry will be incomplete!
|
||||
*/
|
||||
public async DownloadReferencingRelations(id: string): Promise<OsmRelation[]> {
|
||||
const data = await Utils.downloadJsonCached(
|
||||
const data = await Utils.downloadJsonCached<{ elements: { id: number }[] }>(
|
||||
`${this.backend}api/0.6/${id}/relations`,
|
||||
60 * 1000
|
||||
)
|
||||
|
|
|
|||
|
|
@ -38,14 +38,27 @@ export class OsmPreferences {
|
|||
})
|
||||
}
|
||||
|
||||
private setPreferencesAll(key: string, value: string) {
|
||||
/**
|
||||
* Sets a new preferenceValue in 'allPreferences'
|
||||
* @param key
|
||||
* @param value
|
||||
* @param deferping: the end user will ping '_allPreferences'
|
||||
* @private
|
||||
*/
|
||||
private setPreferencesAll(key: string, value: string, deferping = false) {
|
||||
if (this._allPreferences.data[key] !== value) {
|
||||
this._allPreferences.data[key] = value
|
||||
this._allPreferences.ping()
|
||||
if (!deferping) {
|
||||
this._allPreferences.ping()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private initPreference(key: string, value: string = undefined): UIEventSource<string> {
|
||||
private initPreference(
|
||||
key: string,
|
||||
value: string = undefined,
|
||||
deferPing = false
|
||||
): UIEventSource<string> {
|
||||
if (this.preferences[key] !== undefined) {
|
||||
if (value !== undefined) {
|
||||
this.preferences[key].set(value)
|
||||
|
|
@ -54,12 +67,12 @@ export class OsmPreferences {
|
|||
}
|
||||
const pref = (this.preferences[key] = new UIEventSource(value, "preference: " + key))
|
||||
if (value) {
|
||||
this.setPreferencesAll(key, value)
|
||||
this.setPreferencesAll(key, value, deferPing)
|
||||
}
|
||||
pref.addCallback((v) => {
|
||||
console.log("Got an update:", key, "--->", v)
|
||||
this.uploadKvSplit(key, v)
|
||||
this.setPreferencesAll(key, v)
|
||||
this.setPreferencesAll(key, v, deferPing)
|
||||
})
|
||||
return pref
|
||||
}
|
||||
|
|
@ -73,11 +86,12 @@ export class OsmPreferences {
|
|||
await this.removeLegacy(legacy)
|
||||
}
|
||||
for (const key in merged) {
|
||||
this.initPreference(key, prefs[key])
|
||||
this.initPreference(key, prefs[key], true)
|
||||
}
|
||||
for (const key in legacy) {
|
||||
this.initPreference(key, legacy[key])
|
||||
this.initPreference(key, legacy[key], true)
|
||||
}
|
||||
this._allPreferences.ping()
|
||||
}
|
||||
|
||||
public getPreference(key: string, defaultValue: string = undefined, prefix?: string) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,3 @@
|
|||
import * as nsi from "../../../node_modules/name-suggestion-index/dist/nsi.json"
|
||||
import * as nsiWD from "../../../node_modules/name-suggestion-index/dist/wikidata.min.json"
|
||||
|
||||
import * as nsiFeatures from "../../../node_modules/name-suggestion-index/dist/featureCollection.json"
|
||||
import { LocationConflation } from "@rapideditor/location-conflation"
|
||||
import type { Feature, MultiPolygon } from "geojson"
|
||||
|
|
@ -56,28 +53,57 @@ export interface NSIItem {
|
|||
}
|
||||
|
||||
export default class NameSuggestionIndex {
|
||||
private static readonly nsiFile: Readonly<NSIFile> = <any>nsi
|
||||
private static readonly nsiWdFile: Readonly<
|
||||
public static readonly supportedTypes = ["brand", "flag", "operator", "transit"] as const
|
||||
private readonly nsiFile: Readonly<NSIFile>
|
||||
private readonly nsiWdFile: Readonly<
|
||||
Record<
|
||||
string,
|
||||
{
|
||||
logos: { wikidata?: string; facebook?: string }
|
||||
}
|
||||
>
|
||||
> = <any>nsiWD["wikidata"]
|
||||
>
|
||||
|
||||
private static loco = new LocationConflation(nsiFeatures) // Some additional boundaries
|
||||
|
||||
private static _supportedTypes: string[]
|
||||
private _supportedTypes: string[]
|
||||
|
||||
public static supportedTypes(): string[] {
|
||||
constructor(
|
||||
nsiFile: Readonly<NSIFile>,
|
||||
nsiWdFile: Readonly<
|
||||
Record<
|
||||
string,
|
||||
{
|
||||
logos: { wikidata?: string; facebook?: string }
|
||||
}
|
||||
>
|
||||
>
|
||||
) {
|
||||
this.nsiFile = nsiFile
|
||||
this.nsiWdFile = nsiWdFile
|
||||
}
|
||||
|
||||
private static inited: NameSuggestionIndex = undefined
|
||||
|
||||
public static async getNsiIndex(): Promise<NameSuggestionIndex> {
|
||||
if (NameSuggestionIndex.inited) {
|
||||
return NameSuggestionIndex.inited
|
||||
}
|
||||
const [nsi, nsiWd] = await Promise.all(
|
||||
["assets/data/nsi/nsi.json", "assets/data/nsi/wikidata.min.json"].map((url) =>
|
||||
Utils.downloadJsonCached(url, 1000 * 60 * 60 * 24 * 30)
|
||||
)
|
||||
)
|
||||
NameSuggestionIndex.inited = new NameSuggestionIndex(<any>nsi, <any>nsiWd["wikidata"])
|
||||
return NameSuggestionIndex.inited
|
||||
}
|
||||
|
||||
public supportedTypes(): string[] {
|
||||
if (this._supportedTypes) {
|
||||
return this._supportedTypes
|
||||
}
|
||||
const keys = Object.keys(NameSuggestionIndex.nsiFile.nsi)
|
||||
const all = keys.map(
|
||||
(k) => NameSuggestionIndex.nsiFile.nsi[k].properties.path.split("/")[0]
|
||||
)
|
||||
const keys = Object.keys(this.nsiFile.nsi)
|
||||
const all = keys.map((k) => this.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)
|
||||
|
|
@ -123,7 +149,12 @@ export default class NameSuggestionIndex {
|
|||
return merged
|
||||
}
|
||||
|
||||
public static isSvg(nsiItem: NSIItem, type: string): boolean | undefined {
|
||||
public isSvg(nsiItem: NSIItem, type: string): boolean | undefined {
|
||||
if (this.nsiWdFile === undefined) {
|
||||
throw (
|
||||
"nsiWdi file is not loaded, cannot determine if " + nsiItem.id + " has an SVG image"
|
||||
)
|
||||
}
|
||||
const logos = this.nsiWdFile[nsiItem?.tags?.[type + ":wikidata"]]?.logos
|
||||
if (!logos) {
|
||||
return undefined
|
||||
|
|
@ -138,7 +169,7 @@ export default class NameSuggestionIndex {
|
|||
return false
|
||||
}
|
||||
|
||||
public static async generateMappings(
|
||||
public async generateMappings(
|
||||
type: string,
|
||||
tags: Record<string, string>,
|
||||
country: string[],
|
||||
|
|
@ -157,7 +188,7 @@ export default class NameSuggestionIndex {
|
|||
continue
|
||||
}
|
||||
const value = tags[key]
|
||||
const actualBrands = NameSuggestionIndex.getSuggestionsForKV(
|
||||
const actualBrands = this.getSuggestionsForKV(
|
||||
type,
|
||||
key,
|
||||
value,
|
||||
|
|
@ -177,7 +208,7 @@ export default class NameSuggestionIndex {
|
|||
if (hasIcon) {
|
||||
// Using <img src=...> works fine without an extension for JPG and PNG, but _not_ svg :(
|
||||
icon = "./assets/data/nsi/logos/" + nsiItem.id
|
||||
if (NameSuggestionIndex.isSvg(nsiItem, type)) {
|
||||
if (this.isSvg(nsiItem, type)) {
|
||||
icon = icon + ".svg"
|
||||
}
|
||||
}
|
||||
|
|
@ -207,13 +238,13 @@ export default class NameSuggestionIndex {
|
|||
return mappings
|
||||
}
|
||||
|
||||
public static supportedTags(
|
||||
public supportedTags(
|
||||
type: "operator" | "brand" | "flag" | "transit" | string
|
||||
): Record<string, string[]> {
|
||||
const tags: Record<string, string[]> = {}
|
||||
const keys = Object.keys(NameSuggestionIndex.nsiFile.nsi)
|
||||
const keys = Object.keys(this.nsiFile.nsi)
|
||||
for (const key of keys) {
|
||||
const nsiItem = NameSuggestionIndex.nsiFile.nsi[key]
|
||||
const nsiItem = this.nsiFile.nsi[key]
|
||||
const path = nsiItem.properties.path
|
||||
const [osmType, osmkey, osmvalue] = path.split("/")
|
||||
if (type !== osmType && type + "s" !== osmType) {
|
||||
|
|
@ -231,9 +262,9 @@ export default class NameSuggestionIndex {
|
|||
* Returns a list of all brands/operators
|
||||
* @param type
|
||||
*/
|
||||
public static allPossible(type: "brand" | "operator"): NSIItem[] {
|
||||
public allPossible(type: "brand" | "operator"): NSIItem[] {
|
||||
const options: NSIItem[] = []
|
||||
const tags = NameSuggestionIndex.supportedTags(type)
|
||||
const tags = this.supportedTags(type)
|
||||
for (const osmKey in tags) {
|
||||
const values = tags[osmKey]
|
||||
for (const osmValue of values) {
|
||||
|
|
@ -249,7 +280,7 @@ 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(
|
||||
public getSuggestionsFor(
|
||||
type: string,
|
||||
tags: { key: string; value: string }[],
|
||||
country: string = undefined,
|
||||
|
|
@ -274,7 +305,7 @@ 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(
|
||||
public getSuggestionsForKV(
|
||||
type: string,
|
||||
key: string,
|
||||
value: string,
|
||||
|
|
@ -282,7 +313,7 @@ export default class NameSuggestionIndex {
|
|||
location: [number, number] = undefined
|
||||
): NSIItem[] {
|
||||
const path = `${type}s/${key}/${value}`
|
||||
const entry = NameSuggestionIndex.nsiFile.nsi[path]
|
||||
const entry = this.nsiFile.nsi[path]
|
||||
const countries = country?.split(";") ?? []
|
||||
return entry?.items?.filter((i) => {
|
||||
if (i.locationSet.include.indexOf("001") >= 0) {
|
||||
|
|
@ -312,8 +343,8 @@ export default class NameSuggestionIndex {
|
|||
}
|
||||
|
||||
const hasSpecial =
|
||||
i.locationSet.include?.some((i) => i.endsWith(".geojson") || Array.isArray(i)) ||
|
||||
i.locationSet.exclude?.some((i) => i.endsWith(".geojson") || Array.isArray(i))
|
||||
i.locationSet.include?.some((i) => Array.isArray(i) || i.endsWith(".geojson")) ||
|
||||
i.locationSet.exclude?.some((i) => Array.isArray(i) || i.endsWith(".geojson"))
|
||||
if (!hasSpecial) {
|
||||
return false
|
||||
}
|
||||
|
|
@ -335,4 +366,17 @@ export default class NameSuggestionIndex {
|
|||
return false
|
||||
})
|
||||
}
|
||||
|
||||
public static async generateMappings(
|
||||
key: string,
|
||||
tags: Exclude<Record<string, string>, undefined | null>,
|
||||
country: string[],
|
||||
center: [number, number],
|
||||
options: {
|
||||
sortByFrequency: boolean
|
||||
}
|
||||
): Promise<Mapping[]> {
|
||||
const nsi = await NameSuggestionIndex.getNsiIndex()
|
||||
return nsi.generateMappings(key, tags, country, center, options)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue