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()
 |