mapcomplete/scripts/thieves/readIdPresets.ts

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

352 lines
11 KiB
TypeScript
Raw Normal View History

/***
* Parses presets from the iD repository and extracts some usefull tags from them
*/
2022-07-11 09:14:26 +02:00
import ScriptUtils from "../ScriptUtils"
import { existsSync, readFileSync, writeFileSync } from "fs"
2023-10-01 13:13:07 +02:00
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"
2024-07-29 03:47:20 +02:00
import Script from "../Script"
interface IconThief {
steal(iconName: string): boolean
}
2022-07-02 02:45:59 +02:00
interface IdPresetJson {
icon: string
geometry: ("point" | "line" | "area")[]
/**
* Extra search terms
*/
terms: string[]
tags: Record<string, string>
name: string
2022-07-02 02:45:59 +02:00
searchable?: boolean
}
2022-07-02 02:45:59 +02:00
class IdPreset implements IdPresetJson {
2024-07-29 03:47:20 +02:00
private readonly _preset: IdPresetJson
2022-07-02 02:45:59 +02:00
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
2022-07-02 02:45:59 +02:00
private readonly _license: SmallLicense
private readonly _targetDir: string
2022-09-08 21:40:48 +02:00
2022-07-02 02:45:59 +02:00
constructor(
directory: string,
targetDir: string,
license: SmallLicense,
prefix: string = "maki-"
) {
this._license = license
this._directory = directory
this._targetDir = targetDir
2022-07-02 02:45:59 +02:00
this._prefix = prefix
}
2022-07-02 02:45:59 +02:00
public steal(iconName: string): boolean {
const target = this._targetDir + iconName + ".svg"
if (existsSync(target)) {
return true
}
2022-07-02 02:45:59 +02:00
try {
const file = readFileSync(this._directory + iconName + ".svg", "utf8")
writeFileSync(target, file, "utf8")
2022-07-02 02:45:59 +02:00
writeFileSync(
target + ".license_info.json",
JSON.stringify({ ...this._license, path: this._prefix + iconName + ".svg" }),
"utf8"
)
console.log("Successfully stolen " + iconName)
return true
2022-07-02 02:45:59 +02:00
} 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"))
2022-07-02 02:45:59 +02:00
const missing = Object.keys(known_languages).filter(
(lng) => knownById.indexOf(lng.replace("-", "_")) < 0
2022-09-08 21:40:48 +02:00
)
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
}
2022-07-02 02:45:59 +02:00
/**
* 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",
2022-07-02 02:45:59 +02:00
}
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) {
2022-07-02 02:45:59 +02:00
const name = file.substring(file.lastIndexOf("/") + 1, file.length - ".json".length)
const preset = IdPreset.fromFile(file)
2022-07-02 02:45:59 +02:00
if (preset.searchable === false) {
continue
}
2022-07-02 02:45:59 +02:00
2024-08-09 16:58:48 +02:00
if (preset.tags["shop"] === "vacant") {
2024-07-29 03:47:20 +02:00
console.log("Skipping 'vacant'")
continue
}
2022-07-02 02:45:59 +02:00
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("-", "_")
2022-07-02 02:45:59 +02:00
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(",")
}
}
2022-07-02 02:45:59 +02:00
2024-07-29 03:47:20 +02:00
const tag = preset.parseTags()
const mapping: MappingConfigJson = {
if: tag,
then: thenClause,
searchTerms: terms,
}
2022-07-02 02:45:59 +02:00
if (preset.tags["shop"] == "yes") {
mapping["hideInAnswer"] = true
mapping.if["en"] = "Unspecified shop"
}
2022-07-02 02:45:59 +02:00
if (this._iconThief.steal(preset.icon)) {
mapping["icon"] = {
path: "./assets/layers/id_presets/" + preset.icon + ".svg",
2022-07-02 02:45:59 +02:00
class: "medium",
}
2022-07-02 02:45:59 +02:00
} else {
console.log(preset.icon + " could not be stolen :(")
}
2022-07-02 02:45:59 +02:00
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")
))
}
}
2024-08-09 16:58:48 +02:00
class ReadIdPresets extends Script {
2024-07-29 03:47:20 +02:00
constructor() {
2024-08-09 16:58:48 +02:00
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"
)
2024-07-29 03:47:20 +02:00
}
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()