Themes: add first version of ski theme

This commit is contained in:
Pieter Vander Vennet 2024-01-30 18:33:30 +01:00
parent 99621549e9
commit 8f25006998
4 changed files with 136 additions and 94 deletions

View file

@ -1073,16 +1073,6 @@
"https://commons.wikimedia.org/wiki/File:Font_Awesome_5_solid_robot.svg" "https://commons.wikimedia.org/wiki/File:Font_Awesome_5_solid_robot.svg"
] ]
}, },
{
"path": "satellite.svg",
"license": "CC0-1.0",
"authors": [
"SVG Repo"
],
"sources": [
"https://www.svgrepo.com/svg/80960/satellite"
]
},
{ {
"path": "scissors.svg", "path": "scissors.svg",
"license": "CC-BY-3.0", "license": "CC-BY-3.0",

View file

@ -368,6 +368,10 @@
"if": "theme=sidewalks", "if": "theme=sidewalks",
"then": "./assets/svg/bug.svg" "then": "./assets/svg/bug.svg"
}, },
{
"if": "theme=ski",
"then": "./assets/layers/aerialway/chair_lift.svg"
},
{ {
"if": "theme=speelplekken", "if": "theme=speelplekken",
"then": "./assets/themes/speelplekken/logo.svg" "then": "./assets/themes/speelplekken/logo.svg"

View file

@ -0,0 +1,33 @@
{
"id": "ski",
"title": {
"en": "Ski pistes and aerialways"
},
"description": {
"en": "Everything you need to go skiing"
},
"icon": "./assets/layers/aerialway/chair_lift.svg",
"layers": [
"ski_piste",
"aerialway",
{
"builtin": [
"toilet",
"drinking_water",
"food",
"map",
"information_board",
"viewpoint",
"binocular"
],
"override": {
"pointRendering": [
{
"=iconSize": "25,25",
"=label":null
}
]
}
}
]
}

View file

@ -14,7 +14,11 @@ import {
import { Translation } from "../src/UI/i18n/Translation" import { Translation } from "../src/UI/i18n/Translation"
import { PrepareLayer } from "../src/Models/ThemeConfig/Conversion/PrepareLayer" import { PrepareLayer } from "../src/Models/ThemeConfig/Conversion/PrepareLayer"
import { PrepareTheme } from "../src/Models/ThemeConfig/Conversion/PrepareTheme" import { PrepareTheme } from "../src/Models/ThemeConfig/Conversion/PrepareTheme"
import { Conversion, DesugaringContext, DesugaringStep } from "../src/Models/ThemeConfig/Conversion/Conversion" import {
Conversion,
DesugaringContext,
DesugaringStep,
} from "../src/Models/ThemeConfig/Conversion/Conversion"
import { Utils } from "../src/Utils" import { Utils } from "../src/Utils"
import Script from "./Script" import Script from "./Script"
import { AllSharedLayers } from "../src/Customizations/AllSharedLayers" import { AllSharedLayers } from "../src/Customizations/AllSharedLayers"
@ -47,7 +51,7 @@ class ParseLayer extends Conversion<
convert( convert(
path: string, path: string,
context: ConversionContext, context: ConversionContext
): { ): {
parsed: LayerConfig parsed: LayerConfig
raw: LayerConfigJson raw: LayerConfigJson
@ -63,7 +67,7 @@ class ParseLayer extends Conversion<
try { try {
parsed = JSON.parse(fileContents) parsed = JSON.parse(fileContents)
} catch (e) { } catch (e) {
context.err("Could not parse file as JSON") context.err("Could not parse file as JSON: " + e)
return undefined return undefined
} }
if (parsed === undefined) { if (parsed === undefined) {
@ -102,7 +106,7 @@ class AddIconSummary extends DesugaringStep<{ raw: LayerConfigJson; parsed: Laye
const fixed = json.raw const fixed = json.raw
const layerConfig = json.parsed const layerConfig = json.parsed
const pointRendering: PointRenderingConfig = layerConfig.mapRendering.find((pr) => const pointRendering: PointRenderingConfig = layerConfig.mapRendering.find((pr) =>
pr.location.has("point"), pr.location.has("point")
) )
const defaultTags = layerConfig.GetBaseTags() const defaultTags = layerConfig.GetBaseTags()
fixed["_layerIcon"] = Utils.NoNull( fixed["_layerIcon"] = Utils.NoNull(
@ -117,7 +121,7 @@ class AddIconSummary extends DesugaringStep<{ raw: LayerConfigJson; parsed: Laye
result["color"] = c result["color"] = c
} }
return result return result
}), })
) )
return { raw: fixed, parsed: layerConfig } return { raw: fixed, parsed: layerConfig }
} }
@ -139,7 +143,7 @@ class LayerOverviewUtils extends Script {
private static extractLayerIdsFrom( private static extractLayerIdsFrom(
themeFile: LayoutConfigJson, themeFile: LayoutConfigJson,
includeInlineLayers = true, includeInlineLayers = true
): string[] { ): string[] {
const publicLayerIds = [] const publicLayerIds = []
if (!Array.isArray(themeFile.layers)) { if (!Array.isArray(themeFile.layers)) {
@ -202,10 +206,10 @@ class LayerOverviewUtils extends Script {
| LayerConfigJson | LayerConfigJson
| string | string
| { | {
builtin builtin
} }
)[] )[]
}[], }[]
) { ) {
const perId = new Map<string, any>() const perId = new Map<string, any>()
for (const theme of themes) { for (const theme of themes) {
@ -246,7 +250,7 @@ class LayerOverviewUtils extends Script {
writeFileSync( writeFileSync(
"./src/assets/generated/theme_overview.json", "./src/assets/generated/theme_overview.json",
JSON.stringify(sorted, null, " "), JSON.stringify(sorted, null, " "),
{ encoding: "utf8" }, { encoding: "utf8" }
) )
} }
@ -258,7 +262,7 @@ class LayerOverviewUtils extends Script {
writeFileSync( writeFileSync(
`${LayerOverviewUtils.themePath}${theme.id}.json`, `${LayerOverviewUtils.themePath}${theme.id}.json`,
JSON.stringify(theme, null, " "), JSON.stringify(theme, null, " "),
{ encoding: "utf8" }, { encoding: "utf8" }
) )
} }
@ -269,13 +273,13 @@ class LayerOverviewUtils extends Script {
writeFileSync( writeFileSync(
`${LayerOverviewUtils.layerPath}${layer.id}.json`, `${LayerOverviewUtils.layerPath}${layer.id}.json`,
JSON.stringify(layer, null, " "), JSON.stringify(layer, null, " "),
{ encoding: "utf8" }, { encoding: "utf8" }
) )
} }
getSharedTagRenderings( getSharedTagRenderings(
doesImageExist: DoesImageExist, doesImageExist: DoesImageExist,
bootstrapTagRenderings: Map<string, QuestionableTagRenderingConfigJson> = null, bootstrapTagRenderings: Map<string, QuestionableTagRenderingConfigJson> = null
): Map<string, QuestionableTagRenderingConfigJson> { ): Map<string, QuestionableTagRenderingConfigJson> {
const prepareLayer = new PrepareLayer({ const prepareLayer = new PrepareLayer({
tagRenderings: bootstrapTagRenderings, tagRenderings: bootstrapTagRenderings,
@ -336,8 +340,8 @@ class LayerOverviewUtils extends Script {
if (contents.indexOf("<text") > 0) { if (contents.indexOf("<text") > 0) {
console.warn( console.warn(
"The SVG at " + "The SVG at " +
path + path +
" contains a `text`-tag. This is highly discouraged. Every machine viewing your theme has their own font libary, and the font you choose might not be present, resulting in a different font being rendered. Solution: open your .svg in inkscape (or another program), select the text and convert it to a path", " contains a `text`-tag. This is highly discouraged. Every machine viewing your theme has their own font libary, and the font you choose might not be present, resulting in a different font being rendered. Solution: open your .svg in inkscape (or another program), select the text and convert it to a path"
) )
errCount++ errCount++
} }
@ -349,11 +353,19 @@ class LayerOverviewUtils extends Script {
async main(args: string[]) { async main(args: string[]) {
console.log("Generating layer overview...") console.log("Generating layer overview...")
const themeWhitelist = new Set(args.find(a => a.startsWith("--themes=")) const themeWhitelist = new Set(
?.substring("--themes=".length)?.split(",") ?? []) args
.find((a) => a.startsWith("--themes="))
?.substring("--themes=".length)
?.split(",") ?? []
)
const layerWhitelist = new Set(args.find(a => a.startsWith("--layers=")) const layerWhitelist = new Set(
?.substring("--layers=".length)?.split(",") ?? []) args
.find((a) => a.startsWith("--layers="))
?.substring("--layers=".length)
?.split(",") ?? []
)
const start = new Date() const start = new Date()
const forceReload = args.some((a) => a == "--force") const forceReload = args.some((a) => a == "--force")
@ -385,14 +397,14 @@ class LayerOverviewUtils extends Script {
themeWhitelist themeWhitelist
) )
if (recompiledThemes.length > 0){ if (recompiledThemes.length > 0) {
writeFileSync( writeFileSync(
"./src/assets/generated/known_layers.json", "./src/assets/generated/known_layers.json",
JSON.stringify({ JSON.stringify({
layers: Array.from(sharedLayers.values()).filter((l) => l.id !== "favourite"), layers: Array.from(sharedLayers.values()).filter((l) => l.id !== "favourite"),
}), })
) )
} }
const mcChangesPath = "./assets/themes/mapcomplete-changes/mapcomplete-changes.json" const mcChangesPath = "./assets/themes/mapcomplete-changes/mapcomplete-changes.json"
if ( if (
@ -411,7 +423,7 @@ class LayerOverviewUtils extends Script {
const proto: LayoutConfigJson = JSON.parse( const proto: LayoutConfigJson = JSON.parse(
readFileSync("./assets/themes/mapcomplete-changes/mapcomplete-changes.proto.json", { readFileSync("./assets/themes/mapcomplete-changes/mapcomplete-changes.proto.json", {
encoding: "utf8", encoding: "utf8",
}), })
) )
const protolayer = <LayerConfigJson>( const protolayer = <LayerConfigJson>(
proto.layers.filter((l) => l["id"] === "mapcomplete-changes")[0] proto.layers.filter((l) => l["id"] === "mapcomplete-changes")[0]
@ -428,21 +440,21 @@ class LayerOverviewUtils extends Script {
layers: ScriptUtils.getLayerFiles().map((f) => f.parsed), layers: ScriptUtils.getLayerFiles().map((f) => f.parsed),
themes: ScriptUtils.getThemeFiles().map((f) => f.parsed), themes: ScriptUtils.getThemeFiles().map((f) => f.parsed),
}, },
ConversionContext.construct([], []), ConversionContext.construct([], [])
) )
for (const [_, theme] of sharedThemes) { for (const [_, theme] of sharedThemes) {
theme.layers = theme.layers.filter( theme.layers = theme.layers.filter(
(l) => Constants.added_by_default.indexOf(l["id"]) < 0, (l) => Constants.added_by_default.indexOf(l["id"]) < 0
) )
} }
if(recompiledThemes.length > 0) { if (recompiledThemes.length > 0) {
writeFileSync( writeFileSync(
"./src/assets/generated/known_themes.json", "./src/assets/generated/known_themes.json",
JSON.stringify({ JSON.stringify({
themes: Array.from(sharedThemes.values()), themes: Array.from(sharedThemes.values()),
}), })
) )
} }
@ -451,8 +463,8 @@ class LayerOverviewUtils extends Script {
if (AllSharedLayers.getSharedLayersConfigs().size == 0) { if (AllSharedLayers.getSharedLayersConfigs().size == 0) {
console.error( console.error(
"This was a bootstrapping-run. Run generate layeroverview again!(" + "This was a bootstrapping-run. Run generate layeroverview again!(" +
millisNeeded + millisNeeded +
" ms)", " ms)"
) )
} else { } else {
const green = (s) => "\x1b[92m" + s + "\x1b[0m" const green = (s) => "\x1b[92m" + s + "\x1b[0m"
@ -463,7 +475,7 @@ class LayerOverviewUtils extends Script {
private parseLayer( private parseLayer(
doesImageExist: DoesImageExist, doesImageExist: DoesImageExist,
prepLayer: PrepareLayer, prepLayer: PrepareLayer,
sharedLayerPath: string, sharedLayerPath: string
): { ): {
raw: LayerConfigJson raw: LayerConfigJson
parsed: LayerConfig parsed: LayerConfig
@ -474,7 +486,7 @@ class LayerOverviewUtils extends Script {
const parsed = parser.convertStrict(sharedLayerPath, context) const parsed = parser.convertStrict(sharedLayerPath, context)
const result = AddIconSummary.singleton.convertStrict( const result = AddIconSummary.singleton.convertStrict(
parsed, parsed,
context.inOperation("AddIconSummary"), context.inOperation("AddIconSummary")
) )
return { ...result, context } return { ...result, context }
} }
@ -500,9 +512,12 @@ class LayerOverviewUtils extends Script {
const recompiledLayers: string[] = [] const recompiledLayers: string[] = []
let warningCount = 0 let warningCount = 0
for (const sharedLayerPath of ScriptUtils.getLayerPaths()) { for (const sharedLayerPath of ScriptUtils.getLayerPaths()) {
if(whitelist.size > 0){ if (whitelist.size > 0) {
const idByPath = sharedLayerPath.split("/").at(-1).split(".")[0] const idByPath = sharedLayerPath.split("/").at(-1).split(".")[0]
if(Constants.priviliged_layers.indexOf(<any> idByPath) < 0 && !whitelist.has(idByPath)){ if (
Constants.priviliged_layers.indexOf(<any>idByPath) < 0 &&
!whitelist.has(idByPath)
) {
continue continue
} }
} }
@ -534,17 +549,17 @@ class LayerOverviewUtils extends Script {
console.log( console.log(
"Recompiled layers " + "Recompiled layers " +
recompiledLayers.join(", ") + recompiledLayers.join(", ") +
" and skipped " + " and skipped " +
skippedLayers.length + skippedLayers.length +
" layers. Detected " + " layers. Detected " +
warningCount + warningCount +
" warnings", " warnings"
) )
// We always need the calculated tags of 'usersettings', so we export them separately // We always need the calculated tags of 'usersettings', so we export them separately
this.extractJavascriptCodeForLayer( this.extractJavascriptCodeForLayer(
state.sharedLayers.get("usersettings"), state.sharedLayers.get("usersettings"),
"./src/Logic/State/UserSettingsMetaTagging.ts", "./src/Logic/State/UserSettingsMetaTagging.ts"
) )
return sharedLayers return sharedLayers
@ -561,8 +576,8 @@ class LayerOverviewUtils extends Script {
private extractJavascriptCode(themeFile: LayoutConfigJson) { private extractJavascriptCode(themeFile: LayoutConfigJson) {
const allCode = [ const allCode = [
"import {Feature} from 'geojson'", "import {Feature} from 'geojson'",
"import { ExtraFuncType } from \"../../../Logic/ExtraFunctions\";", 'import { ExtraFuncType } from "../../../Logic/ExtraFunctions";',
"import { Utils } from \"../../../Utils\"", 'import { Utils } from "../../../Utils"',
"export class ThemeMetaTagging {", "export class ThemeMetaTagging {",
" public static readonly themeName = " + JSON.stringify(themeFile.id), " public static readonly themeName = " + JSON.stringify(themeFile.id),
"", "",
@ -574,8 +589,8 @@ class LayerOverviewUtils extends Script {
allCode.push( allCode.push(
" public metaTaggging_for_" + " public metaTaggging_for_" +
id + id +
"(feat: Feature, helperFunctions: Record<ExtraFuncType, (feature: Feature) => Function>) {", "(feat: Feature, helperFunctions: Record<ExtraFuncType, (feature: Feature) => Function>) {"
) )
allCode.push(" const {" + ExtraFunctions.types.join(", ") + "} = helperFunctions") allCode.push(" const {" + ExtraFunctions.types.join(", ") + "} = helperFunctions")
for (const line of code) { for (const line of code) {
@ -586,10 +601,10 @@ class LayerOverviewUtils extends Script {
if (!isStrict) { if (!isStrict) {
allCode.push( allCode.push(
" Utils.AddLazyProperty(feat.properties, '" + " Utils.AddLazyProperty(feat.properties, '" +
attributeName + attributeName +
"', () => " + "', () => " +
expression + expression +
" ) ", " ) "
) )
} else { } else {
attributeName = attributeName.substring(0, attributeName.length - 1).trim() attributeName = attributeName.substring(0, attributeName.length - 1).trim()
@ -634,7 +649,7 @@ class LayerOverviewUtils extends Script {
const code = l.calculatedTags ?? [] const code = l.calculatedTags ?? []
allCode.push( allCode.push(
" public metaTaggging_for_" + l.id + "(feat: {properties: Record<string, string>}) {", " public metaTaggging_for_" + l.id + "(feat: {properties: Record<string, string>}) {"
) )
for (const line of code) { for (const line of code) {
const firstEq = line.indexOf("=") const firstEq = line.indexOf("=")
@ -644,10 +659,10 @@ class LayerOverviewUtils extends Script {
if (!isStrict) { if (!isStrict) {
allCode.push( allCode.push(
" Utils.AddLazyProperty(feat.properties, '" + " Utils.AddLazyProperty(feat.properties, '" +
attributeName + attributeName +
"', () => " + "', () => " +
expression + expression +
" ) ", " ) "
) )
} else { } else {
attributeName = attributeName.substring(0, attributeName.length - 2).trim() attributeName = attributeName.substring(0, attributeName.length - 2).trim()
@ -679,13 +694,13 @@ class LayerOverviewUtils extends Script {
const fixed = new Map<string, LayoutConfigJson>() const fixed = new Map<string, LayoutConfigJson>()
const publicLayers = LayerOverviewUtils.publicLayerIdsFrom( const publicLayers = LayerOverviewUtils.publicLayerIdsFrom(
themeFiles.map((th) => th.parsed), themeFiles.map((th) => th.parsed)
) )
const convertState: DesugaringContext = { const convertState: DesugaringContext = {
sharedLayers, sharedLayers,
tagRenderings: this.getSharedTagRenderings( tagRenderings: this.getSharedTagRenderings(
new DoesImageExist(licensePaths, existsSync), new DoesImageExist(licensePaths, existsSync)
), ),
publicLayers, publicLayers,
} }
@ -710,7 +725,7 @@ class LayerOverviewUtils extends Script {
const themeInfo = themeFiles[i] const themeInfo = themeFiles[i]
const themePath = themeInfo.path const themePath = themeInfo.path
let themeFile = themeInfo.parsed let themeFile = themeInfo.parsed
if(whitelist.size > 0 && !whitelist.has(themeFile.id)){ if (whitelist.size > 0 && !whitelist.has(themeFile.id)) {
continue continue
} }
@ -718,15 +733,15 @@ class LayerOverviewUtils extends Script {
LayerOverviewUtils.themePath + "/" + themePath.substring(themePath.lastIndexOf("/")) LayerOverviewUtils.themePath + "/" + themePath.substring(themePath.lastIndexOf("/"))
const usedLayers = Array.from( const usedLayers = Array.from(
LayerOverviewUtils.extractLayerIdsFrom(themeFile, false), LayerOverviewUtils.extractLayerIdsFrom(themeFile, false)
).map((id) => LayerOverviewUtils.layerPath + id + ".json") ).map((id) => LayerOverviewUtils.layerPath + id + ".json")
if (!forceReload && !this.shouldBeUpdated([themePath, ...usedLayers], targetPath)) { if (!forceReload && !this.shouldBeUpdated([themePath, ...usedLayers], targetPath)) {
fixed.set( fixed.set(
themeFile.id, themeFile.id,
JSON.parse( JSON.parse(
readFileSync(LayerOverviewUtils.themePath + themeFile.id + ".json", "utf8"), readFileSync(LayerOverviewUtils.themePath + themeFile.id + ".json", "utf8")
), )
) )
ScriptUtils.erasableLog("Skipping", themeFile.id) ScriptUtils.erasableLog("Skipping", themeFile.id)
skippedThemes.push(themeFile.id) skippedThemes.push(themeFile.id)
@ -737,23 +752,23 @@ class LayerOverviewUtils extends Script {
new PrevalidateTheme().convertStrict( new PrevalidateTheme().convertStrict(
themeFile, themeFile,
ConversionContext.construct([themePath], ["PrepareLayer"]), ConversionContext.construct([themePath], ["PrepareLayer"])
) )
try { try {
themeFile = new PrepareTheme(convertState, { themeFile = new PrepareTheme(convertState, {
skipDefaultLayers: true, skipDefaultLayers: true,
}).convertStrict( }).convertStrict(
themeFile, themeFile,
ConversionContext.construct([themePath], ["PrepareLayer"]), ConversionContext.construct([themePath], ["PrepareLayer"])
) )
new ValidateThemeAndLayers( new ValidateThemeAndLayers(
new DoesImageExist(licensePaths, existsSync, knownTagRenderings), new DoesImageExist(licensePaths, existsSync, knownTagRenderings),
themePath, themePath,
true, true,
knownTagRenderings, knownTagRenderings
).convertStrict( ).convertStrict(
themeFile, themeFile,
ConversionContext.construct([themePath], ["PrepareLayer"]), ConversionContext.construct([themePath], ["PrepareLayer"])
) )
if (themeFile.icon.endsWith(".svg")) { if (themeFile.icon.endsWith(".svg")) {
@ -795,29 +810,29 @@ class LayerOverviewUtils extends Script {
} }
} }
if(whitelist.size == 0){ if (whitelist.size == 0) {
this.writeSmallOverview( this.writeSmallOverview(
Array.from(fixed.values()).map((t) => { Array.from(fixed.values()).map((t) => {
return { return {
...t, ...t,
hideFromOverview: t.hideFromOverview ?? false, hideFromOverview: t.hideFromOverview ?? false,
shortDescription: shortDescription:
t.shortDescription ?? t.shortDescription ??
new Translation(t.description) new Translation(t.description)
.FirstSentence() .FirstSentence()
.OnEveryLanguage((s) => parse_html(s).textContent).translations, .OnEveryLanguage((s) => parse_html(s).textContent).translations,
mustHaveLanguage: t.mustHaveLanguage?.length > 0, mustHaveLanguage: t.mustHaveLanguage?.length > 0,
} }
}), })
) )
} }
console.log( console.log(
"Recompiled themes " + "Recompiled themes " +
recompiledThemes.join(", ") + recompiledThemes.join(", ") +
" and skipped " + " and skipped " +
skippedThemes.length + skippedThemes.length +
" themes", " themes"
) )
return fixed return fixed