forked from MapComplete/MapComplete
		
	Move NSI-code in separate file
This commit is contained in:
		
							parent
							
								
									037887fea0
								
							
						
					
					
						commit
						07edee550c
					
				
					 4 changed files with 428 additions and 71 deletions
				
			
		
							
								
								
									
										153
									
								
								src/Logic/Web/NameSuggestionIndex.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										153
									
								
								src/Logic/Web/NameSuggestionIndex.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,153 @@ | |||
| import * as nsi from "../../../node_modules/name-suggestion-index/dist/nsi.json" | ||||
| import * as nsiFeatures from "../../../node_modules/name-suggestion-index/dist/featureCollection.json" | ||||
| import { LocationConflation } from "@rapideditor/location-conflation" | ||||
| import type { Feature, FeatureCollection, MultiPolygon } from "geojson" | ||||
| import * as turf from "@turf/turf" | ||||
| import { Utils } from "../../Utils" | ||||
| import TagInfo from "./TagInfo" | ||||
| 
 | ||||
| /** | ||||
|  * Main name suggestion index file | ||||
|  */ | ||||
| interface NSIFile { | ||||
|     _meta: { | ||||
|         version: string | ||||
|         generated: string | ||||
|         url: string | ||||
|         hash: string | ||||
|     } | ||||
|     nsi: { | ||||
|         [path: string]: NSIEntry | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * A collection of brands/operators/flagpoles/... with common properties | ||||
|  * See https://github.com/osmlab/name-suggestion-index/wiki/Category-Files for an introduction and
 | ||||
|  * https://github.com/osmlab/name-suggestion-index/blob/main/schema/categories.json for a full breakdown
 | ||||
|  */ | ||||
| interface NSIEntry { | ||||
|     properties: { | ||||
|         path: string | ||||
|         skipCollection?: boolean | ||||
|         preserveTags?: string[] | ||||
|         exclude: unknown | ||||
|     } | ||||
|     items: NSIItem[] | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Represents a single brand/operator/flagpole/... | ||||
|  */ | ||||
| export interface NSIItem { | ||||
|     displayName: string | ||||
|     id: string | ||||
|     locationSet: { | ||||
|         include: string[], | ||||
|         exclude: string[] | ||||
|     } | ||||
|     tags: { | ||||
|         [key: string]: string | ||||
|     } | ||||
|     fromTemplate?: boolean | ||||
| } | ||||
| 
 | ||||
| export default class NameSuggestionIndex { | ||||
| 
 | ||||
|     private static readonly nsiFile: Readonly<NSIFile> = <any>nsi | ||||
|     private static loco = new LocationConflation(nsiFeatures) // Some additional boundaries
 | ||||
| 
 | ||||
|     private static _supportedTypes: string[] | ||||
| 
 | ||||
|     public static 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]) | ||||
|         this._supportedTypes = Utils.Dedup(all) | ||||
|         return this._supportedTypes | ||||
|     } | ||||
| 
 | ||||
|     public static async buildTaginfoCountsPerCountry(type = "brand", key: string, value: string) { | ||||
|         const allData: { nsi: NSIItem, stats }[] = [] | ||||
|         const brands = NameSuggestionIndex.getSuggestionsFor(type, key, value) | ||||
|         for (const brand of brands) { | ||||
|             const brandValue = brand.tags[type] | ||||
|             const allStats = await TagInfo.getGlobalDistributionsFor(type, brandValue) | ||||
|             allData.push({ nsi: brand, stats: allStats }) | ||||
|         } | ||||
|         return allData | ||||
|     } | ||||
| 
 | ||||
|     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)) { | ||||
|                 continue | ||||
|             } | ||||
|             if (!tags[osmkey]) { | ||||
|                 tags[osmkey] = [] | ||||
|             } | ||||
|             tags[osmkey].push(osmvalue) | ||||
|         } | ||||
|         return tags | ||||
|     } | ||||
| 
 | ||||
|     public static allPossible(type: "brand" | "operator"): string[] { | ||||
|         const options: string[] = [] | ||||
|         const tags = NameSuggestionIndex.supportedTags(type) | ||||
|         for (const osmKey in tags) { | ||||
|             const values = tags[osmKey] | ||||
|             for (const osmValue of values) { | ||||
|                 const suggestions = this.getSuggestionsFor(type, osmKey, osmValue) | ||||
|                 for (const suggestion of suggestions) { | ||||
|                     const value = suggestion.tags[type] | ||||
|                     options.push(value) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return Utils.Dedup(options) | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * | ||||
|      * @param path | ||||
|      * @param country | ||||
|      * @param location: center point of the feature, should be [lon, lat] | ||||
|      */ | ||||
|     public static getSuggestionsFor(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 => { | ||||
|             if (i.locationSet.include.indexOf("001") >= 0) { | ||||
|                 return true | ||||
|             } | ||||
| 
 | ||||
|             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)) { | ||||
|                 return true | ||||
|             } | ||||
| 
 | ||||
|             if (location === undefined) { | ||||
|                 return true | ||||
|             } | ||||
|             const resolvedSet = NameSuggestionIndex.loco.resolveLocationSet(i.locationSet) | ||||
|             if (resolvedSet) { | ||||
|                 // We actually have a location set, so we can check if the feature is in it, by determining if our point is inside the MultiPolygon using @turf/boolean-point-in-polygon
 | ||||
|                 // This might occur for some extra boundaries, such as counties, ...
 | ||||
|                 const setFeature: Feature<MultiPolygon> = resolvedSet.feature | ||||
|                 return turf.booleanPointInPolygon(location, setFeature.geometry) | ||||
|             } | ||||
| 
 | ||||
|             return false | ||||
|         }) | ||||
|     } | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue