/*** * Parses presets from the iD repository and extracts some usefull tags from them */ import ScriptUtils from "./ScriptUtils"; import {existsSync, readFileSync, writeFileSync} from "fs"; import * as known_languages from "../assets/language_native.json" import {LayerConfigJson} from "../Models/ThemeConfig/Json/LayerConfigJson"; import { MappingConfigJson, QuestionableTagRenderingConfigJson } from "../Models/ThemeConfig/Json/QuestionableTagRenderingConfigJson"; import SmallLicense from "../Models/smallLicense"; 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 _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: "circle:white;./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 } 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(",") } } let 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')) } } const targetDir = "./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("../id-tagging-schema/", iconThief) const shopLayerPath = targetDir + "id_presets.json" const idPresets = JSON.parse(readFileSync(shopLayerPath, 'utf8')) idPresets.tagRenderings = [ { id: "shop_types", mappings: thief.readShopPresets() }, { id: "shop_rendering", mappings: thief.readShopIcons() } ] writeFileSync(shopLayerPath, JSON.stringify(idPresets, null, " "), 'utf8')