forked from MapComplete/MapComplete
		
	
		
			
				
	
	
		
			351 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			351 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| /***
 | |
|  * Parses presets from the iD repository and extracts some usefull tags from them
 | |
|  */
 | |
| import ScriptUtils from "../ScriptUtils"
 | |
| import { existsSync, readFileSync, writeFileSync } from "fs"
 | |
| import known_languages from "../../src/assets/language_native.json"
 | |
| import { LayerConfigJson } from "../../src/Models/ThemeConfig/Json/LayerConfigJson"
 | |
| import { MappingConfigJson } from "../../src/Models/ThemeConfig/Json/QuestionableTagRenderingConfigJson"
 | |
| import SmallLicense from "../../src/Models/smallLicense"
 | |
| import Script from "../Script"
 | |
| 
 | |
| interface IconThief {
 | |
|     steal(iconName: string): boolean
 | |
| }
 | |
| 
 | |
| interface IdPresetJson {
 | |
|     icon: string
 | |
|     geometry: ("point" | "line" | "area")[]
 | |
|     /**
 | |
|      * Extra search terms
 | |
|      */
 | |
|     terms: string[]
 | |
|     tags: Record<string, string>
 | |
|     name: string
 | |
|     searchable?: boolean
 | |
| }
 | |
| 
 | |
