forked from MapComplete/MapComplete
Merge branch 'develop' into feature/nsi
This commit is contained in:
commit
63beb43cb7
3 changed files with 85 additions and 11 deletions
|
@ -119,7 +119,7 @@ class CountryTagger extends SimpleMetaTagger {
|
||||||
constructor() {
|
constructor() {
|
||||||
super({
|
super({
|
||||||
keys: ["_country"],
|
keys: ["_country"],
|
||||||
doc: "The country code of the property (with latlon2country)",
|
doc: "The country codes of the of the country/countries that the feature is located in (with latlon2country). Might contain _multiple_ countries, separated by a `;`",
|
||||||
includesDates: false,
|
includesDates: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -138,7 +138,7 @@ class CountryTagger extends SimpleMetaTagger {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const oldCountry = feature.properties["_country"]
|
const oldCountry = feature.properties["_country"]
|
||||||
const newCountry = countries[0].trim().toLowerCase()
|
const newCountry = countries.join(";").trim().toLowerCase()
|
||||||
if (oldCountry !== newCountry) {
|
if (oldCountry !== newCountry) {
|
||||||
if (typeof window === undefined) {
|
if (typeof window === undefined) {
|
||||||
tagsSource.data["_country"] = newCountry
|
tagsSource.data["_country"] = newCountry
|
||||||
|
@ -150,7 +150,7 @@ class CountryTagger extends SimpleMetaTagger {
|
||||||
/**
|
/**
|
||||||
* What is this weird construction?
|
* What is this weird construction?
|
||||||
*
|
*
|
||||||
* For a theme with a hundreds of items (e.g. shops)
|
* For a theme with hundreds of items (e.g. shops)
|
||||||
* the country for all those shops will probably arrive at the same time.
|
* the country for all those shops will probably arrive at the same time.
|
||||||
*
|
*
|
||||||
* This means that all those stores will be pinged around the same time.
|
* This means that all those stores will be pinged around the same time.
|
||||||
|
|
|
@ -1,35 +1,48 @@
|
||||||
import { Utils } from "../../Utils"
|
import { Utils } from "../../Utils"
|
||||||
|
import type { FeatureCollection } from "geojson"
|
||||||
|
|
||||||
export interface TagInfoStats {
|
export interface TagInfoStats {
|
||||||
/**
|
/**
|
||||||
* The total number of entries in the data array, **not** the total number of objects known in OSM!
|
* The total number of entries in the data array, **not** the total number of objects known in OSM!
|
||||||
*
|
*
|
||||||
* Use `data.find(item => item.type==="all").count` for this
|
* Use `data.find(item => item.type==="all").count` for this
|
||||||
|
* @deprecated: you probably want to use data.find(item => item.type==="all").count
|
||||||
*/
|
*/
|
||||||
total: number
|
total: number
|
||||||
data: {
|
data: {
|
||||||
type: "all" | "nodes" | "ways" | "relations"
|
type: "all" | "nodes" | "ways" | "relations"
|
||||||
|
// Absolute number
|
||||||
count: number
|
count: number
|
||||||
|
// Relative, percentage
|
||||||
count_fraction: number
|
count_fraction: number
|
||||||
}[]
|
}[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface GeofabrikCountryProperties {
|
||||||
|
id: string,
|
||||||
|
parent: string | "europe" | "asia",
|
||||||
|
urls: string[],
|
||||||
|
name: string,
|
||||||
|
"iso3166-1:alpha2": string[]
|
||||||
|
}
|
||||||
|
|
||||||
export default class TagInfo {
|
export default class TagInfo {
|
||||||
public static readonly global = new TagInfo()
|
public static readonly global = new TagInfo()
|
||||||
private readonly _backend: string
|
private readonly _backend: string
|
||||||
|
private static _geofabrikCountries = undefined
|
||||||
|
|
||||||
constructor(backend = "https://taginfo.openstreetmap.org/") {
|
constructor(backend = "https://taginfo.openstreetmap.org/") {
|
||||||
this._backend = backend
|
this._backend = backend
|
||||||
}
|
}
|
||||||
|
|
||||||
public getStats(key: string, value?: string): Promise<TagInfoStats> {
|
public async getStats(key: string, value?: string): Promise<TagInfoStats> {
|
||||||
let url: string
|
let url: string
|
||||||
if (value) {
|
if (value) {
|
||||||
url = `${this._backend}api/4/tag/stats?key=${key}&value=${value}`
|
url = `${this._backend}api/4/tag/stats?key=${key}&value=${value}`
|
||||||
} else {
|
} else {
|
||||||
url = `${this._backend}api/4/key/stats?key=${key}`
|
url = `${this._backend}api/4/key/stats?key=${key}`
|
||||||
}
|
}
|
||||||
return Utils.downloadJsonCached(url, 1000 * 60 * 60)
|
return await Utils.downloadJsonCached<TagInfoStats>(url, 1000 * 60 * 60)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -44,4 +57,65 @@ export default class TagInfo {
|
||||||
return `${this._backend}/keys/${k}#overview`
|
return `${this._backend}/keys/${k}#overview`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does not work in the browser due to some resources not being CORS-accessible
|
||||||
|
*/
|
||||||
|
public static async geofabrikCountries(): Promise<GeofabrikCountryProperties[]> {
|
||||||
|
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)
|
||||||
|
return TagInfo._geofabrikCountries
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a TagInfo-api-object for geofabrik for the given country.
|
||||||
|
* Returns undefined if geofabrik does not have such a country
|
||||||
|
*
|
||||||
|
* Does not work in the browser due to some resources not being CORS-accessible
|
||||||
|
* @param countryCode: an iso3166-1:alpha2 code
|
||||||
|
*/
|
||||||
|
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)
|
||||||
|
if (!country || !country?.parent || !country?.id) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
const url = `https://taginfo.geofabrik.de/${country.parent}:${country.id}/`
|
||||||
|
return new TagInfo(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async getDistributionsFor(countryCode: string, key: string, value?: string): Promise<TagInfoStats>{
|
||||||
|
if (!countryCode) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
const ti = await TagInfo.getInstanceFor(countryCode)
|
||||||
|
if (!ti) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return await ti.getStats(key, value)
|
||||||
|
} catch (e) {
|
||||||
|
console.warn("Could not fetch info for", countryCode,key,value, "due to", e)
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async getGlobalDistributionsFor(key: string, value?: string): Promise<Record<string, TagInfoStats>> {
|
||||||
|
const countries = await this.geofabrikCountries()
|
||||||
|
const perCountry: Record<string, TagInfoStats> = {}
|
||||||
|
const results = await Promise.all(countries.map(country => TagInfo.getDistributionsFor(country?.["iso3166-1:alpha2"]?.[0], key, value)))
|
||||||
|
for (let i = 0; i < countries.length; i++){
|
||||||
|
const country = countries[i]
|
||||||
|
const countryCode = country["iso3166-1:alpha2"]?.[0]
|
||||||
|
if(results[i]){
|
||||||
|
perCountry[countryCode] = results[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return perCountry
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
12
src/Utils.ts
12
src/Utils.ts
|
@ -1011,11 +1011,11 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async downloadJsonCached(
|
public static async downloadJsonCached<T = object | []>(
|
||||||
url: string,
|
url: string,
|
||||||
maxCacheTimeMs: number,
|
maxCacheTimeMs: number,
|
||||||
headers?: Record<string, string>
|
headers?: Record<string, string>
|
||||||
): Promise<object> {
|
): Promise<T> {
|
||||||
const result = await Utils.downloadJsonCachedAdvanced(url, maxCacheTimeMs, headers)
|
const result = await Utils.downloadJsonCachedAdvanced(url, maxCacheTimeMs, headers)
|
||||||
if (result["content"]) {
|
if (result["content"]) {
|
||||||
return result["content"]
|
return result["content"]
|
||||||
|
@ -1023,11 +1023,11 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
||||||
throw result["error"]
|
throw result["error"]
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async downloadJsonCachedAdvanced(
|
public static async downloadJsonCachedAdvanced<T = object | []>(
|
||||||
url: string,
|
url: string,
|
||||||
maxCacheTimeMs: number,
|
maxCacheTimeMs: number,
|
||||||
headers?: Record<string, string>
|
headers?: Record<string, string>
|
||||||
): Promise<{ content: object | [] } | { error: string; url: string; statuscode?: number }> {
|
): Promise<{ content: T } | { error: string; url: string; statuscode?: number }> {
|
||||||
const cached = Utils._download_cache.get(url)
|
const cached = Utils._download_cache.get(url)
|
||||||
if (cached !== undefined) {
|
if (cached !== undefined) {
|
||||||
if (new Date().getTime() - cached.timestamp <= maxCacheTimeMs) {
|
if (new Date().getTime() - cached.timestamp <= maxCacheTimeMs) {
|
||||||
|
@ -1042,10 +1042,10 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
||||||
Utils._download_cache.set(url, { promise, timestamp: new Date().getTime() })
|
Utils._download_cache.set(url, { promise, timestamp: new Date().getTime() })
|
||||||
return await promise
|
return await promise
|
||||||
}
|
}
|
||||||
public static async downloadJson(
|
public static async downloadJson<T = object | []>(
|
||||||
url: string,
|
url: string,
|
||||||
headers?: Record<string, string>
|
headers?: Record<string, string>
|
||||||
): Promise<object | []>
|
): Promise<T>
|
||||||
public static async downloadJson<T>(
|
public static async downloadJson<T>(
|
||||||
url: string,
|
url: string,
|
||||||
headers?: Record<string, string>
|
headers?: Record<string, string>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue