/*** * 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 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 = {} 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 = { en: preset.name, } const terms: Record = { 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 { 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 = 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()