339 lines
12 KiB
TypeScript
339 lines
12 KiB
TypeScript
import Script from "./Script"
|
|
import { LayerConfigJson } from "../src/Models/ThemeConfig/Json/LayerConfigJson"
|
|
import { existsSync, readFileSync, writeFileSync } from "fs"
|
|
import { AllSharedLayers } from "../src/Customizations/AllSharedLayers"
|
|
import { AllKnownLayoutsLazy } from "../src/Customizations/AllKnownLayouts"
|
|
import { Utils } from "../src/Utils"
|
|
import { AddEditingElements } from "../src/Models/ThemeConfig/Conversion/PrepareLayer"
|
|
import {
|
|
MappingConfigJson,
|
|
QuestionableTagRenderingConfigJson,
|
|
} from "../src/Models/ThemeConfig/Json/QuestionableTagRenderingConfigJson"
|
|
import { TagConfigJson } from "../src/Models/ThemeConfig/Json/TagConfigJson"
|
|
import { TagUtils } from "../src/Logic/Tags/TagUtils"
|
|
import { TagRenderingConfigJson } from "../src/Models/ThemeConfig/Json/TagRenderingConfigJson"
|
|
import { Translatable } from "../src/Models/ThemeConfig/Json/Translatable"
|
|
|
|
export class GenerateFavouritesLayer extends Script {
|
|
private readonly layers: LayerConfigJson[] = []
|
|
|
|
constructor() {
|
|
super("Prepares the 'favourites'-layer")
|
|
const allThemes = new AllKnownLayoutsLazy(false).values()
|
|
for (const theme of allThemes) {
|
|
if (theme.hideFromOverview) {
|
|
continue
|
|
}
|
|
for (const layer of theme.layers) {
|
|
if (!layer.source) {
|
|
continue
|
|
}
|
|
if (layer.source.geojsonSource) {
|
|
continue
|
|
}
|
|
const layerConfig = AllSharedLayers.getSharedLayersConfigs().get(layer.id)
|
|
if (!layerConfig) {
|
|
continue
|
|
}
|
|
this.layers.push(layerConfig)
|
|
}
|
|
}
|
|
}
|
|
|
|
async main(args: string[]): Promise<void> {
|
|
console.log("Generating the favourite layer: stealing _all_ tagRenderings")
|
|
const proto = this.readLayer("favourite/favourite.proto.json")
|
|
this.addTagRenderings(proto)
|
|
this.addTitle(proto)
|
|
proto.titleIcons = this.generateTitleIcons()
|
|
const targetContent = JSON.stringify(proto, null, " ")
|
|
const path = "./assets/layers/favourite/favourite.json"
|
|
if (existsSync(path)) {
|
|
if (readFileSync(path, "utf8") === targetContent) {
|
|
console.log(
|
|
"Already existing favourite layer is identical to the generated one, not writing"
|
|
)
|
|
return
|
|
}
|
|
}
|
|
console.log("Written favourite layer to", path)
|
|
writeFileSync(path, targetContent)
|
|
}
|
|
|
|
private sortMappings(mappings: MappingConfigJson[]): MappingConfigJson[] {
|
|
const sortedMappings: MappingConfigJson[] = [...mappings]
|
|
sortedMappings.sort((a, b) => {
|
|
const aTag = TagUtils.Tag(a.if)
|
|
const bTag = TagUtils.Tag(b.if)
|
|
const aPop = TagUtils.GetPopularity(aTag)
|
|
const bPop = TagUtils.GetPopularity(bTag)
|
|
return aPop - bPop
|
|
})
|
|
|
|
return sortedMappings
|
|
}
|
|
|
|
private addTagRenderings(proto: LayerConfigJson) {
|
|
const blacklistedIds = new Set([
|
|
"images",
|
|
"questions",
|
|
"mapillary",
|
|
"leftover-questions",
|
|
"last_edit",
|
|
"minimap",
|
|
"move-button",
|
|
"delete-button",
|
|
"all-tags",
|
|
"all_tags",
|
|
...AddEditingElements.addedElements,
|
|
])
|
|
|
|
const generatedTagRenderings: (string | QuestionableTagRenderingConfigJson)[] = []
|
|
const trPerId = new Map<
|
|
string,
|
|
{ conditions: TagConfigJson[]; tr: QuestionableTagRenderingConfigJson }
|
|
>()
|
|
for (const layerConfig of this.layers) {
|
|
if (!layerConfig.tagRenderings) {
|
|
continue
|
|
}
|
|
for (const tagRendering of layerConfig.tagRenderings) {
|
|
if (typeof tagRendering === "string") {
|
|
if (blacklistedIds.has(tagRendering)) {
|
|
continue
|
|
}
|
|
generatedTagRenderings.push(tagRendering)
|
|
blacklistedIds.add(tagRendering)
|
|
continue
|
|
}
|
|
if (tagRendering["builtin"]) {
|
|
continue
|
|
}
|
|
const id = tagRendering.id
|
|
if (blacklistedIds.has(id)) {
|
|
continue
|
|
}
|
|
if (trPerId.has(id)) {
|
|
const old = trPerId.get(id).tr
|
|
|
|
// We need to figure out if this was a 'recycled' tag rendering or just happens to have the same id
|
|
function isSame(fieldName: string) {
|
|
return old[fieldName]?.["en"] === tagRendering[fieldName]?.["en"]
|
|
}
|
|
|
|
const sameQuestion = isSame("question") && isSame("render")
|
|
if (!sameQuestion) {
|
|
const newTr = <QuestionableTagRenderingConfigJson>Utils.Clone(tagRendering)
|
|
newTr.id = layerConfig.id + "_" + newTr.id
|
|
if (blacklistedIds.has(newTr.id)) {
|
|
continue
|
|
}
|
|
newTr.condition = {
|
|
and: Utils.NoNull([newTr.condition, layerConfig.source["osmTags"]]),
|
|
}
|
|
generatedTagRenderings.push(newTr)
|
|
blacklistedIds.add(newTr.id)
|
|
continue
|
|
}
|
|
}
|
|
if (!trPerId.has(id)) {
|
|
const newTr = <QuestionableTagRenderingConfigJson>Utils.Clone(tagRendering)
|
|
generatedTagRenderings.push(newTr)
|
|
trPerId.set(newTr.id, { tr: newTr, conditions: [] })
|
|
}
|
|
const conditions = trPerId.get(id).conditions
|
|
if (tagRendering["condition"]) {
|
|
conditions.push({
|
|
and: [tagRendering["condition"], layerConfig.source["osmTags"]],
|
|
})
|
|
} else {
|
|
conditions.push(layerConfig.source["osmTags"])
|
|
}
|
|
}
|
|
}
|
|
|
|
for (const { tr, conditions } of Array.from(trPerId.values())) {
|
|
const optimized = TagUtils.optimzeJson({ or: conditions })
|
|
if (optimized === true) {
|
|
continue
|
|
}
|
|
if (optimized === false) {
|
|
throw "Optimized into 'false', this is weird..."
|
|
}
|
|
tr.condition = optimized
|
|
}
|
|
|
|
const allTags: QuestionableTagRenderingConfigJson = {
|
|
id: "all-tags",
|
|
render: { "*": "{all_tags()}" },
|
|
|
|
metacondition: {
|
|
or: [
|
|
"__featureSwitchIsDebugging=true",
|
|
"mapcomplete-show_tags=full",
|
|
"mapcomplete-show_debug=yes",
|
|
],
|
|
},
|
|
}
|
|
proto.tagRenderings = [
|
|
"images",
|
|
...generatedTagRenderings,
|
|
...proto.tagRenderings,
|
|
"questions",
|
|
allTags,
|
|
]
|
|
}
|
|
|
|
/**
|
|
* const titleIcons = new GenerateFavouritesLayer().generateTitleIcons()
|
|
* JSON.stringify(titleIcons).indexOf("icons.defaults") // => -1
|
|
* */
|
|
private generateTitleIcons(): TagRenderingConfigJson[] {
|
|
let iconsLibrary: Map<string, TagRenderingConfigJson[]> = new Map<
|
|
string,
|
|
TagRenderingConfigJson[]
|
|
>()
|
|
const path = "./src/assets/generated/layers/icons.json"
|
|
if (existsSync(path)) {
|
|
const config = <LayerConfigJson>JSON.parse(readFileSync(path, "utf8"))
|
|
for (const tagRendering of config.tagRenderings) {
|
|
const qtr = <QuestionableTagRenderingConfigJson>tagRendering
|
|
const id = qtr.id
|
|
if (id) {
|
|
iconsLibrary.set(id, [qtr])
|
|
}
|
|
for (const label of tagRendering["labels"] ?? []) {
|
|
if (!iconsLibrary.has(label)) {
|
|
iconsLibrary.set(label, [])
|
|
}
|
|
iconsLibrary.get(label).push(qtr)
|
|
}
|
|
}
|
|
}
|
|
let titleIcons: TagRenderingConfigJson[] = []
|
|
const seenTitleIcons = new Set<string>()
|
|
for (const layer of this.layers) {
|
|
for (const titleIcon of layer.titleIcons) {
|
|
if (typeof titleIcon === "string") {
|
|
continue
|
|
}
|
|
if (titleIcon["labels"]?.indexOf("defaults") >= 0) {
|
|
continue
|
|
}
|
|
if (titleIcon.id === "iconsdefaults") {
|
|
continue
|
|
}
|
|
|
|
if (titleIcon.id === "rating") {
|
|
if (!seenTitleIcons.has("rating")) {
|
|
titleIcons.unshift(...iconsLibrary.get("rating"))
|
|
seenTitleIcons.add("rating")
|
|
}
|
|
continue
|
|
}
|
|
if (seenTitleIcons.has(titleIcon.id)) {
|
|
continue
|
|
}
|
|
seenTitleIcons.add(titleIcon.id)
|
|
console.log("Adding ", titleIcon.id)
|
|
titleIcons.push(titleIcon)
|
|
}
|
|
}
|
|
titleIcons.push(...(iconsLibrary.get("defaults") ?? []))
|
|
return titleIcons
|
|
}
|
|
|
|
private addTitle(proto: LayerConfigJson) {
|
|
let mappings: MappingConfigJson[] = []
|
|
for (const layer of this.layers) {
|
|
const t = layer.title
|
|
const tags: TagConfigJson = layer.source["osmTags"]
|
|
if (!t) {
|
|
continue
|
|
}
|
|
if (typeof t === "string") {
|
|
mappings.push({ if: tags, then: t })
|
|
} else if (t["render"] !== undefined || t["mappings"] !== undefined) {
|
|
const tr = <TagRenderingConfigJson>t
|
|
for (let i = 0; i < (tr.mappings ?? []).length; i++) {
|
|
const mapping = tr.mappings[i]
|
|
const optimized = TagUtils.optimzeJson({
|
|
and: [mapping.if, tags],
|
|
})
|
|
if (optimized === false) {
|
|
console.warn(
|
|
"The following tags yielded 'false':",
|
|
JSON.stringify(mapping.if),
|
|
JSON.stringify(tags)
|
|
)
|
|
continue
|
|
}
|
|
if (optimized === true) {
|
|
console.error(
|
|
"The following tags yielded 'false':",
|
|
JSON.stringify(mapping.if),
|
|
JSON.stringify(tags)
|
|
)
|
|
throw "Tags for title optimized to true"
|
|
}
|
|
|
|
if (!mapping.then) {
|
|
throw (
|
|
"The title has a missing 'then' for mapping " +
|
|
i +
|
|
" in layer " +
|
|
layer.id
|
|
)
|
|
}
|
|
mappings.push({
|
|
if: optimized,
|
|
then: mapping.then,
|
|
})
|
|
}
|
|
if (tr.render) {
|
|
mappings.push({
|
|
if: tags,
|
|
then: <Translatable>tr.render,
|
|
})
|
|
}
|
|
} else {
|
|
mappings.push({ if: tags, then: <Record<string, string>>t })
|
|
}
|
|
}
|
|
|
|
mappings = this.sortMappings(mappings)
|
|
|
|
if (proto.title["mappings"]) {
|
|
mappings.unshift(...proto.title["mappings"])
|
|
}
|
|
if (proto.title["render"]) {
|
|
mappings.push({
|
|
if: "id~*",
|
|
then: proto.title["render"],
|
|
})
|
|
}
|
|
|
|
for (const mapping of mappings) {
|
|
const opt = TagUtils.optimzeJson(mapping.if)
|
|
if (typeof opt === "boolean") {
|
|
continue
|
|
}
|
|
mapping.if = opt
|
|
}
|
|
|
|
proto.title = {
|
|
mappings,
|
|
}
|
|
}
|
|
|
|
private readLayer(path: string): LayerConfigJson {
|
|
try {
|
|
return JSON.parse(readFileSync("./assets/layers/" + path, "utf8"))
|
|
} catch (e) {
|
|
console.error("Could not read ./assets/layers/" + path)
|
|
throw e
|
|
}
|
|
}
|
|
}
|
|
|
|
new GenerateFavouritesLayer().run()
|