| class IdPreset implements IdPresetJson {
 | |
|     private readonly _preset: IdPresetJson
 | |
| 
 | |
|     constructor(preset: IdPresetJson) {
 | |
|         this._preset = preset
 | |
|     }
 | |
| 
 | |
|     public get searchable(): boolean {
 | |
|         return this._preset.searchable
 | |
|     }
 | |
| 
 | |
|     public get name() {
 | |
|         return this._preset.name
 | |
|     }
 | |
| 
 | |
|     public get terms() {
 | |
|         return this._preset.terms
 | |
|     }
 | |
| 
 | |
|     public get tags() {
 | |
|         return this._preset.tags
 | |
|     }
 | |
| 
 | |
|     public get geometry() {
 | |
|         return this._preset.geometry
 | |
|     }
 | |
| 
 | |
|     public get icon(): string {
 | |
|         return this._preset.icon
 | |
|     }
 | |
| 
 | |
|     static fromFile(file: string): IdPreset {
 | |
|         return new IdPreset(JSON.parse(readFileSync(file, "utf8")))
 | |
|     }
 | |
| 
 | |
|     public parseTags(): string | { and: string[] } {
 | |
|         const preset = this._preset
 | |
|         const tagKeys = Object.keys(preset.tags)
 | |
|         if (tagKeys.length === 1) {
 | |
|             return tagKeys[0] + "=" + preset.tags[tagKeys[0]]
 | |
|         } else {
 | |
|             return {
 | |
|                 and: tagKeys.map((key) => key + "=" + preset.tags[key]),
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| class MakiThief implements IconThief {
 | |
|     public readonly _prefix: string
 | |
|     private readonly _directory: string
 | |
|     private readonly _license: SmallLicense
 | |
|     private readonly _targetDir: string
 | |
| 
 | |
|     constructor(
 | |
|         directory: string,
 | |
|         targetDir: string,
 | |
|         license: SmallLicense,
 | |
|         prefix: string = "maki-"
 | |
|     ) {
 | |
|         this._license = license
 | |
|         this._directory = directory
 | |
|         this._targetDir = targetDir
 | |
|         this._prefix = prefix
 | |
|     }
 | |
| 
 | |
|     public steal(iconName: string): boolean {
 | |
|         const target = this._targetDir + iconName + ".svg"
 | |
|         if (existsSync(target)) {
 | |
|             return true
 | |
|         }
 | |
|         try {
 | |
|             const file = readFileSync(this._directory + iconName + ".svg", "utf8")
 | |
|             writeFileSync(target, file, "utf8")
 | |
| 
 | |
|             writeFileSync(
 | |
|                 target + ".license_info.json",
 | |
|                 JSON.stringify({ ...this._license, path: this._prefix + iconName + ".svg" }),
 | |
|                 "utf8"
 | |
|             )
 | |
|             console.log("Successfully stolen " + iconName)
 | |
|             return true
 | |
|         } catch (e) {
 | |
|             console.log("Could not steal " + iconName + " due to " + e.message)
 | |
|             return false
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| class AggregateIconThief implements IconThief {
 | |
|     private readonly makiThiefs: MakiThief[]
 | |
| 
 | |
|     constructor(makiThiefs: MakiThief[]) {
 | |
|         this.makiThiefs = makiThiefs
 | |
|     }
 | |
| 
 | |
|     public steal(iconName: string): boolean {
 | |
|         for (const makiThief1 of this.makiThiefs) {
 | |
|             if (iconName.startsWith(makiThief1._prefix)) {
 | |
|                 return makiThief1.steal(iconName.substr(makiThief1._prefix.length))
 | |
|             }
 | |
|         }
 | |
|         return false
 | |
|     }
 | |
| }
 | |
| 
 | |
| class IdThief {
 | |
|     private readonly _idPresetsRepository: string
 | |
| 
 | |
|     private readonly _tranlationFiles: Record<string, object> = {}
 | |
|     private readonly _knownLanguages: string[]
 | |
|     private readonly _iconThief: IconThief
 | |
| 
 | |
|     public constructor(idPresetsRepository: string, iconThief: IconThief) {
 | |
|         this._idPresetsRepository = idPresetsRepository
 | |
|         this._iconThief = iconThief
 | |
|         const knownById = ScriptUtils.readDirRecSync(
 | |
|             `${this._idPresetsRepository}/dist/translations/`
 | |
|         )
 | |
|             .map((pth) => pth.substring(pth.lastIndexOf("/") + 1, pth.length - ".json".length))
 | |
|             .filter((lng) => !lng.endsWith(".min"))
 | |
|         const missing = Object.keys(known_languages).filter(
 | |
|             (lng) => knownById.indexOf(lng.replace("-", "_")) < 0
 | |
|         )
 | |
|         this._knownLanguages = knownById.filter((lng) => known_languages[lng] !== undefined)
 | |
|         console.log(
 | |
|             "Id knows following languages:",
 | |
|             this._knownLanguages.join(", "),
 | |
|             "missing:",
 | |
|             missing
 | |
|         )
 | |
|     }
 | |
| 
 | |
|     public getTranslation(language: string, ...path: string[]): string {
 | |
|         let obj = this.loadTranslationFile(language)[language]
 | |
|         for (const p of path) {
 | |
|             obj = obj[p]
 | |
|             if (obj === undefined) {
 | |
|                 return undefined
 | |
|             }
 | |
|         }
 | |
|         return obj
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Creates a mapRendering-mapping for the 'shop' theme
 | |
|      */
 | |
|     public readShopIcons(): { if: string | { and: string[] }; then: string }[] {
 | |
|         const dir = this._idPresetsRepository + "/data/presets/shop"
 | |
| 
 | |
|         const mappings: {
 | |
|             if: string | { and: string[] }
 | |
|             then: string
 | |
|         }[] = []
 | |
|         const files = ScriptUtils.readDirRecSync(dir, 1)
 | |
|         for (const file of files) {
 | |
|             const preset = IdPreset.fromFile(file)
 | |
| 
 | |
|             if (!this._iconThief.steal(preset.icon)) {
 | |
|                 continue
 | |
|             }
 | |
| 
 | |
|             const mapping = {
 | |
|                 if: preset.parseTags(),
 | |
|                 then: "./assets/layers/id_presets/" + preset.icon + ".svg",
 | |
|             }
 | |
|             mappings.push(mapping)
 | |
|         }
 | |
| 
 | |
|         return mappings
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Creates a tagRenderingConfigJson for the 'shop' theme
 | |
|      */
 | |
|     public readShopPresets(): MappingConfigJson[] {
 | |
|         const dir = this._idPresetsRepository + "/data/presets/shop"
 | |
| 
 | |
|         const mappings: MappingConfigJson[] = []
 | |
|         const files = ScriptUtils.readDirRecSync(dir, 1)
 | |
|         for (const file of files) {
 | |
|             const name = file.substring(file.lastIndexOf("/") + 1, file.length - ".json".length)
 | |
|             const preset = IdPreset.fromFile(file)
 | |
| 
 | |
|             if (preset.searchable === false) {
 | |
|                 continue
 | |
|             }
 | |
| 
 | |
|             if (preset.tags["shop"] === "vacant") {
 | |
|                 console.log("Skipping 'vacant'")
 | |
|                 continue
 | |
|             }
 | |
| 
 | |
|             console.log(`     ${name} (shop=${preset.tags["shop"]}), ${preset.icon}`)
 | |
| 
 | |
|             const thenClause: Record<string, string> = {
 | |
|                 en: preset.name,
 | |
|             }
 | |
|             const terms: Record<string, string[]> = {
 | |
|                 en: preset.terms,
 | |
|             }
 | |
|             for (const lng of this._knownLanguages) {
 | |
|                 const lngMc = lng.replace("-", "_")
 | |
|                 const tr = this.getTranslation(lng, "presets", "presets", "shop/" + name, "name")
 | |
|                 if (tr !== undefined) {
 | |
|                     thenClause[lngMc] = tr
 | |
|                 }
 | |
| 
 | |
|                 const termsTr = this.getTranslation(
 | |
|                     lng,
 | |
|                     "presets",
 | |
|                     "presets",
 | |
|                     "shop/" + name,
 | |
|                     "terms"
 | |
|                 )
 | |
|                 if (termsTr !== undefined) {
 | |
|                     terms[lngMc] = termsTr.split(",")
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             const tag = preset.parseTags()
 | |
|             const mapping: MappingConfigJson = {
 | |
|                 if: tag,
 | |
|                 then: thenClause,
 | |
|                 searchTerms: terms,
 | |
|             }
 | |
|             if (preset.tags["shop"] == "yes") {
 | |
|                 mapping["hideInAnswer"] = true
 | |
|                 mapping.if["en"] = "Unspecified shop"
 | |
|             }
 | |
| 
 | |
|             if (this._iconThief.steal(preset.icon)) {
 | |
|                 mapping["icon"] = {
 | |
|                     path: "./assets/layers/id_presets/" + preset.icon + ".svg",
 | |
|                     class: "medium",
 | |
|                 }
 | |
|             } else {
 | |
|                 console.log(preset.icon + " could not be stolen :(")
 | |
|             }
 | |
| 
 | |
|             mappings.push(mapping)
 | |
|         }
 | |
| 
 | |
|         return mappings
 | |
|     }
 | |
| 
 | |
|     private loadTranslationFile(language: string): object {
 | |
|         const cached = this._tranlationFiles[language]
 | |
|         if (cached) {
 | |
|             return cached
 | |
|         }
 | |
|         return (this._tranlationFiles[language] = JSON.parse(
 | |
|             readFileSync(`${this._idPresetsRepository}/dist/translations/${language}.json`, "utf8")
 | |
|         ))
 | |
|     }
 | |
| }
 | |
| 
 | |
| class ReadIdPresets extends Script {
 | |
|     constructor() {
 | |
|         super(
 | |
|             "Reads the id-tagging-schema repository and steals the presets; which will be written into 'id_presets.json'\n\nArguments: [path-to-repository] [path-to-target]\n" +
 | |
|                 "Note that default arguments are used"
 | |
|         )
 | |
|     }
 | |
| 
 | |
|     async main(args: string[]): Promise<void> {
 | |
|         const targetDir = args[1] ?? "./assets/layers/id_presets/"
 | |
| 
 | |
|         const makiThief = new MakiThief(
 | |
|             "../maki/icons/",
 | |
|             targetDir + "maki-",
 | |
|             {
 | |
|                 authors: ["Maki icon set"],
 | |
|                 license: "CC0",
 | |
|                 path: null,
 | |
|                 sources: ["https://github.com/mapbox/maki"],
 | |
|             },
 | |
|             "maki-"
 | |
|         )
 | |
| 
 | |
|         const temakiThief = new MakiThief(
 | |
|             "../temaki/icons/",
 | |
|             targetDir + "temaki-",
 | |
|             {
 | |
|                 authors: ["Temaki icon set"],
 | |
|                 license: "CC0",
 | |
|                 path: null,
 | |
|                 sources: ["https://github.com/ideditor/temaki"],
 | |
|             },
 | |
|             "temaki-"
 | |
|         )
 | |
|         const fasThief = new MakiThief(
 | |
|             "../Font-Awesome/svgs/solid/",
 | |
|             targetDir + "fas-",
 | |
|             {
 | |
|                 authors: ["Font-Awesome icon set"],
 | |
|                 license: "CC-BY 4.0",
 | |
|                 path: null,
 | |
|                 sources: ["https://github.com/FortAwesome/Font-Awesome"],
 | |
|             },
 | |
|             "fas-"
 | |
|         )
 | |
|         const iconThief = new AggregateIconThief([makiThief, temakiThief, fasThief])
 | |
| 
 | |
|         const thief = new IdThief(args[0] ?? "../id-tagging-schema/", iconThief)
 | |
| 
 | |
|         const id_presets_path = targetDir + "id_presets.json"
 | |
|         const idPresets = <LayerConfigJson>JSON.parse(readFileSync(id_presets_path, "utf8"))
 | |
|         idPresets.tagRenderings = [
 | |
|             {
 | |
|                 id: "shop_types",
 | |
|                 mappings: thief.readShopPresets(),
 | |
|             },
 | |
|             {
 | |
|                 id: "shop_rendering",
 | |
|                 mappings: thief.readShopIcons(),
 | |
|             },
 | |
|         ]
 | |
|         console.log("Writing id presets to", id_presets_path)
 | |
|         writeFileSync(id_presets_path, JSON.stringify(idPresets, null, "  "), "utf8")
 | |
|     }
 | |
| }
 | |
| 
 | |
| new ReadIdPresets().run()
 |