Refactoring: use theme instead of layout in a lot of places

This commit is contained in:
Pieter Vander Vennet 2024-10-17 04:06:03 +02:00
parent 9427083939
commit bdc9ba52a6
104 changed files with 445 additions and 449 deletions

View file

@ -33,7 +33,7 @@ You can create your own theme at https://mapcomplete.org/studio
What is a good theme? What is a good theme?
--------------------- ---------------------
A **theme** (or _layout_) is a single map showing one or more layers. A **theme** is a single map showing one or more layers.
The layers should work together in such a way that they serve a certain **audience**. The layers should work together in such a way that they serve a certain **audience**.
You should be able to state in a few sentences whom would be the user of such a map, e.g. You should be able to state in a few sentences whom would be the user of such a map, e.g.
@ -294,12 +294,12 @@ There are three important levels in the JSON file:
Every field is documented in the source code itself - you can find them here: Every field is documented in the source code itself - you can find them here:
- [The top level `LayoutConfig`](/src/Models/ThemeConfig/Json/LayoutConfigJson.ts) - [The top level `ThemeConfig`](/src/Models/ThemeConfig/Json/ThemeConfigJson.ts)
- [A layer object `LayerConfig`](/src/Models/ThemeConfig/Json/LayerConfigJson.ts) - [A layer object `LayerConfig`](/src/Models/ThemeConfig/Json/LayerConfigJson.ts)
- [The `TagRendering`](/src/Models/ThemeConfig/Json/TagRenderingConfigJson.ts) - [The `TagRendering`](/src/Models/ThemeConfig/Json/TagRenderingConfigJson.ts)
- At last, the exact semantics of tags are documented [here](Tags_format.md) - At last, the exact semantics of tags are documented [here](Tags_format.md)
A JSON schema file is available in `Docs/Schemas` - use `LayoutConfig.schema.json` to validate a theme file. A JSON schema file is available in `Docs/Schemas` - use `ThemeConfig.schema.json` to validate a theme file.
### MetaTags ### MetaTags

View file

@ -61,7 +61,7 @@
"minzoom": 18, "minzoom": 18,
"isCounted": false, "isCounted": false,
"calculatedTags": [ "calculatedTags": [
"_surface:strict:=feat(get)('_surface')" "_surface:strict:=get(feat)('_surface')"
], ],
"tagRenderings": [ "tagRenderings": [
{ {
@ -224,19 +224,19 @@
"minzoom": 18, "minzoom": 18,
"calculatedTags": [ "calculatedTags": [
"_overlaps_with_buildings=overlapWith(feat)('osm_buildings').filter(f => f.feat.properties.id.indexOf('-') < 0)", "_overlaps_with_buildings=overlapWith(feat)('osm_buildings').filter(f => f.feat.properties.id.indexOf('-') < 0)",
"_overlaps_with=feat(get)('_overlaps_with_buildings').find(f => f.overlap > 1 /* square meter */ )", "_overlaps_with=get(feat)('_overlaps_with_buildings').find(f => f.overlap > 1 /* square meter */ )",
"_overlaps_with_properties=feat(get)('_overlaps_with')?.feat?.properties", "_overlaps_with_properties=get(feat)('_overlaps_with')?.feat?.properties",
"_overlap_percentage=Math.round(100 * (feat(get)('_overlaps_with')?.overlap / feat(get)('_overlaps_with_properties')['_surface:strict']))", "_overlap_percentage=Math.round(100 * (get(feat)('_overlaps_with')?.overlap / get(feat)('_overlaps_with_properties')['_surface:strict']))",
"_reverse_overlap_percentage=Math.round(100 * (feat(get)('_overlaps_with')?.overlap / feat(get)('_surface')))", "_reverse_overlap_percentage=Math.round(100 * (get(feat)('_overlaps_with')?.overlap / get(feat)('_surface')))",
"_bag_obj:in_construction=feat.properties.status.startsWith('Bouwvergunning verleend') || feat.properties.status.startsWith('Bouw gestart')", "_bag_obj:in_construction=feat.properties.status.startsWith('Bouwvergunning verleend') || feat.properties.status.startsWith('Bouw gestart')",
"_bag_obj:construction=(feat.properties.gebruiksdoel == 'woonfunctie') ? ((Number(feat.properties.aantal_verblijfsobjecten) == 1) ? 'house' : 'apartments') : 'yes'", "_bag_obj:construction=(feat.properties.gebruiksdoel == 'woonfunctie') ? ((Number(feat.properties.aantal_verblijfsobjecten) == 1) ? 'house' : 'apartments') : 'yes'",
"_bag_obj:building=(feat.properties.status.startsWith('Bouwvergunning verleend') || feat.properties.status.startsWith('Bouw gestart')) ? 'construction' : feat.properties['_bag_obj:construction']", "_bag_obj:building=(feat.properties.status.startsWith('Bouwvergunning verleend') || feat.properties.status.startsWith('Bouw gestart')) ? 'construction' : feat.properties['_bag_obj:construction']",
"_bag_obj:ref:bag=Number(feat.properties.identificatie)", "_bag_obj:ref:bag=Number(feat.properties.identificatie)",
"_bag_obj:source:date=new Date().toISOString().split('T')[0]", "_bag_obj:source:date=new Date().toISOString().split('T')[0]",
"_bag_obj:start_date=feat.properties.bouwjaar", "_bag_obj:start_date=feat.properties.bouwjaar",
"_osm_obj:id=feat(get)('_overlaps_with_properties')?.id", "_osm_obj:id=get(feat)('_overlaps_with_properties')?.id",
"_osm_obj:building=feat(get)('_overlaps_with_properties')?.building", "_osm_obj:building=get(feat)('_overlaps_with_properties')?.building",
"_imported_osm_object_found:=Number(feat.properties.identificatie)==Number(feat(get)('_overlaps_with_properties')['ref:bag'])" "_imported_osm_object_found:=Number(feat.properties.identificatie)==Number(get(feat)('_overlaps_with_properties')['ref:bag'])"
], ],
"tagRenderings": [ "tagRenderings": [
{ {
@ -435,11 +435,11 @@
"_bag_obj:addr:housenumber=`${feat.properties.huisnummer}${feat.properties.huisletter}${(feat.properties.toevoeging != '') ? '-' : ''}${feat.properties.toevoeging}`", "_bag_obj:addr:housenumber=`${feat.properties.huisnummer}${feat.properties.huisletter}${(feat.properties.toevoeging != '') ? '-' : ''}${feat.properties.toevoeging}`",
"_bag_obj:ref:bag=Number(feat.properties.identificatie)", "_bag_obj:ref:bag=Number(feat.properties.identificatie)",
"_bag_obj:source:date=new Date().toISOString().split('T')[0]", "_bag_obj:source:date=new Date().toISOString().split('T')[0]",
"_osm_obj:addr:city:=feat(get)('_closed_osm_addr')['addr:city']", "_osm_obj:addr:city:=get(feat)('_closed_osm_addr')['addr:city']",
"_osm_obj:addr:housenumber:=feat(get)('_closed_osm_addr')['addr:housenumber']", "_osm_obj:addr:housenumber:=get(feat)('_closed_osm_addr')['addr:housenumber']",
"_osm_obj:addr:postcode:=feat(get)('_closed_osm_addr')['addr:postcode']", "_osm_obj:addr:postcode:=get(feat)('_closed_osm_addr')['addr:postcode']",
"_osm_obj:addr:street:=feat(get)('_closed_osm_addr')['addr:street']", "_osm_obj:addr:street:=get(feat)('_closed_osm_addr')['addr:street']",
"_imported_osm_object_found:=(feat.properties.woonplaats==feat(get)('_closed_osm_addr')['addr:city'])&&(feat(get)('_bag_obj:addr:housenumber')==feat(get)('_closed_osm_addr')['addr:housenumber'])&&(feat.properties.postcode==feat(get)('_closed_osm_addr')['addr:postcode'])&&(feat.properties.openbare_ruimte==feat(get)('_closed_osm_addr')['addr:street'])" "_imported_osm_object_found:=(feat.properties.woonplaats==get(feat)('_closed_osm_addr')['addr:city'])&&(get(feat)('_bag_obj:addr:housenumber')==get(feat)('_closed_osm_addr')['addr:housenumber'])&&(feat.properties.postcode==get(feat)('_closed_osm_addr')['addr:postcode'])&&(feat.properties.openbare_ruimte==get(feat)('_closed_osm_addr')['addr:street'])"
], ],
"tagRenderings": [ "tagRenderings": [
{ {

View file

@ -2,7 +2,7 @@ import * as fs from "fs"
import { existsSync, lstatSync, readdirSync, readFileSync } from "fs" import { existsSync, lstatSync, readdirSync, readFileSync } from "fs"
import { Utils } from "../src/Utils" import { Utils } from "../src/Utils"
import { https } from "follow-redirects" import { https } from "follow-redirects"
import { LayoutConfigJson } from "../src/Models/ThemeConfig/Json/LayoutConfigJson" import { ThemeConfigJson } from "../src/Models/ThemeConfig/Json/ThemeConfigJson"
import { LayerConfigJson } from "../src/Models/ThemeConfig/Json/LayerConfigJson" import { LayerConfigJson } from "../src/Models/ThemeConfig/Json/LayerConfigJson"
import xml2js from "xml2js" import xml2js from "xml2js"
@ -111,7 +111,7 @@ export default class ScriptUtils {
} }
public static getThemeFiles(useTranslationPaths = false): { public static getThemeFiles(useTranslationPaths = false): {
parsed: LayoutConfigJson; parsed: ThemeConfigJson;
path: string; path: string;
raw: string raw: string
}[] { }[] {

View file

@ -8,7 +8,7 @@ import QueryParameterDocumentation from "../src/UI/QueryParameterDocumentation"
import ScriptUtils from "./ScriptUtils" import ScriptUtils from "./ScriptUtils"
import Translations from "../src/UI/i18n/Translations" import Translations from "../src/UI/i18n/Translations"
import themeOverview from "../src/assets/generated/theme_overview.json" import themeOverview from "../src/assets/generated/theme_overview.json"
import LayoutConfig from "../src/Models/ThemeConfig/LayoutConfig" import ThemeConfig from "../src/Models/ThemeConfig/ThemeConfig"
import bookcases from "../src/assets/generated/themes/bookcases.json" import bookcases from "../src/assets/generated/themes/bookcases.json"
import fakedom from "fake-dom" import fakedom from "fake-dom"
import unit from "../src/assets/generated/layers/unit.json" import unit from "../src/assets/generated/layers/unit.json"
@ -253,7 +253,7 @@ export class GenerateDocs extends Script {
} }
private generateHotkeyDocs() { private generateHotkeyDocs() {
new ThemeViewState(new LayoutConfig(<any>bookcases), new Set()) new ThemeViewState(new ThemeConfig(<any>bookcases), new Set())
this.WriteMarkdownFile("./Docs/Hotkeys.md", Hotkeys.generateDocumentation(), [ this.WriteMarkdownFile("./Docs/Hotkeys.md", Hotkeys.generateDocumentation(), [
"src/UI/Base/Hotkeys.ts", "src/UI/Base/Hotkeys.ts",
]) ])
@ -455,7 +455,7 @@ export class GenerateDocs extends Script {
) )
} }
private generateForTheme(theme: LayoutConfig): void { private generateForTheme(theme: ThemeConfig): void {
const allLayers = AllSharedLayers.getSharedLayersConfigs() const allLayers = AllSharedLayers.getSharedLayersConfigs()
const layersToShow = theme.layers.filter( const layersToShow = theme.layers.filter(
(l) => (l) =>

View file

@ -1,7 +1,7 @@
import ScriptUtils from "./ScriptUtils" import ScriptUtils from "./ScriptUtils"
import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "fs" import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "fs"
import licenses from "../src/assets/generated/license_info.json" import licenses from "../src/assets/generated/license_info.json"
import { LayoutConfigJson } from "../src/Models/ThemeConfig/Json/LayoutConfigJson" import { ThemeConfigJson } from "../src/Models/ThemeConfig/Json/ThemeConfigJson"
import { LayerConfigJson } from "../src/Models/ThemeConfig/Json/LayerConfigJson" import { LayerConfigJson } from "../src/Models/ThemeConfig/Json/LayerConfigJson"
import Constants from "../src/Models/Constants" import Constants from "../src/Models/Constants"
import { import {
@ -29,7 +29,7 @@ import LayerConfig from "../src/Models/ThemeConfig/LayerConfig"
import PointRenderingConfig from "../src/Models/ThemeConfig/PointRenderingConfig" import PointRenderingConfig from "../src/Models/ThemeConfig/PointRenderingConfig"
import { ConversionContext } from "../src/Models/ThemeConfig/Conversion/ConversionContext" import { ConversionContext } from "../src/Models/ThemeConfig/Conversion/ConversionContext"
import { GenerateFavouritesLayer } from "./generateFavouritesLayer" import { GenerateFavouritesLayer } from "./generateFavouritesLayer"
import LayoutConfig, { MinimalLayoutInformation } from "../src/Models/ThemeConfig/LayoutConfig" import ThemeConfig, { MinimalThemeInformation } from "../src/Models/ThemeConfig/ThemeConfig"
import Translations from "../src/UI/i18n/Translations" import Translations from "../src/UI/i18n/Translations"
import { Translatable } from "../src/Models/ThemeConfig/Json/Translatable" import { Translatable } from "../src/Models/ThemeConfig/Json/Translatable"
import { ValidateThemeAndLayers } from "../src/Models/ThemeConfig/Conversion/ValidateThemeAndLayers" import { ValidateThemeAndLayers } from "../src/Models/ThemeConfig/Conversion/ValidateThemeAndLayers"
@ -148,14 +148,14 @@ class LayerOverviewUtils extends Script {
super("Reviews and generates the compiled themes") super("Reviews and generates the compiled themes")
} }
private static publicLayerIdsFrom(themefiles: LayoutConfigJson[]): Set<string> { private static publicLayerIdsFrom(themefiles: ThemeConfigJson[]): Set<string> {
const publicThemes = [].concat(...themefiles.filter((th) => !th.hideFromOverview)) const publicThemes = [].concat(...themefiles.filter((th) => !th.hideFromOverview))
return new Set([].concat(...publicThemes.map((th) => this.extractLayerIdsFrom(th)))) return new Set([].concat(...publicThemes.map((th) => this.extractLayerIdsFrom(th))))
} }
private static extractLayerIdsFrom( private static extractLayerIdsFrom(
themeFile: LayoutConfigJson, themeFile: ThemeConfigJson,
includeInlineLayers = true, includeInlineLayers = true,
): string[] { ): string[] {
const publicLayerIds: string[] = [] const publicLayerIds: string[] = []
@ -295,7 +295,7 @@ class LayerOverviewUtils extends Script {
layerKeywords[id] = this.layerKeywords(layer) layerKeywords[id] = this.layerKeywords(layer)
}) })
const perId = new Map<string, MinimalLayoutInformation>() const perId = new Map<string, MinimalThemeInformation>()
for (const theme of themes) { for (const theme of themes) {
const keywords: Record<string, string[]> = {} const keywords: Record<string, string[]> = {}
@ -308,7 +308,7 @@ class LayerOverviewUtils extends Script {
} }
const data = <MinimalLayoutInformation> { const data = <MinimalThemeInformation> {
id: theme.id, id: theme.id,
title: theme.title, title: theme.title,
shortDescription: LayerOverviewUtils.cleanTranslation(theme.shortDescription), shortDescription: LayerOverviewUtils.cleanTranslation(theme.shortDescription),
@ -342,7 +342,7 @@ class LayerOverviewUtils extends Script {
) )
} }
writeTheme(theme: LayoutConfigJson) { writeTheme(theme: ThemeConfigJson) {
if (!existsSync(LayerOverviewUtils.themePath)) { if (!existsSync(LayerOverviewUtils.themePath)) {
mkdirSync(LayerOverviewUtils.themePath) mkdirSync(LayerOverviewUtils.themePath)
} }
@ -519,7 +519,7 @@ class LayerOverviewUtils extends Script {
) )
new ValidateThemeEnsemble().convertStrict( new ValidateThemeEnsemble().convertStrict(
Array.from(sharedThemes.values()).map((th) => new LayoutConfig(th, true)), Array.from(sharedThemes.values()).map((th) => new ThemeConfig(th, true)),
) )
if (recompiledThemes.length > 0) { if (recompiledThemes.length > 0) {
@ -545,7 +545,7 @@ class LayerOverviewUtils extends Script {
if: "theme=" + th.id, if: "theme=" + th.id,
then: th.icon, then: th.icon,
})) }))
const proto: LayoutConfigJson = JSON.parse( const proto: ThemeConfigJson = JSON.parse(
readFileSync("./assets/themes/mapcomplete-changes/mapcomplete-changes.proto.json", { readFileSync("./assets/themes/mapcomplete-changes/mapcomplete-changes.proto.json", {
encoding: "utf8", encoding: "utf8",
}), }),
@ -689,7 +689,7 @@ class LayerOverviewUtils extends Script {
* @param themeFile * @param themeFile
* @private * @private
*/ */
private extractJavascriptCode(themeFile: LayoutConfigJson) { private extractJavascriptCode(themeFile: ThemeConfigJson) {
const allCode = [ const allCode = [
"import {Feature} from 'geojson'", "import {Feature} from 'geojson'",
"import { ExtraFuncType } from \"../../../Logic/ExtraFunctions\";", "import { ExtraFuncType } from \"../../../Logic/ExtraFunctions\";",
@ -804,10 +804,10 @@ class LayerOverviewUtils extends Script {
recompiledThemes: string[], recompiledThemes: string[],
forceReload: boolean, forceReload: boolean,
whitelist: Set<string>, whitelist: Set<string>,
): Map<string, LayoutConfigJson> { ): Map<string, ThemeConfigJson> {
console.log(" ---------- VALIDATING BUILTIN THEMES ---------") console.log(" ---------- VALIDATING BUILTIN THEMES ---------")
const themeFiles = ScriptUtils.getThemeFiles() const themeFiles = ScriptUtils.getThemeFiles()
const fixed = new Map<string, LayoutConfigJson>() const fixed = new Map<string, ThemeConfigJson>()
const publicLayers = LayerOverviewUtils.publicLayerIdsFrom( const publicLayers = LayerOverviewUtils.publicLayerIdsFrom(
themeFiles.map((th) => th.parsed), themeFiles.map((th) => th.parsed),

View file

@ -3,8 +3,8 @@ import Locale from "../src/UI/i18n/Locale"
import Translations from "../src/UI/i18n/Translations" import Translations from "../src/UI/i18n/Translations"
import { Translation } from "../src/UI/i18n/Translation" import { Translation } from "../src/UI/i18n/Translation"
import all_known_layouts from "../src/assets/generated/known_themes.json" import all_known_layouts from "../src/assets/generated/known_themes.json"
import { LayoutConfigJson } from "../src/Models/ThemeConfig/Json/LayoutConfigJson" import { ThemeConfigJson } from "../src/Models/ThemeConfig/Json/ThemeConfigJson"
import LayoutConfig from "../src/Models/ThemeConfig/LayoutConfig" import ThemeConfig from "../src/Models/ThemeConfig/ThemeConfig"
import xml2js from "xml2js" import xml2js from "xml2js"
import ScriptUtils from "./ScriptUtils" import ScriptUtils from "./ScriptUtils"
import { Utils } from "../src/Utils" import { Utils } from "../src/Utils"
@ -100,7 +100,7 @@ class GenerateLayouts extends Script {
return newname return newname
} }
async createSocialImage(layout: LayoutConfig, template: "" | "Wide"): Promise<string> { async createSocialImage(layout: ThemeConfig, template: "" | "Wide"): Promise<string> {
if (!layout.icon.endsWith(".svg")) { if (!layout.icon.endsWith(".svg")) {
console.warn( console.warn(
"Not creating a social image for " + "Not creating a social image for " +
@ -160,7 +160,7 @@ class GenerateLayouts extends Script {
} }
async createManifest( async createManifest(
layout: LayoutConfig, layout: ThemeConfig,
alreadyWritten: string[] alreadyWritten: string[]
): Promise<{ ): Promise<{
manifest: any manifest: any
@ -319,8 +319,8 @@ class GenerateLayouts extends Script {
} }
async generateCsp( async generateCsp(
layout: LayoutConfig, layout: ThemeConfig,
layoutJson: LayoutConfigJson, layoutJson: ThemeConfigJson,
options: { options: {
scriptSrcs: string[] scriptSrcs: string[]
} }
@ -441,8 +441,8 @@ class GenerateLayouts extends Script {
} }
async createLandingPage( async createLandingPage(
layout: LayoutConfig, layout: ThemeConfig,
layoutJson: LayoutConfigJson, layoutJson: ThemeConfigJson,
whiteIcons, whiteIcons,
alreadyWritten alreadyWritten
) { ) {
@ -456,7 +456,7 @@ class GenerateLayouts extends Script {
.replace(/"/g, '\\"') .replace(/"/g, '\\"')
let ogImage = layout.socialImage let ogImage = layout.socialImage
let twitterImage = ogImage let twitterImage = ogImage
if (ogImage === LayoutConfig.defaultSocialImage && layout.official) { if (ogImage === ThemeConfig.defaultSocialImage && layout.official) {
try { try {
ogImage = (await this.createSocialImage(layout, "")) ?? layout.socialImage ogImage = (await this.createSocialImage(layout, "")) ?? layout.socialImage
twitterImage = (await this.createSocialImage(layout, "Wide")) ?? layout.socialImage twitterImage = (await this.createSocialImage(layout, "Wide")) ?? layout.socialImage
@ -573,7 +573,7 @@ class GenerateLayouts extends Script {
) )
} }
async createIndexFor(theme: LayoutConfig) { async createIndexFor(theme: ThemeConfig) {
const filename = "index_" + theme.id + ".ts" const filename = "index_" + theme.id + ".ts"
const imports = [ const imports = [
@ -628,18 +628,18 @@ class GenerateLayouts extends Script {
"theme", "theme",
] ]
// @ts-ignore // @ts-ignore
const all: LayoutConfigJson[] = all_known_layouts.themes const all: ThemeConfigJson[] = all_known_layouts.themes
const args = process.argv const args = process.argv
const theme = args[2] const theme = args[2]
if (theme !== undefined) { if (theme !== undefined) {
console.warn("Only generating layout " + theme) console.warn("Only generating layout " + theme)
} }
for (const i in all) { for (const i in all) {
const layoutConfigJson: LayoutConfigJson = all[i] const layoutConfigJson: ThemeConfigJson = all[i]
if (theme !== undefined && layoutConfigJson.id !== theme) { if (theme !== undefined && layoutConfigJson.id !== theme) {
continue continue
} }
const layout = new LayoutConfig(layoutConfigJson, true) const layout = new ThemeConfig(layoutConfigJson, true)
const layoutName = layout.id const layoutName = layout.id
if (blacklist.indexOf(layoutName.toLowerCase()) >= 0) { if (blacklist.indexOf(layoutName.toLowerCase()) >= 0) {
console.log(`Skipping a layout with name ${layoutName}, it is on the blacklist`) console.log(`Skipping a layout with name ${layoutName}, it is on the blacklist`)
@ -668,7 +668,7 @@ class GenerateLayouts extends Script {
} }
const { manifest } = await this.createManifest( const { manifest } = await this.createManifest(
new LayoutConfig({ new ThemeConfig({
icon: "./assets/svg/mapcomplete_logo.svg", icon: "./assets/svg/mapcomplete_logo.svg",
id: "index", id: "index",
layers: [], layers: [],

View file

@ -2,7 +2,7 @@ import { AllKnownLayouts } from "../src/Customizations/AllKnownLayouts"
import Locale from "../src/UI/i18n/Locale" import Locale from "../src/UI/i18n/Locale"
import { Translation } from "../src/UI/i18n/Translation" import { Translation } from "../src/UI/i18n/Translation"
import { readFileSync, writeFileSync } from "fs" import { readFileSync, writeFileSync } from "fs"
import LayoutConfig from "../src/Models/ThemeConfig/LayoutConfig" import ThemeConfig from "../src/Models/ThemeConfig/ThemeConfig"
import LayerConfig from "../src/Models/ThemeConfig/LayerConfig" import LayerConfig from "../src/Models/ThemeConfig/LayerConfig"
import { Utils } from "../src/Utils" import { Utils } from "../src/Utils"
@ -33,7 +33,7 @@ function generateTagOverview(
return overview return overview
} }
function generateLayerUsage(layer: LayerConfig, layout: LayoutConfig): any[] { function generateLayerUsage(layer: LayerConfig, layout: ThemeConfig): any[] {
if (layer.name === undefined) { if (layer.name === undefined) {
return [] // Probably a duplicate or irrelevant layer return [] // Probably a duplicate or irrelevant layer
} }
@ -124,7 +124,7 @@ function generateLayerUsage(layer: LayerConfig, layout: LayoutConfig): any[] {
* Generates the JSON-object representing the theme for inclusion on taginfo * Generates the JSON-object representing the theme for inclusion on taginfo
* @param layout * @param layout
*/ */
function generateTagInfoEntry(layout: LayoutConfig): any { function generateTagInfoEntry(layout: ThemeConfig): any {
const usedTags = [] const usedTags = []
for (const layer of layout.layers) { for (const layer of layout.layers) {
if (layer.source === null) { if (layer.source === null) {

View file

@ -15,7 +15,7 @@ type ErrorMessage = {
message: { message: {
stacktrace: string stacktrace: string
message: string message: string
layout: string theme: string
version: string version: string
language: string language: string
username: string username: string
@ -40,7 +40,7 @@ class HandleErrors extends Script {
) { ) {
console.log( console.log(
parsed.message.username, parsed.message.username,
parsed.message.layout, parsed.message.theme,
parsed.message.message, parsed.message.message,
parsed.date parsed.date
) )
@ -82,7 +82,7 @@ class HandleErrors extends Script {
} = changesObj.CreateChangesetObjects(toUpload, objects, true) } = changesObj.CreateChangesetObjects(toUpload, objects, true)
const changeset = Changes.buildChangesetXML("", changes) const changeset = Changes.buildChangesetXML("", changes)
const path = "error_changeset_" + parsed.index + "_" + e.layout + "_" + e.username + ".osc" const path = "error_changeset_" + parsed.index + "_" + e.theme + "_" + e.username + ".osc"
if ( if (
changeset === changeset ===
`<osmChange version='0.6' generator='Mapcomplete ${Constants.vNumber}'></osmChange>` `<osmChange version='0.6' generator='Mapcomplete ${Constants.vNumber}'></osmChange>`

View file

@ -1,7 +1,7 @@
import known_themes from "../assets/generated/known_themes.json" import known_themes from "../assets/generated/known_themes.json"
import LayoutConfig from "../Models/ThemeConfig/LayoutConfig" import ThemeConfig from "../Models/ThemeConfig/ThemeConfig"
import favourite from "../assets/generated/layers/favourite.json" import favourite from "../assets/generated/layers/favourite.json"
import { LayoutConfigJson } from "../Models/ThemeConfig/Json/LayoutConfigJson" import { ThemeConfigJson } from "../Models/ThemeConfig/Json/ThemeConfigJson"
import { AllSharedLayers } from "./AllSharedLayers" import { AllSharedLayers } from "./AllSharedLayers"
import Constants from "../Models/Constants" import Constants from "../Models/Constants"
@ -9,8 +9,8 @@ import Constants from "../Models/Constants"
* Somewhat of a dictionary, which lazily parses needed themes * Somewhat of a dictionary, which lazily parses needed themes
*/ */
export class AllKnownLayoutsLazy { export class AllKnownLayoutsLazy {
private readonly raw: Map<string, LayoutConfigJson> = new Map() private readonly raw: Map<string, ThemeConfigJson> = new Map()
private readonly dict: Map<string, LayoutConfig> = new Map() private readonly dict: Map<string, ThemeConfig> = new Map()
constructor(includeFavouriteLayer = true) { constructor(includeFavouriteLayer = true) {
for (const layoutConfigJson of known_themes["themes"]) { for (const layoutConfigJson of known_themes["themes"]) {
@ -32,7 +32,7 @@ export class AllKnownLayoutsLazy {
} }
} }
public getConfig(key: string): LayoutConfigJson { public getConfig(key: string): ThemeConfigJson {
return this.raw.get(key) return this.raw.get(key)
} }
@ -40,13 +40,13 @@ export class AllKnownLayoutsLazy {
return this.raw.size return this.raw.size
} }
public get(key: string): LayoutConfig { public get(key: string): ThemeConfig {
const cached = this.dict.get(key) const cached = this.dict.get(key)
if (cached !== undefined) { if (cached !== undefined) {
return cached return cached
} }
const layout = new LayoutConfig(this.getConfig(key)) const layout = new ThemeConfig(this.getConfig(key))
this.dict.set(key, layout) this.dict.set(key, layout)
return layout return layout
} }

View file

@ -1,5 +1,5 @@
import { ImmutableStore, Store, UIEventSource } from "../UIEventSource" import { ImmutableStore, Store, UIEventSource } from "../UIEventSource"
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig" import ThemeConfig from "../../Models/ThemeConfig/ThemeConfig"
import { LocalStorageSource } from "../Web/LocalStorageSource" import { LocalStorageSource } from "../Web/LocalStorageSource"
import { QueryParameters } from "../Web/QueryParameters" import { QueryParameters } from "../Web/QueryParameters"
import Hash from "../Web/Hash" import Hash from "../Web/Hash"
@ -25,7 +25,7 @@ export default class InitialMapPositioning {
public location: UIEventSource<{ lon: number; lat: number }> public location: UIEventSource<{ lon: number; lat: number }>
public useTerrain: Store<boolean> public useTerrain: Store<boolean>
constructor(layoutToUse: LayoutConfig, geolocationState: GeoLocationState) { constructor(layoutToUse: ThemeConfig, geolocationState: GeoLocationState) {
function localStorageSynced( function localStorageSynced(
key: string, key: string,
deflt: number, deflt: number,

View file

@ -14,7 +14,7 @@ export default class NoElementsInViewDetector {
constructor(themeViewState: ThemeViewState) { constructor(themeViewState: ThemeViewState) {
const state = themeViewState const state = themeViewState
const minZoom = Math.min( const minZoom = Math.min(
...themeViewState.layout.layers ...themeViewState.theme.layers
.filter((l) => Constants.priviliged_layers.indexOf(<any>l.id) < 0) .filter((l) => Constants.priviliged_layers.indexOf(<any>l.id) < 0)
.map((l) => l.minzoom) .map((l) => l.minzoom)
) )
@ -43,7 +43,7 @@ export default class NoElementsInViewDetector {
// Nope, no data loaded // Nope, no data loaded
continue continue
} }
const layer = themeViewState.layout.getLayer(layerName) const layer = themeViewState.theme.getLayer(layerName)
if (mapProperties.zoom.data < layer.minzoom) { if (mapProperties.zoom.data < layer.minzoom) {
minzoomWithData = Math.min(layer.minzoom) minzoomWithData = Math.min(layer.minzoom)
continue continue
@ -67,7 +67,7 @@ export default class NoElementsInViewDetector {
continue continue
} }
const layer = themeViewState.layout.getLayer(layerName) const layer = themeViewState.theme.getLayer(layerName)
if (mapProperties.zoom.data < layer.minzoom) { if (mapProperties.zoom.data < layer.minzoom) {
continue continue
} }

View file

@ -34,7 +34,7 @@ export default class SelectedElementTagsUpdater {
public static applyUpdate(latestTags: OsmTags, id: string, state: SpecialVisualizationState) { public static applyUpdate(latestTags: OsmTags, id: string, state: SpecialVisualizationState) {
try { try {
const leftRightSensitive = state.layout.isLeftRightSensitive() const leftRightSensitive = state.theme.isLeftRightSensitive()
if (leftRightSensitive) { if (leftRightSensitive) {
SimpleMetaTagger.removeBothTagging(latestTags) SimpleMetaTagger.removeBothTagging(latestTags)
@ -111,7 +111,7 @@ export default class SelectedElementTagsUpdater {
} }
private invalidateCache(s: Feature) { private invalidateCache(s: Feature) {
const state = this.state const state = this.state
const wasPartOfLayer = state.layout.getMatchingLayer(s.properties) const wasPartOfLayer = state.theme.getMatchingLayer(s.properties)
state.toCacheSavers?.get(wasPartOfLayer.id)?.invalidateCacheAround(BBox.get(s)) state.toCacheSavers?.get(wasPartOfLayer.id)?.invalidateCacheAround(BBox.get(s))
} }
private installCallback() { private installCallback() {

View file

@ -12,11 +12,11 @@ export default class TitleHandler {
const currentTitle: Store<string> = selectedElement.map( const currentTitle: Store<string> = selectedElement.map(
(selected) => { (selected) => {
const lng = Locale.language.data const lng = Locale.language.data
const defaultTitle = state.layout?.title?.textFor(lng) ?? "MapComplete" const defaultTitle = state.theme?.title?.textFor(lng) ?? "MapComplete"
if (selected === undefined) { if (selected === undefined) {
return defaultTitle return defaultTitle
} }
const layer = state.layout.getMatchingLayer(selected.properties) const layer = state.theme.getMatchingLayer(selected.properties)
if (layer === undefined) { if (layer === undefined) {
return defaultTitle return defaultTitle
} }

View file

@ -1,4 +1,4 @@
import LayoutConfig from "../Models/ThemeConfig/LayoutConfig" import ThemeConfig from "../Models/ThemeConfig/ThemeConfig"
import { QueryParameters } from "./Web/QueryParameters" import { QueryParameters } from "./Web/QueryParameters"
import { AllKnownLayouts } from "../Customizations/AllKnownLayouts" import { AllKnownLayouts } from "../Customizations/AllKnownLayouts"
import { FixedUiElement } from "../UI/Base/FixedUiElement" import { FixedUiElement } from "../UI/Base/FixedUiElement"
@ -19,10 +19,10 @@ import { DesugaringContext } from "../Models/ThemeConfig/Conversion/Conversion"
import { TagRenderingConfigJson } from "../Models/ThemeConfig/Json/TagRenderingConfigJson" import { TagRenderingConfigJson } from "../Models/ThemeConfig/Json/TagRenderingConfigJson"
import Hash from "./Web/Hash" import Hash from "./Web/Hash"
import { QuestionableTagRenderingConfigJson } from "../Models/ThemeConfig/Json/QuestionableTagRenderingConfigJson" import { QuestionableTagRenderingConfigJson } from "../Models/ThemeConfig/Json/QuestionableTagRenderingConfigJson"
import { LayoutConfigJson } from "../Models/ThemeConfig/Json/LayoutConfigJson" import { ThemeConfigJson } from "../Models/ThemeConfig/Json/ThemeConfigJson"
import { ValidateThemeAndLayers } from "../Models/ThemeConfig/Conversion/ValidateThemeAndLayers" import { ValidateThemeAndLayers } from "../Models/ThemeConfig/Conversion/ValidateThemeAndLayers"
export default class DetermineLayout { export default class DetermineTheme {
private static readonly _knownImages = new Set(Array.from(licenses).map((l) => l.path)) private static readonly _knownImages = new Set(Array.from(licenses).map((l) => l.path))
private static readonly loadCustomThemeParam = QueryParameters.GetQueryParameter( private static readonly loadCustomThemeParam = QueryParameters.GetQueryParameter(
"userlayout", "userlayout",
@ -31,7 +31,7 @@ export default class DetermineLayout {
) )
public static getCustomDefinition(): string { public static getCustomDefinition(): string {
const layoutFromBase64 = decodeURIComponent(DetermineLayout.loadCustomThemeParam.data) const layoutFromBase64 = decodeURIComponent(DetermineTheme.loadCustomThemeParam.data)
if (layoutFromBase64.startsWith("http")) { if (layoutFromBase64.startsWith("http")) {
return layoutFromBase64 return layoutFromBase64
@ -53,8 +53,8 @@ export default class DetermineLayout {
} }
private static async expandRemoteLayers( private static async expandRemoteLayers(
layoutConfig: LayoutConfigJson layoutConfig: ThemeConfigJson
): Promise<LayoutConfigJson> { ): Promise<ThemeConfigJson> {
if (!layoutConfig.layers) { if (!layoutConfig.layers) {
// This is probably a layer in 'layer-only-mode' // This is probably a layer in 'layer-only-mode'
return layoutConfig return layoutConfig
@ -79,16 +79,16 @@ export default class DetermineLayout {
/** /**
* Gets the correct layout for this website * Gets the correct layout for this website
*/ */
public static async GetLayout(): Promise<LayoutConfig | undefined> { public static async getTheme(): Promise<ThemeConfig | undefined> {
const layoutFromBase64 = decodeURIComponent(DetermineLayout.loadCustomThemeParam.data) const layoutFromBase64 = decodeURIComponent(DetermineTheme.loadCustomThemeParam.data)
if (layoutFromBase64.startsWith("http")) { if (layoutFromBase64.startsWith("http")) {
return await DetermineLayout.LoadRemoteTheme(layoutFromBase64) return await DetermineTheme.LoadRemoteTheme(layoutFromBase64)
} }
if (layoutFromBase64 !== "false") { if (layoutFromBase64 !== "false") {
// We have to load something from the hash (or from disk) // We have to load something from the hash (or from disk)
return await DetermineLayout.LoadLayoutFromHash(DetermineLayout.loadCustomThemeParam) return await DetermineTheme.LoadLayoutFromHash(DetermineTheme.loadCustomThemeParam)
} }
let layoutId: string = undefined let layoutId: string = undefined
@ -127,7 +127,7 @@ export default class DetermineLayout {
public static async LoadLayoutFromHash( public static async LoadLayoutFromHash(
userLayoutParam: UIEventSource<string> userLayoutParam: UIEventSource<string>
): Promise<LayoutConfig | null> { ): Promise<ThemeConfig | null> {
let hash = location.hash.substr(1) let hash = location.hash.substr(1)
let json: any let json: any
@ -157,7 +157,7 @@ export default class DetermineLayout {
json = await this.expandRemoteLayers(json) json = await this.expandRemoteLayers(json)
const layoutToUse = DetermineLayout.prepCustomTheme(json) const layoutToUse = DetermineTheme.prepCustomTheme(json)
userLayoutParam.setData(layoutToUse.id) userLayoutParam.setData(layoutToUse.id)
return layoutToUse return layoutToUse
} }
@ -166,7 +166,7 @@ export default class DetermineLayout {
const dict = new Map<string, QuestionableTagRenderingConfigJson>() const dict = new Map<string, QuestionableTagRenderingConfigJson>()
for (const tagRendering of questions.tagRenderings) { for (const tagRendering of questions.tagRenderings) {
dict.set(tagRendering.id, tagRendering) dict.set(tagRendering.id, <QuestionableTagRenderingConfigJson> tagRendering)
} }
return dict return dict
@ -176,7 +176,7 @@ export default class DetermineLayout {
return questions.tagRenderings.map((tr) => tr.id) return questions.tagRenderings.map((tr) => tr.id)
} }
private static prepCustomTheme(json: any, sourceUrl?: string, forceId?: string): LayoutConfig { private static prepCustomTheme(json: any, sourceUrl?: string, forceId?: string): ThemeConfig {
if (json.layers === undefined && json.tagRenderings !== undefined) { if (json.layers === undefined && json.tagRenderings !== undefined) {
// We got fed a layer instead of a theme // We got fed a layer instead of a theme
const layerConfig = <LayerConfigJson>json const layerConfig = <LayerConfigJson>json
@ -224,15 +224,15 @@ export default class DetermineLayout {
knownLayersDict.set(layer.id, <LayerConfigJson>layer) knownLayersDict.set(layer.id, <LayerConfigJson>layer)
} }
const convertState: DesugaringContext = { const convertState: DesugaringContext = {
tagRenderings: DetermineLayout.getSharedTagRenderings(), tagRenderings: DetermineTheme.getSharedTagRenderings(),
tagRenderingOrder: DetermineLayout.getSharedTagRenderingOrder(), tagRenderingOrder: DetermineTheme.getSharedTagRenderingOrder(),
sharedLayers: knownLayersDict, sharedLayers: knownLayersDict,
publicLayers: new Set<string>(), publicLayers: new Set<string>(),
} }
json = new FixLegacyTheme().convertStrict(json) json = new FixLegacyTheme().convertStrict(json)
const raw = json const raw = json
json = new FixImages(DetermineLayout._knownImages).convertStrict(json) json = new FixImages(DetermineTheme._knownImages).convertStrict(json)
json.enableNoteImports = json.enableNoteImports ?? false json.enableNoteImports = json.enableNoteImports ?? false
json = new PrepareTheme(convertState).convertStrict(json) json = new PrepareTheme(convertState).convertStrict(json)
console.log("The layoutconfig is ", json) console.log("The layoutconfig is ", json)
@ -249,20 +249,20 @@ export default class DetermineLayout {
false false
).convertStrict(json) ).convertStrict(json)
} }
return new LayoutConfig(json, false, { return new ThemeConfig(json, false, {
definitionRaw: JSON.stringify(raw, null, " "), definitionRaw: JSON.stringify(raw, null, " "),
definedAtUrl: sourceUrl, definedAtUrl: sourceUrl,
}) })
} }
private static async LoadRemoteTheme(link: string): Promise<LayoutConfig | null> { private static async LoadRemoteTheme(link: string): Promise<ThemeConfig | null> {
console.log("Downloading map theme from ", link) console.log("Downloading map theme from ", link)
new FixedUiElement(`Downloading the theme from the <a href="${link}">link</a>...`).AttachTo( new FixedUiElement(`Downloading the theme from the <a href="${link}">link</a>...`).AttachTo(
"maindiv" "maindiv"
) )
let parsed = <LayoutConfigJson>await Utils.downloadJson(link) let parsed = <ThemeConfigJson>await Utils.downloadJson(link)
let forcedId = parsed.id let forcedId = parsed.id
const url = new URL(link) const url = new URL(link)
if (!(url.hostname === "localhost" || url.hostname === "127.0.0.1")) { if (!(url.hostname === "localhost" || url.hostname === "127.0.0.1")) {
@ -270,6 +270,6 @@ export default class DetermineLayout {
} }
console.log("Loaded remote link:", link) console.log("Loaded remote link:", link)
parsed = await this.expandRemoteLayers(parsed) parsed = await this.expandRemoteLayers(parsed)
return DetermineLayout.prepCustomTheme(parsed, link, forcedId) return DetermineTheme.prepCustomTheme(parsed, link, forcedId)
} }
} }

View file

@ -49,7 +49,7 @@ export default class FavouritesFeatureSource extends StaticFeatureSource {
const featuresWithoutAlreadyPresent = features.map((features) => const featuresWithoutAlreadyPresent = features.map((features) =>
features.filter( features.filter(
(feat) => !state.layout.layers.some((l) => l.id === feat.properties._orig_layer) (feat) => !state.theme.layers.some((l) => l.id === feat.properties._orig_layer)
) )
) )

View file

@ -1,4 +1,4 @@
import LayoutConfig from "../../../Models/ThemeConfig/LayoutConfig" import ThemeConfig from "../../../Models/ThemeConfig/ThemeConfig"
import { ImmutableStore, Store, UIEventSource } from "../../UIEventSource" import { ImmutableStore, Store, UIEventSource } from "../../UIEventSource"
import { Feature, Point } from "geojson" import { Feature, Point } from "geojson"
import { TagUtils } from "../../Tags/TagUtils" import { TagUtils } from "../../Tags/TagUtils"
@ -22,7 +22,7 @@ export class LastClickFeatureSource implements FeatureSource {
private _usermode: UIEventSource<string> private _usermode: UIEventSource<string>
private _enabledAddMorePoints: UIEventSource<boolean> private _enabledAddMorePoints: UIEventSource<boolean>
constructor( constructor(
layout: LayoutConfig, layout: ThemeConfig,
clickSource: Store<{ lon: number; lat: number; mode: "left" | "right" | "middle" }>, clickSource: Store<{ lon: number; lat: number; mode: "left" | "right" | "middle" }>,
usermode?: UIEventSource<string>, usermode?: UIEventSource<string>,
enabledAddMorePoints?: UIEventSource<boolean> enabledAddMorePoints?: UIEventSource<boolean>

View file

@ -18,7 +18,7 @@ import FeatureSourceMerger from "./FeatureSourceMerger"
* *
* Note that special layers (with `source=null` will be ignored) * Note that special layers (with `source=null` will be ignored)
*/ */
export default class LayoutSource extends FeatureSourceMerger { export default class ThemeSource extends FeatureSourceMerger {
/** /**
* Indicates if a data source is loading something * Indicates if a data source is loading something
*/ */
@ -51,7 +51,7 @@ export default class LayoutSource extends FeatureSourceMerger {
const src = new LocalStorageFeatureSource( const src = new LocalStorageFeatureSource(
backend, backend,
layer, layer,
LayoutSource.fromCacheZoomLevel, ThemeSource.fromCacheZoomLevel,
mapProperties, mapProperties,
{ {
isActive: isDisplayed(layer.id), isActive: isDisplayed(layer.id),
@ -63,13 +63,13 @@ export default class LayoutSource extends FeatureSourceMerger {
} }
const mvtSources: UpdatableFeatureSource[] = osmLayers const mvtSources: UpdatableFeatureSource[] = osmLayers
.filter((f) => mvtAvailableLayers.has(f.id)) .filter((f) => mvtAvailableLayers.has(f.id))
.map((l) => LayoutSource.setupMvtSource(l, mapProperties, isDisplayed(l.id))) .map((l) => ThemeSource.setupMvtSource(l, mapProperties, isDisplayed(l.id)))
const nonMvtSources = [] const nonMvtSources = []
const nonMvtLayers = osmLayers.filter((l) => !mvtAvailableLayers.has(l.id)) const nonMvtLayers = osmLayers.filter((l) => !mvtAvailableLayers.has(l.id))
const isLoading = new UIEventSource(false) const isLoading = new UIEventSource(false)
const osmApiSource = LayoutSource.setupOsmApiSource( const osmApiSource = ThemeSource.setupOsmApiSource(
osmLayers, osmLayers,
bounds, bounds,
zoom, zoom,
@ -86,7 +86,7 @@ export default class LayoutSource extends FeatureSourceMerger {
nonMvtLayers.map((l) => l.id), nonMvtLayers.map((l) => l.id),
" cannot be fetched from the cache server, defaulting to overpass/OSM-api" " cannot be fetched from the cache server, defaulting to overpass/OSM-api"
) )
overpassSource = LayoutSource.setupOverpass(osmLayers, bounds, zoom, featureSwitches) overpassSource = ThemeSource.setupOverpass(osmLayers, bounds, zoom, featureSwitches)
nonMvtSources.push(overpassSource) nonMvtSources.push(overpassSource)
supportsForceDownload.push(overpassSource) supportsForceDownload.push(overpassSource)
} }
@ -100,7 +100,7 @@ export default class LayoutSource extends FeatureSourceMerger {
osmApiSource?.isRunning?.addCallbackAndRun(() => setIsLoading()) osmApiSource?.isRunning?.addCallbackAndRun(() => setIsLoading())
const geojsonSources: UpdatableFeatureSource[] = geojsonlayers.map((l) => const geojsonSources: UpdatableFeatureSource[] = geojsonlayers.map((l) =>
LayoutSource.setupGeojsonSource(l, mapProperties, isDisplayed(l.id)) ThemeSource.setupGeojsonSource(l, mapProperties, isDisplayed(l.id))
) )
super(...geojsonSources, ...Array.from(fromCache.values()), ...mvtSources, ...nonMvtSources) super(...geojsonSources, ...Array.from(fromCache.values()), ...mvtSources, ...nonMvtSources)
@ -198,7 +198,7 @@ export default class LayoutSource extends FeatureSourceMerger {
zoom, zoom,
bounds, bounds,
layers: osmLayers, layers: osmLayers,
widenFactor: featureSwitches.layoutToUse.widenFactor, widenFactor: 1.5,
overpassUrl: featureSwitches.overpassUrl, overpassUrl: featureSwitches.overpassUrl,
overpassTimeout: featureSwitches.overpassTimeout, overpassTimeout: featureSwitches.overpassTimeout,
overpassMaxZoom: featureSwitches.overpassMaxZoom, overpassMaxZoom: featureSwitches.overpassMaxZoom,

View file

@ -2,7 +2,7 @@ import { ImageUploader, UploadResult } from "./ImageUploader"
import LinkImageAction from "../Osm/Actions/LinkImageAction" import LinkImageAction from "../Osm/Actions/LinkImageAction"
import FeaturePropertiesStore from "../FeatureSource/Actors/FeaturePropertiesStore" import FeaturePropertiesStore from "../FeatureSource/Actors/FeaturePropertiesStore"
import { OsmId, OsmTags } from "../../Models/OsmFeature" import { OsmId, OsmTags } from "../../Models/OsmFeature"
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig" import ThemeConfig from "../../Models/ThemeConfig/ThemeConfig"
import { Store, UIEventSource } from "../UIEventSource" import { Store, UIEventSource } from "../UIEventSource"
import { OsmConnection } from "../Osm/OsmConnection" import { OsmConnection } from "../Osm/OsmConnection"
import { Changes } from "../Osm/Changes" import { Changes } from "../Osm/Changes"
@ -17,7 +17,7 @@ import { GeoOperations } from "../GeoOperations"
export class ImageUploadManager { export class ImageUploadManager {
private readonly _uploader: ImageUploader private readonly _uploader: ImageUploader
private readonly _featureProperties: FeaturePropertiesStore private readonly _featureProperties: FeaturePropertiesStore
private readonly _layout: LayoutConfig private readonly _theme: ThemeConfig
private readonly _indexedFeatures: IndexedFeatureSource private readonly _indexedFeatures: IndexedFeatureSource
private readonly _gps: Store<GeolocationCoordinates | undefined> private readonly _gps: Store<GeolocationCoordinates | undefined>
private readonly _uploadStarted: Map<string, UIEventSource<number>> = new Map() private readonly _uploadStarted: Map<string, UIEventSource<number>> = new Map()
@ -31,7 +31,7 @@ export class ImageUploadManager {
private readonly _reportError: (message: (string | Error | XMLHttpRequest), extramessage?: string) => Promise<void> private readonly _reportError: (message: (string | Error | XMLHttpRequest), extramessage?: string) => Promise<void>
constructor( constructor(
layout: LayoutConfig, layout: ThemeConfig,
uploader: ImageUploader, uploader: ImageUploader,
featureProperties: FeaturePropertiesStore, featureProperties: FeaturePropertiesStore,
osmConnection: OsmConnection, osmConnection: OsmConnection,
@ -42,7 +42,7 @@ export class ImageUploadManager {
) { ) {
this._uploader = uploader this._uploader = uploader
this._featureProperties = featureProperties this._featureProperties = featureProperties
this._layout = layout this._theme = layout
this._osmConnection = osmConnection this._osmConnection = osmConnection
this._changes = changes this._changes = changes
this._indexedFeatures = allFeatures this._indexedFeatures = allFeatures
@ -131,7 +131,7 @@ export class ImageUploadManager {
const properties = this._featureProperties.getStore(featureId) const properties = this._featureProperties.getStore(featureId)
const action = new LinkImageAction(featureId, uploadResult. key, uploadResult . value, properties, { const action = new LinkImageAction(featureId, uploadResult. key, uploadResult . value, properties, {
theme: tags?.data?.["_orig_theme"] ?? this._layout.id, theme: tags?.data?.["_orig_theme"] ?? this._theme.id,
changeType: "add-image", changeType: "add-image",
}) })

View file

@ -3,7 +3,7 @@ import { ExtraFuncParams, ExtraFunctions, ExtraFuncType } from "./ExtraFunctions
import LayerConfig from "../Models/ThemeConfig/LayerConfig" import LayerConfig from "../Models/ThemeConfig/LayerConfig"
import { Feature } from "geojson" import { Feature } from "geojson"
import FeaturePropertiesStore from "./FeatureSource/Actors/FeaturePropertiesStore" import FeaturePropertiesStore from "./FeatureSource/Actors/FeaturePropertiesStore"
import LayoutConfig from "../Models/ThemeConfig/LayoutConfig" import ThemeConfig from "../Models/ThemeConfig/ThemeConfig"
import { GeoIndexedStoreForLayer } from "./FeatureSource/Actors/GeoIndexedStore" import { GeoIndexedStoreForLayer } from "./FeatureSource/Actors/GeoIndexedStore"
import { IndexedFeatureSource } from "./FeatureSource/FeatureSource" import { IndexedFeatureSource } from "./FeatureSource/FeatureSource"
import OsmObjectDownloader from "./Osm/OsmObjectDownloader" import OsmObjectDownloader from "./Osm/OsmObjectDownloader"
@ -27,7 +27,7 @@ export default class MetaTagging {
>() >()
private state: { private state: {
readonly selectedElement: Store<Feature> readonly selectedElement: Store<Feature>
readonly layout: LayoutConfig readonly theme: ThemeConfig
readonly osmObjectDownloader: OsmObjectDownloader readonly osmObjectDownloader: OsmObjectDownloader
readonly perLayer: ReadonlyMap<string, GeoIndexedStoreForLayer> readonly perLayer: ReadonlyMap<string, GeoIndexedStoreForLayer>
readonly indexedFeatures: IndexedFeatureSource readonly indexedFeatures: IndexedFeatureSource
@ -40,7 +40,7 @@ export default class MetaTagging {
constructor(state: { constructor(state: {
readonly selectedElement: Store<Feature> readonly selectedElement: Store<Feature>
readonly layout: LayoutConfig readonly theme: ThemeConfig
readonly osmObjectDownloader: OsmObjectDownloader readonly osmObjectDownloader: OsmObjectDownloader
readonly perLayer: ReadonlyMap<string, GeoIndexedStoreForLayer> readonly perLayer: ReadonlyMap<string, GeoIndexedStoreForLayer>
readonly indexedFeatures: IndexedFeatureSource readonly indexedFeatures: IndexedFeatureSource
@ -48,7 +48,7 @@ export default class MetaTagging {
}) { }) {
this.state = state this.state = state
const params = (this.params = MetaTagging.createExtraFuncParams(state)) const params = (this.params = MetaTagging.createExtraFuncParams(state))
for (const layer of state.layout.layers) { for (const layer of state.theme.layers) {
if (layer.source === null) { if (layer.source === null) {
continue continue
} }
@ -69,7 +69,7 @@ export default class MetaTagging {
features, features,
params, params,
layer, layer,
state.layout, state.theme,
state.osmObjectDownloader, state.osmObjectDownloader,
state.featureProperties state.featureProperties
) )
@ -115,7 +115,7 @@ export default class MetaTagging {
return return
} }
const state = this.state const state = this.state
const layer = state.layout.getMatchingLayer(feature.properties) const layer = state.theme.getMatchingLayer(feature.properties)
if (!layer) { if (!layer) {
return return
} }
@ -124,7 +124,7 @@ export default class MetaTagging {
[feature], [feature],
this.params, this.params,
layer, layer,
state.layout, state.theme,
state.osmObjectDownloader, state.osmObjectDownloader,
state.featureProperties, state.featureProperties,
{ {
@ -161,7 +161,7 @@ export default class MetaTagging {
features: Feature[], features: Feature[],
params: ExtraFuncParams, params: ExtraFuncParams,
layer: LayerConfig, layer: LayerConfig,
layout: LayoutConfig, theme: ThemeConfig,
osmObjectDownloader: OsmObjectDownloader, osmObjectDownloader: OsmObjectDownloader,
featurePropertiesStores?: FeaturePropertiesStore, featurePropertiesStores?: FeaturePropertiesStore,
options?: { options?: {
@ -190,7 +190,7 @@ export default class MetaTagging {
// The calculated functions - per layer - which add the new keys // The calculated functions - per layer - which add the new keys
// Calculated functions are defined by the layer // Calculated functions are defined by the layer
const layerFuncs = this.createRetaggingFunc(layer, ExtraFunctions.constructHelpers(params)) const layerFuncs = this.createRetaggingFunc(layer, ExtraFunctions.constructHelpers(params))
const state: MetataggingState = { layout, osmObjectDownloader } const state: MetataggingState = { theme: theme, osmObjectDownloader }
let atLeastOneFeatureChanged = false let atLeastOneFeatureChanged = false
let strictlyEvaluated = 0 let strictlyEvaluated = 0
@ -424,7 +424,7 @@ export default class MetaTagging {
} }
} }
if (!window.location.pathname.endsWith("theme.html")) { if (!Utils.runningFromConsole && !window.location.pathname.endsWith("theme.html")) {
console.warn( console.warn(
"Static MetataggingObject for theme is not set; using `new Function` (aka `eval`) to get calculated tags. This might trip up the CSP" "Static MetataggingObject for theme is not set; using `new Function` (aka `eval`) to get calculated tags. This might trip up the CSP"
) )

View file

@ -7,7 +7,7 @@ import CreateWayWithPointReuseAction, { MergePointConfig } from "./CreateWayWith
import { And } from "../../Tags/And" import { And } from "../../Tags/And"
import { TagUtils } from "../../Tags/TagUtils" import { TagUtils } from "../../Tags/TagUtils"
import { FeatureSource, IndexedFeatureSource } from "../../FeatureSource/FeatureSource" import { FeatureSource, IndexedFeatureSource } from "../../FeatureSource/FeatureSource"
import LayoutConfig from "../../../Models/ThemeConfig/LayoutConfig" import ThemeConfig from "../../../Models/ThemeConfig/ThemeConfig"
import { Position } from "geojson" import { Position } from "geojson"
import FullNodeDatabaseSource from "../../FeatureSource/TiledFeatureSource/FullNodeDatabaseSource" import FullNodeDatabaseSource from "../../FeatureSource/TiledFeatureSource/FullNodeDatabaseSource"
@ -32,7 +32,7 @@ export default class CreateMultiPolygonWithPointReuseAction
outerRingCoordinates: Position[], outerRingCoordinates: Position[],
innerRingsCoordinates: Position[][], innerRingsCoordinates: Position[][],
state: { state: {
layout: LayoutConfig theme: ThemeConfig
changes: Changes changes: Changes
indexedFeatures: IndexedFeatureSource indexedFeatures: IndexedFeatureSource
fullNodeDatabase?: FullNodeDatabaseSource fullNodeDatabase?: FullNodeDatabaseSource
@ -43,7 +43,7 @@ export default class CreateMultiPolygonWithPointReuseAction
super(null, true) super(null, true)
this._tags = [...tags, new Tag("type", "multipolygon")] this._tags = [...tags, new Tag("type", "multipolygon")]
this.changeType = changeType this.changeType = changeType
this.theme = state?.layout?.id ?? "" this.theme = state?.theme?.id ?? ""
this.createOuterWay = new CreateWayWithPointReuseAction( this.createOuterWay = new CreateWayWithPointReuseAction(
[], [],
<[number, number][]>outerRingCoordinates, <[number, number][]>outerRingCoordinates,
@ -55,7 +55,7 @@ export default class CreateMultiPolygonWithPointReuseAction
new CreateNewWayAction( new CreateNewWayAction(
[], [],
ringCoordinates.map(([lon, lat]) => ({ lat, lon })), ringCoordinates.map(([lon, lat]) => ({ lat, lon })),
{ theme: state?.layout?.id } { theme: state?.theme?.id }
) )
) )

View file

@ -9,7 +9,7 @@ import { FeatureSource, IndexedFeatureSource } from "../../FeatureSource/Feature
import StaticFeatureSource from "../../FeatureSource/Sources/StaticFeatureSource" import StaticFeatureSource from "../../FeatureSource/Sources/StaticFeatureSource"
import CreateNewNodeAction from "./CreateNewNodeAction" import CreateNewNodeAction from "./CreateNewNodeAction"
import CreateNewWayAction from "./CreateNewWayAction" import CreateNewWayAction from "./CreateNewWayAction"
import LayoutConfig from "../../../Models/ThemeConfig/LayoutConfig" import ThemeConfig from "../../../Models/ThemeConfig/ThemeConfig"
import FullNodeDatabaseSource from "../../FeatureSource/TiledFeatureSource/FullNodeDatabaseSource" import FullNodeDatabaseSource from "../../FeatureSource/TiledFeatureSource/FullNodeDatabaseSource"
import { Position } from "geojson" import { Position } from "geojson"
@ -69,7 +69,7 @@ export default class CreateWayWithPointReuseAction
*/ */
private readonly _coordinateInfo: CoordinateInfo[] private readonly _coordinateInfo: CoordinateInfo[]
private readonly _state: { private readonly _state: {
layout: LayoutConfig theme: ThemeConfig
changes: Changes changes: Changes
indexedFeatures: IndexedFeatureSource indexedFeatures: IndexedFeatureSource
fullNodeDatabase?: FullNodeDatabaseSource fullNodeDatabase?: FullNodeDatabaseSource
@ -80,7 +80,7 @@ export default class CreateWayWithPointReuseAction
tags: Tag[], tags: Tag[],
coordinates: Position[], coordinates: Position[],
state: { state: {
layout: LayoutConfig theme: ThemeConfig
changes: Changes changes: Changes
indexedFeatures: IndexedFeatureSource indexedFeatures: IndexedFeatureSource
fullNodeDatabase?: FullNodeDatabaseSource fullNodeDatabase?: FullNodeDatabaseSource
@ -203,7 +203,7 @@ export default class CreateWayWithPointReuseAction
} }
public async CreateChangeDescriptions(changes: Changes): Promise<ChangeDescription[]> { public async CreateChangeDescriptions(changes: Changes): Promise<ChangeDescription[]> {
const theme = this._state?.layout?.id const theme = this._state?.theme?.id
const allChanges: ChangeDescription[] = [] const allChanges: ChangeDescription[] = []
const nodeIdsToUse: { lat: number; lon: number; nodeId?: number }[] = [] const nodeIdsToUse: { lat: number; lon: number; nodeId?: number }[] = []
for (let i = 0; i < this._coordinateInfo.length; i++) { for (let i = 0; i < this._coordinateInfo.length; i++) {

View file

@ -217,7 +217,7 @@ export default class ReplaceGeometryAction extends OsmChangeAction implements Pr
const url = `${ const url = `${
this.state.osmConnection?._oauth_config?.url ?? "https://api.openstreetmap.org" this.state.osmConnection?._oauth_config?.url ?? "https://api.openstreetmap.org"
}/api/0.6/${this.wayToReplaceId}/full` }/api/0.6/${this.wayToReplaceId}/full`
const rawData = await Utils.downloadJsonCached(url, 1000) const rawData = await Utils.downloadJsonCached<{elements: any[]}>(url, 1000)
parsed = OsmObject.ParseObjects(rawData.elements) parsed = OsmObject.ParseObjects(rawData.elements)
} }
const allNodes = parsed.filter((o) => o.type === "node") const allNodes = parsed.filter((o) => o.type === "node")

View file

@ -4,7 +4,7 @@ import Constants from "../../Models/Constants"
import FilterConfig, { FilterConfigOption } from "../../Models/ThemeConfig/FilterConfig" import FilterConfig, { FilterConfigOption } from "../../Models/ThemeConfig/FilterConfig"
import LayerConfig from "../../Models/ThemeConfig/LayerConfig" import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
import LayerState from "../State/LayerState" import LayerState from "../State/LayerState"
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig" import ThemeConfig from "../../Models/ThemeConfig/ThemeConfig"
export type FilterSearchResult = { option: FilterConfigOption, filter: FilterConfig, layer: LayerConfig, index: number } export type FilterSearchResult = { option: FilterConfigOption, filter: FilterConfig, layer: LayerConfig, index: number }
@ -13,9 +13,9 @@ export type FilterSearchResult = { option: FilterConfigOption, filter: FilterCon
* Searches matching filters * Searches matching filters
*/ */
export default class FilterSearch { export default class FilterSearch {
private readonly _state: {layerState: LayerState, layout: LayoutConfig} private readonly _state: {layerState: LayerState, theme: ThemeConfig}
constructor(state: {layerState: LayerState, layout: LayoutConfig}) { constructor(state: {layerState: LayerState, theme: ThemeConfig}) {
this._state = state this._state = state
} }
@ -30,7 +30,7 @@ export default class FilterSearch {
return query return query
}).filter(q => q.length > 0) }).filter(q => q.length > 0)
const possibleFilters: FilterSearchResult[] = [] const possibleFilters: FilterSearchResult[] = []
for (const layer of this._state.layout.layers) { for (const layer of this._state.theme.layers) {
if (!Array.isArray(layer.filters)) { if (!Array.isArray(layer.filters)) {
continue continue
} }

View file

@ -1,17 +1,17 @@
import SearchUtils from "./SearchUtils" import SearchUtils from "./SearchUtils"
import ThemeSearch from "./ThemeSearch" import ThemeSearch from "./ThemeSearch"
import LayerConfig from "../../Models/ThemeConfig/LayerConfig" import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig" import ThemeConfig from "../../Models/ThemeConfig/ThemeConfig"
import { Utils } from "../../Utils" import { Utils } from "../../Utils"
export default class LayerSearch { export default class LayerSearch {
private readonly _layout: LayoutConfig private readonly _theme: ThemeConfig
private readonly _layerWhitelist: Set<string> private readonly _layerWhitelist: Set<string>
constructor(layout: LayoutConfig) { constructor(theme: ThemeConfig) {
this._layout = layout this._theme = theme
this._layerWhitelist = new Set(layout.layers this._layerWhitelist = new Set(theme.layers
.filter(l => l.isNormal()) .filter(l => l.isNormal())
.map(l => l.id)) .map(l => l.id))
} }
@ -29,8 +29,7 @@ export default class LayerSearch {
continue continue
} }
const keywords = ThemeSearch.officialThemes.layers[id] const keywords = ThemeSearch.officialThemes.layers[id]
const distance = Math.min(...queryParts.map(q => SearchUtils.scoreKeywords(q, keywords))) result[id] = Math.min(...queryParts.map(q => SearchUtils.scoreKeywords(q, keywords)))
result[id] = distance
} }
return result return result
} }
@ -44,7 +43,7 @@ export default class LayerSearch {
const asList: ({ layer: LayerConfig, score: number })[] = [] const asList: ({ layer: LayerConfig, score: number })[] = []
for (const layer in scores) { for (const layer in scores) {
asList.push({ asList.push({
layer: this._layout.getLayer(layer), layer: this._theme.getLayer(layer),
score: scores[layer], score: scores[layer],
}) })
} }

View file

@ -1,4 +1,4 @@
import LayoutConfig, { MinimalLayoutInformation } from "../../Models/ThemeConfig/LayoutConfig" import ThemeConfig, { MinimalThemeInformation } from "../../Models/ThemeConfig/ThemeConfig"
import { Store } from "../UIEventSource" import { Store } from "../UIEventSource"
import UserRelatedState from "../State/UserRelatedState" import UserRelatedState from "../State/UserRelatedState"
import { Utils } from "../../Utils" import { Utils } from "../../Utils"
@ -10,7 +10,7 @@ import { OsmConnection } from "../Osm/OsmConnection"
type ThemeSearchScore = { type ThemeSearchScore = {
theme: MinimalLayoutInformation, theme: MinimalThemeInformation,
lowest: number, lowest: number,
perLayer?: Record<string, number>, perLayer?: Record<string, number>,
other: number, other: number,
@ -20,10 +20,10 @@ type ThemeSearchScore = {
export default class ThemeSearch { export default class ThemeSearch {
public static readonly officialThemes: { public static readonly officialThemes: {
themes: MinimalLayoutInformation[], themes: MinimalThemeInformation[],
layers: Record<string, Record<string, string[]>> layers: Record<string, Record<string, string[]>>
} = <any> themeOverview } = <any> themeOverview
public static readonly officialThemesById: Map<string, MinimalLayoutInformation> = new Map<string, MinimalLayoutInformation>() public static readonly officialThemesById: Map<string, MinimalThemeInformation> = new Map<string, MinimalThemeInformation>()
static { static {
for (const th of ThemeSearch.officialThemes.themes ?? []) { for (const th of ThemeSearch.officialThemes.themes ?? []) {
ThemeSearch.officialThemesById.set(th.id, th) ThemeSearch.officialThemesById.set(th.id, th)
@ -33,17 +33,17 @@ export default class ThemeSearch {
private readonly _knownHiddenThemes: Store<Set<string>> private readonly _knownHiddenThemes: Store<Set<string>>
private readonly _layersToIgnore: string[] private readonly _layersToIgnore: string[]
private readonly _otherThemes: MinimalLayoutInformation[] private readonly _otherThemes: MinimalThemeInformation[]
constructor(state: {osmConnection: OsmConnection, layout: LayoutConfig}) { constructor(state: {osmConnection: OsmConnection, theme: ThemeConfig}) {
this._layersToIgnore = state.layout.layers.filter(l => l.isNormal()).map(l => l.id) this._layersToIgnore = state.theme.layers.filter(l => l.isNormal()).map(l => l.id)
this._knownHiddenThemes = UserRelatedState.initDiscoveredHiddenThemes(state.osmConnection).map(list => new Set(list)) this._knownHiddenThemes = UserRelatedState.initDiscoveredHiddenThemes(state.osmConnection).map(list => new Set(list))
this._otherThemes = ThemeSearch.officialThemes.themes this._otherThemes = ThemeSearch.officialThemes.themes
.filter(th => th.id !== state.layout.id) .filter(th => th.id !== state.theme.id)
} }
public search(query: string, limit: number, threshold: number = 3): MinimalLayoutInformation[] { public search(query: string, limit: number, threshold: number = 3): MinimalThemeInformation[] {
if (query.length < 1) { if (query.length < 1) {
return [] return []
} }
@ -101,7 +101,7 @@ export default class ThemeSearch {
* @param ignoreLayers * @param ignoreLayers
* @private * @private
*/ */
private static scoreThemes(query: string, themes: MinimalLayoutInformation[], ignoreLayers: string[] = undefined): Record<string, ThemeSearchScore> { private static scoreThemes(query: string, themes: MinimalThemeInformation[], ignoreLayers: string[] = undefined): Record<string, ThemeSearchScore> {
if (query?.length < 1) { if (query?.length < 1) {
return undefined return undefined
} }
@ -147,13 +147,13 @@ export default class ThemeSearch {
return results return results
} }
public static sortedByLowestScores(search: string, themes: MinimalLayoutInformation[], ignoreLayers: string[] = []): ThemeSearchScore[] { public static sortedByLowestScores(search: string, themes: MinimalThemeInformation[], ignoreLayers: string[] = []): ThemeSearchScore[] {
const scored = Object.values(this.scoreThemes(search, themes, ignoreLayers)) const scored = Object.values(this.scoreThemes(search, themes, ignoreLayers))
scored.sort((a, b) => a.lowest - b.lowest) scored.sort((a, b) => a.lowest - b.lowest)
return scored return scored
} }
public static sortedByLowest(search: string, themes: MinimalLayoutInformation[], ignoreLayers: string[] = []): MinimalLayoutInformation[] { public static sortedByLowest(search: string, themes: MinimalThemeInformation[], ignoreLayers: string[] = []): MinimalThemeInformation[] {
return this.sortedByLowestScores(search, themes, ignoreLayers) return this.sortedByLowestScores(search, themes, ignoreLayers)
.map(th => th.theme) .map(th => th.theme)
} }

View file

@ -8,7 +8,7 @@ import { TagUtils } from "./Tags/TagUtils"
import { Feature, LineString } from "geojson" import { Feature, LineString } from "geojson"
import { OsmTags } from "../Models/OsmFeature" import { OsmTags } from "../Models/OsmFeature"
import { UIEventSource } from "./UIEventSource" import { UIEventSource } from "./UIEventSource"
import LayoutConfig from "../Models/ThemeConfig/LayoutConfig" import ThemeConfig from "../Models/ThemeConfig/ThemeConfig"
import OsmObjectDownloader from "./Osm/OsmObjectDownloader" import OsmObjectDownloader from "./Osm/OsmObjectDownloader"
import countryToCurrency from "country-to-currency" import countryToCurrency from "country-to-currency"
@ -16,7 +16,7 @@ import countryToCurrency from "country-to-currency"
* All elements that are needed to perform metatagging * All elements that are needed to perform metatagging
*/ */
export interface MetataggingState { export interface MetataggingState {
layout: LayoutConfig theme: ThemeConfig
osmObjectDownloader: OsmObjectDownloader osmObjectDownloader: OsmObjectDownloader
} }
@ -399,7 +399,7 @@ export default class SimpleMetaTaggers {
}, },
(feature, _, __, state) => { (feature, _, __, state) => {
const units = Utils.NoNull( const units = Utils.NoNull(
[].concat(...(state?.layout?.layers?.map((layer) => layer.units) ?? [])) [].concat(...(state?.theme?.layers?.map((layer) => layer.units) ?? []))
) )
if (units.length == 0) { if (units.length == 0) {
return return

View file

@ -1,7 +1,7 @@
/** /**
* The part of the global state which initializes the feature switches, based on default values and on the layoutToUse * The part of the global state which initializes the feature switches, based on default values and on the theme
*/ */
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig" import ThemeConfig from "../../Models/ThemeConfig/ThemeConfig"
import { UIEventSource } from "../UIEventSource" import { UIEventSource } from "../UIEventSource"
import { QueryParameters } from "../Web/QueryParameters" import { QueryParameters } from "../Web/QueryParameters"
import Constants from "../../Models/Constants" import Constants from "../../Models/Constants"
@ -45,10 +45,6 @@ export class OsmConnectionFeatureSwitches {
} }
export default class FeatureSwitchState extends OsmConnectionFeatureSwitches { export default class FeatureSwitchState extends OsmConnectionFeatureSwitches {
/**
* The layout that is being used in this run
*/
public readonly layoutToUse: LayoutConfig
public readonly featureSwitchEnableLogin: UIEventSource<boolean> public readonly featureSwitchEnableLogin: UIEventSource<boolean>
public readonly featureSwitchSearch: UIEventSource<boolean> public readonly featureSwitchSearch: UIEventSource<boolean>
@ -74,9 +70,8 @@ export default class FeatureSwitchState extends OsmConnectionFeatureSwitches {
public readonly featureSwitchMorePrivacy: UIEventSource<boolean> public readonly featureSwitchMorePrivacy: UIEventSource<boolean>
public readonly featureSwitchLayerDefault: UIEventSource<boolean> public readonly featureSwitchLayerDefault: UIEventSource<boolean>
public constructor(layoutToUse?: LayoutConfig) { public constructor(theme?: ThemeConfig) {
super() super()
this.layoutToUse = layoutToUse
const legacyRewrite: Record<string, string | string[]> = { const legacyRewrite: Record<string, string | string[]> = {
"fs-userbadge": "fs-enable-login", "fs-userbadge": "fs-enable-login",
@ -102,7 +97,7 @@ export default class FeatureSwitchState extends OsmConnectionFeatureSwitches {
this.featureSwitchEnableLogin = FeatureSwitchUtils.initSwitch( this.featureSwitchEnableLogin = FeatureSwitchUtils.initSwitch(
"fs-enable-login", "fs-enable-login",
layoutToUse?.enableUserBadge ?? true, theme?.enableUserBadge ?? true,
"Disables/Enables logging in and thus disables editing all together. This effectively puts MapComplete into read-only mode." "Disables/Enables logging in and thus disables editing all together. This effectively puts MapComplete into read-only mode."
) )
{ {
@ -117,18 +112,18 @@ export default class FeatureSwitchState extends OsmConnectionFeatureSwitches {
this.featureSwitchSearch = FeatureSwitchUtils.initSwitch( this.featureSwitchSearch = FeatureSwitchUtils.initSwitch(
"fs-search", "fs-search",
layoutToUse?.enableSearch ?? true, theme?.enableSearch ?? true,
"Disables/Enables the search bar" "Disables/Enables the search bar"
) )
this.featureSwitchBackgroundSelection = FeatureSwitchUtils.initSwitch( this.featureSwitchBackgroundSelection = FeatureSwitchUtils.initSwitch(
"fs-background", "fs-background",
layoutToUse?.enableBackgroundLayerSelection ?? true, theme?.enableBackgroundLayerSelection ?? true,
"Disables/Enables the background layer control where a user can enable e.g. aerial imagery" "Disables/Enables the background layer control where a user can enable e.g. aerial imagery"
) )
this.featureSwitchFilter = FeatureSwitchUtils.initSwitch( this.featureSwitchFilter = FeatureSwitchUtils.initSwitch(
"fs-filter", "fs-filter",
layoutToUse?.enableLayers ?? true, theme?.enableLayers ?? true,
"Disables/Enables the filter view where a user can enable/disable MapComplete-layers or filter for certain properties" "Disables/Enables the filter view where a user can enable/disable MapComplete-layers or filter for certain properties"
) )
@ -149,17 +144,17 @@ export default class FeatureSwitchState extends OsmConnectionFeatureSwitches {
) )
this.featureSwitchBackToThemeOverview = FeatureSwitchUtils.initSwitch( this.featureSwitchBackToThemeOverview = FeatureSwitchUtils.initSwitch(
"fs-homepage-link", "fs-homepage-link",
layoutToUse?.enableMoreQuests ?? true, theme?.enableMoreQuests ?? true,
"Disables/Enables the various links which go back to the index page with the theme overview" "Disables/Enables the various links which go back to the index page with the theme overview"
) )
this.featureSwitchShareScreen = FeatureSwitchUtils.initSwitch( this.featureSwitchShareScreen = FeatureSwitchUtils.initSwitch(
"fs-share-screen", "fs-share-screen",
layoutToUse?.enableShareScreen ?? true, theme?.enableShareScreen ?? true,
"Disables/Enables the 'Share-screen'-tab in the welcome message" "Disables/Enables the 'Share-screen'-tab in the welcome message"
) )
this.featureSwitchGeolocation = FeatureSwitchUtils.initSwitch( this.featureSwitchGeolocation = FeatureSwitchUtils.initSwitch(
"fs-geolocation", "fs-geolocation",
layoutToUse?.enableGeolocation ?? true, theme?.enableGeolocation ?? true,
"Disables/Enables the geolocation button" "Disables/Enables the geolocation button"
) )
@ -170,19 +165,19 @@ export default class FeatureSwitchState extends OsmConnectionFeatureSwitches {
) )
this.featureSwitchShowAllQuestions = FeatureSwitchUtils.initSwitch( this.featureSwitchShowAllQuestions = FeatureSwitchUtils.initSwitch(
"fs-all-questions", "fs-all-questions",
layoutToUse?.enableShowAllQuestions ?? false, theme?.enableShowAllQuestions ?? false,
"Always show all questions" "Always show all questions"
) )
this.featureSwitchEnableExport = FeatureSwitchUtils.initSwitch( this.featureSwitchEnableExport = FeatureSwitchUtils.initSwitch(
"fs-export", "fs-export",
layoutToUse?.enableExportButton ?? true, theme?.enableExportButton ?? true,
"Enable the export as GeoJSON and CSV button" "Enable the export as GeoJSON and CSV button"
) )
this.featureSwitchCache = FeatureSwitchUtils.initSwitch( this.featureSwitchCache = FeatureSwitchUtils.initSwitch(
"fs-cache", "fs-cache",
layoutToUse?.enableCache ?? true, theme?.enableCache ?? true,
"Enable/disable caching from localStorage" "Enable/disable caching from localStorage"
) )
@ -209,13 +204,13 @@ export default class FeatureSwitchState extends OsmConnectionFeatureSwitches {
this.featureSwitchMorePrivacy = QueryParameters.GetBooleanQueryParameter( this.featureSwitchMorePrivacy = QueryParameters.GetBooleanQueryParameter(
"moreprivacy", "moreprivacy",
layoutToUse.enableMorePrivacy, theme.enableMorePrivacy,
"If true, the location distance indication will not be written to the changeset and other privacy enhancing measures might be taken." "If true, the location distance indication will not be written to the changeset and other privacy enhancing measures might be taken."
) )
this.overpassUrl = QueryParameters.GetQueryParameter( this.overpassUrl = QueryParameters.GetQueryParameter(
"overpassUrl", "overpassUrl",
(layoutToUse?.overpassUrl ?? Constants.defaultOverpassUrls).join(","), (theme?.overpassUrl ?? Constants.defaultOverpassUrls).join(","),
"Point mapcomplete to a different overpass-instance. Example: https://overpass-api.de/api/interpreter" "Point mapcomplete to a different overpass-instance. Example: https://overpass-api.de/api/interpreter"
).sync( ).sync(
(param) => param?.split(","), (param) => param?.split(","),
@ -226,7 +221,7 @@ export default class FeatureSwitchState extends OsmConnectionFeatureSwitches {
this.overpassTimeout = UIEventSource.asInt( this.overpassTimeout = UIEventSource.asInt(
QueryParameters.GetQueryParameter( QueryParameters.GetQueryParameter(
"overpassTimeout", "overpassTimeout",
"" + layoutToUse?.overpassTimeout, "" + theme?.overpassTimeout,
"Set a different timeout (in seconds) for queries in overpass" "Set a different timeout (in seconds) for queries in overpass"
) )
) )
@ -234,7 +229,7 @@ export default class FeatureSwitchState extends OsmConnectionFeatureSwitches {
this.overpassMaxZoom = UIEventSource.asFloat( this.overpassMaxZoom = UIEventSource.asFloat(
QueryParameters.GetQueryParameter( QueryParameters.GetQueryParameter(
"overpassMaxZoom", "overpassMaxZoom",
"" + layoutToUse?.overpassMaxZoom, "" + theme?.overpassMaxZoom,
" point to switch between OSM-api and overpass" " point to switch between OSM-api and overpass"
) )
) )
@ -242,14 +237,14 @@ export default class FeatureSwitchState extends OsmConnectionFeatureSwitches {
this.osmApiTileSize = UIEventSource.asInt( this.osmApiTileSize = UIEventSource.asInt(
QueryParameters.GetQueryParameter( QueryParameters.GetQueryParameter(
"osmApiTileSize", "osmApiTileSize",
"" + layoutToUse?.osmApiTileSize, "" + theme?.osmApiTileSize,
"Tilesize when the OSM-API is used to fetch data within a BBOX" "Tilesize when the OSM-API is used to fetch data within a BBOX"
) )
) )
this.backgroundLayerId = QueryParameters.GetQueryParameter( this.backgroundLayerId = QueryParameters.GetQueryParameter(
"background", "background",
layoutToUse?.defaultBackgroundId, theme?.defaultBackgroundId,
[ [
"When set, load this raster layer (or a layer of this category) as background layer instead of using the default background. This is as if the user opened the background selection menu and selected the layer with the given id or category.", "When set, load this raster layer (or a layer of this category) as background layer instead of using the default background. This is as if the user opened the background selection menu and selected the layer with the given id or category.",
"Most raster layers are based on the [editor layer index](https://github.com/osmlab/editor-layer-index)", "Most raster layers are based on the [editor layer index](https://github.com/osmlab/editor-layer-index)",

View file

@ -8,7 +8,7 @@ import ThemeSearch from "../Search/ThemeSearch"
import OpenStreetMapIdSearch from "../Search/OpenStreetMapIdSearch" import OpenStreetMapIdSearch from "../Search/OpenStreetMapIdSearch"
import PhotonSearch from "../Search/PhotonSearch" import PhotonSearch from "../Search/PhotonSearch"
import ThemeViewState from "../../Models/ThemeViewState" import ThemeViewState from "../../Models/ThemeViewState"
import type { MinimalLayoutInformation } from "../../Models/ThemeConfig/LayoutConfig" import type { MinimalThemeInformation } from "../../Models/ThemeConfig/ThemeConfig"
import { Translation } from "../../UI/i18n/Translation" import { Translation } from "../../UI/i18n/Translation"
import GeocodingFeatureSource from "../Search/GeocodingFeatureSource" import GeocodingFeatureSource from "../Search/GeocodingFeatureSource"
import LayerSearch from "../Search/LayerSearch" import LayerSearch from "../Search/LayerSearch"
@ -23,7 +23,7 @@ export default class SearchState {
public readonly searchIsFocused = new UIEventSource(false) public readonly searchIsFocused = new UIEventSource(false)
public readonly suggestions: Store<SearchResult[]> public readonly suggestions: Store<SearchResult[]>
public readonly filterSuggestions: Store<FilterSearchResult[]> public readonly filterSuggestions: Store<FilterSearchResult[]>
public readonly themeSuggestions: Store<MinimalLayoutInformation[]> public readonly themeSuggestions: Store<MinimalThemeInformation[]>
public readonly layerSuggestions: Store<LayerConfig[]> public readonly layerSuggestions: Store<LayerConfig[]>
public readonly locationSearchers: ReadonlyArray<GeocodingProvider> public readonly locationSearchers: ReadonlyArray<GeocodingProvider>
@ -64,7 +64,7 @@ export default class SearchState {
const themeSearch = new ThemeSearch(state) const themeSearch = new ThemeSearch(state)
this.themeSuggestions = this.searchTerm.mapD(query => themeSearch.search(query, 3)) this.themeSuggestions = this.searchTerm.mapD(query => themeSearch.search(query, 3))
const layerSearch = new LayerSearch(state.layout) const layerSearch = new LayerSearch(state.theme)
this.layerSuggestions = this.searchTerm.mapD(query => layerSearch.search(query, 5)) this.layerSuggestions = this.searchTerm.mapD(query => layerSearch.search(query, 5))
const filterSearch = new FilterSearch(state) const filterSearch = new FilterSearch(state)

View file

@ -1,4 +1,4 @@
import LayoutConfig, { MinimalLayoutInformation } from "../../Models/ThemeConfig/LayoutConfig" import ThemeConfig, { MinimalThemeInformation } from "../../Models/ThemeConfig/ThemeConfig"
import { OsmConnection } from "../Osm/OsmConnection" import { OsmConnection } from "../Osm/OsmConnection"
import { MangroveIdentity } from "../Web/MangroveReviews" import { MangroveIdentity } from "../Web/MangroveReviews"
import { Store, Stores, UIEventSource } from "../UIEventSource" import { Store, Stores, UIEventSource } from "../UIEventSource"
@ -183,7 +183,7 @@ export default class UserRelatedState {
constructor( constructor(
osmConnection: OsmConnection, osmConnection: OsmConnection,
layout?: LayoutConfig, layout?: ThemeConfig,
featureSwitches?: FeatureSwitchState, featureSwitches?: FeatureSwitchState,
mapProperties?: MapProperties, mapProperties?: MapProperties,
) { ) {
@ -277,14 +277,14 @@ export default class UserRelatedState {
* *
* @param themeInfo note that themeInfo.id should be the URL where it was found * @param themeInfo note that themeInfo.id should be the URL where it was found
*/ */
public addUnofficialTheme(themeInfo: MinimalLayoutInformation) { public addUnofficialTheme(themeInfo: MinimalThemeInformation) {
const pref = this.osmConnection.getPreference("unofficial-theme-" + themeInfo.id) const pref = this.osmConnection.getPreference("unofficial-theme-" + themeInfo.id)
this.osmConnection.isLoggedIn.when( this.osmConnection.isLoggedIn.when(
() => pref.set(JSON.stringify(themeInfo)) () => pref.set(JSON.stringify(themeInfo))
) )
} }
public getUnofficialTheme(id: string): MinimalLayoutInformation | undefined { public getUnofficialTheme(id: string): MinimalThemeInformation | undefined {
const pref = this.osmConnection.getPreference("unofficial-theme-" + id) const pref = this.osmConnection.getPreference("unofficial-theme-" + id)
const str = pref.data const str = pref.data
@ -307,7 +307,7 @@ export default class UserRelatedState {
} }
} }
public markLayoutAsVisited(layout: LayoutConfig) { public markLayoutAsVisited(layout: ThemeConfig) {
if (!layout) { if (!layout) {
console.error("Trying to mark a layout as visited, but ", layout, " got passed") console.error("Trying to mark a layout as visited, but ", layout, " got passed")
return return
@ -399,7 +399,7 @@ export default class UserRelatedState {
* This is inherently a dirty and chaotic method, as it shoves many properties into this EventSource * This is inherently a dirty and chaotic method, as it shoves many properties into this EventSource
* */ * */
private initAmendedPrefs( private initAmendedPrefs(
layout?: LayoutConfig, layout?: ThemeConfig,
featureSwitches?: FeatureSwitchState, featureSwitches?: FeatureSwitchState,
): UIEventSource<Record<string, string>> { ): UIEventSource<Record<string, string>> {
const amendedPrefs = new UIEventSource<Record<string, string>>({ const amendedPrefs = new UIEventSource<Record<string, string>>({

View file

@ -30,6 +30,8 @@ export class LocalStorageSource {
return cached return cached
} }
let saved = defaultValue let saved = defaultValue
if (!Utils.runningFromConsole) {
try { try {
saved = localStorage.getItem(key) saved = localStorage.getItem(key)
if (saved === "undefined") { if (saved === "undefined") {
@ -38,6 +40,7 @@ export class LocalStorageSource {
} catch (e) { } catch (e) {
console.error("Could not get value", key, "from local storage") console.error("Could not get value", key, "from local storage")
} }
}
const source = new UIEventSource<string>(saved ?? defaultValue, "localstorage:" + key) const source = new UIEventSource<string>(saved ?? defaultValue, "localstorage:" + key)
source.addCallback((data) => { source.addCallback((data) => {

View file

@ -55,7 +55,7 @@ export default class Constants {
// The user journey states thresholds when a new feature gets unlocked // The user journey states thresholds when a new feature gets unlocked
public static userJourney = { public static userJourney = {
moreScreenUnlock: 1, moreScreenUnlock: 1,
personalLayoutUnlock: 5, personalThemeUnlock: 5,
historyLinkVisible: 10, historyLinkVisible: 10,
deletePointsOfOthersUnlock: 20, deletePointsOfOthersUnlock: 20,
tagsVisibleAt: 25, tagsVisibleAt: 25,

View file

@ -1,5 +1,5 @@
import { Conversion, DesugaringStep } from "./Conversion" import { Conversion, DesugaringStep } from "./Conversion"
import { LayoutConfigJson } from "../Json/LayoutConfigJson" import { ThemeConfigJson } from "../Json/ThemeConfigJson"
import { Utils } from "../../../Utils" import { Utils } from "../../../Utils"
import metapaths from "../../../assets/schemas/layoutconfigmeta.json" import metapaths from "../../../assets/schemas/layoutconfigmeta.json"
import tagrenderingmetapaths from "../../../assets/schemas/questionabletagrenderingconfigmeta.json" import tagrenderingmetapaths from "../../../assets/schemas/questionabletagrenderingconfigmeta.json"
@ -9,7 +9,7 @@ import { parse as parse_html } from "node-html-parser"
import { ConversionContext } from "./ConversionContext" import { ConversionContext } from "./ConversionContext"
export class ExtractImages extends Conversion< export class ExtractImages extends Conversion<
LayoutConfigJson, ThemeConfigJson,
{ path: string; context: string }[] { path: string; context: string }[]
> { > {
private static readonly layoutMetaPaths = metapaths.filter((mp) => { private static readonly layoutMetaPaths = metapaths.filter((mp) => {
@ -109,7 +109,7 @@ export class ExtractImages extends Conversion<
* *
*/ */
convert( convert(
json: LayoutConfigJson, json: ThemeConfigJson,
context: ConversionContext context: ConversionContext
): { path: string; context: string }[] { ): { path: string; context: string }[] {
const allFoundImages: { path: string; context: string }[] = [] const allFoundImages: { path: string; context: string }[] = []
@ -243,7 +243,7 @@ export class ExtractImages extends Conversion<
} }
} }
export class FixImages extends DesugaringStep<LayoutConfigJson> { export class FixImages extends DesugaringStep<ThemeConfigJson> {
private readonly _knownImages: Set<string> private readonly _knownImages: Set<string>
constructor(knownImages: Set<string>) { constructor(knownImages: Set<string>) {
@ -289,7 +289,7 @@ export class FixImages extends DesugaringStep<LayoutConfigJson> {
* fixed.layers[0]["pointRendering"][0].marker[0].icon // => "https://raw.githubusercontent.com/seppesantens/MapComplete-Themes/main/VerkeerdeBordenDatabank/TS_bolt.svg" * fixed.layers[0]["pointRendering"][0].marker[0].icon // => "https://raw.githubusercontent.com/seppesantens/MapComplete-Themes/main/VerkeerdeBordenDatabank/TS_bolt.svg"
* fixed.layers[0]["pointRendering"][0].iconBadges[0].then.mappings[0].then // => "https://raw.githubusercontent.com/seppesantens/MapComplete-Themes/main/VerkeerdeBordenDatabank/Something.svg" * fixed.layers[0]["pointRendering"][0].iconBadges[0].then.mappings[0].then // => "https://raw.githubusercontent.com/seppesantens/MapComplete-Themes/main/VerkeerdeBordenDatabank/Something.svg"
*/ */
convert(json: LayoutConfigJson, context: ConversionContext): LayoutConfigJson { convert(json: ThemeConfigJson, context: ConversionContext): ThemeConfigJson {
let url: URL let url: URL
try { try {
url = new URL(json.id) url = new URL(json.id)

View file

@ -1,4 +1,4 @@
import { LayoutConfigJson } from "../Json/LayoutConfigJson" import { ThemeConfigJson } from "../Json/ThemeConfigJson"
import { Utils } from "../../../Utils" import { Utils } from "../../../Utils"
import LineRenderingConfigJson from "../Json/LineRenderingConfigJson" import LineRenderingConfigJson from "../Json/LineRenderingConfigJson"
import { LayerConfigJson } from "../Json/LayerConfigJson" import { LayerConfigJson } from "../Json/LayerConfigJson"
@ -257,12 +257,12 @@ export class UpdateLegacyLayer extends DesugaringStep<
} }
} }
class UpdateLegacyTheme extends DesugaringStep<LayoutConfigJson> { class UpdateLegacyTheme extends DesugaringStep<ThemeConfigJson> {
constructor() { constructor() {
super("Small fixes in the theme config", ["roamingRenderings"], "UpdateLegacyTheme") super("Small fixes in the theme config", ["roamingRenderings"], "UpdateLegacyTheme")
} }
convert(json: LayoutConfigJson, context: ConversionContext): LayoutConfigJson { convert(json: ThemeConfigJson, context: ConversionContext): ThemeConfigJson {
const oldThemeConfig = { ...json } const oldThemeConfig = { ...json }
if (oldThemeConfig.socialImage === "") { if (oldThemeConfig.socialImage === "") {
@ -311,7 +311,7 @@ class UpdateLegacyTheme extends DesugaringStep<LayoutConfigJson> {
} }
} }
export class FixLegacyTheme extends Fuse<LayoutConfigJson> { export class FixLegacyTheme extends Fuse<ThemeConfigJson> {
constructor() { constructor() {
super( super(
"Fixes a legacy theme to the modern JSON format geared to humans. Syntactic sugars are kept (i.e. no tagRenderings are expandend, no dependencies are automatically gathered)", "Fixes a legacy theme to the modern JSON format geared to humans. Syntactic sugars are kept (i.e. no tagRenderings are expandend, no dependencies are automatically gathered)",

View file

@ -1,5 +1,5 @@
import { Concat, Conversion, DesugaringContext, DesugaringStep, Each, Fuse, On, Pass, SetDefault } from "./Conversion" import { Concat, Conversion, DesugaringContext, DesugaringStep, Each, Fuse, On, Pass, SetDefault } from "./Conversion"
import { LayoutConfigJson } from "../Json/LayoutConfigJson" import { ThemeConfigJson } from "../Json/ThemeConfigJson"
import { PrepareLayer } from "./PrepareLayer" import { PrepareLayer } from "./PrepareLayer"
import { LayerConfigJson } from "../Json/LayerConfigJson" import { LayerConfigJson } from "../Json/LayerConfigJson"
import { Utils } from "../../../Utils" import { Utils } from "../../../Utils"
@ -165,7 +165,7 @@ class SubstituteLayer extends Conversion<string | LayerConfigJson, LayerConfigJs
} }
} }
class AddDefaultLayers extends DesugaringStep<LayoutConfigJson> { class AddDefaultLayers extends DesugaringStep<ThemeConfigJson> {
private readonly _state: DesugaringContext private readonly _state: DesugaringContext
constructor(state: DesugaringContext) { constructor(state: DesugaringContext) {
@ -177,7 +177,7 @@ class AddDefaultLayers extends DesugaringStep<LayoutConfigJson> {
this._state = state this._state = state
} }
convert(json: LayoutConfigJson, context: ConversionContext): LayoutConfigJson { convert(json: ThemeConfigJson, context: ConversionContext): ThemeConfigJson {
const state = this._state const state = this._state
json.layers = Utils.NoNull([...(json.layers ?? [])]) json.layers = Utils.NoNull([...(json.layers ?? [])])
const alreadyLoaded = new Set(json.layers.map((l) => l["id"])) const alreadyLoaded = new Set(json.layers.map((l) => l["id"]))
@ -209,7 +209,7 @@ class AddDefaultLayers extends DesugaringStep<LayoutConfigJson> {
} }
} }
class AddContextToTranslationsInLayout extends DesugaringStep<LayoutConfigJson> { class AddContextToTranslationsInLayout extends DesugaringStep<ThemeConfigJson> {
constructor() { constructor() {
super( super(
"Adds context to translations, including the prefix 'themes:json.id'; this is to make sure terms in an 'overrides' or inline layer are linkable too", "Adds context to translations, including the prefix 'themes:json.id'; this is to make sure terms in an 'overrides' or inline layer are linkable too",
@ -218,8 +218,8 @@ class AddContextToTranslationsInLayout extends DesugaringStep<LayoutConfigJson>
) )
} }
convert(json: LayoutConfigJson): LayoutConfigJson { convert(json: ThemeConfigJson): ThemeConfigJson {
const conversion = new AddContextToTranslations<LayoutConfigJson>("themes:") const conversion = new AddContextToTranslations<ThemeConfigJson>("themes:")
// The context is used to generate the 'context' in the translation .It _must_ be `json.id` to correctly link into weblate // The context is used to generate the 'context' in the translation .It _must_ be `json.id` to correctly link into weblate
return conversion.convert( return conversion.convert(
json, json,
@ -228,7 +228,7 @@ class AddContextToTranslationsInLayout extends DesugaringStep<LayoutConfigJson>
} }
} }
class ApplyOverrideAll extends DesugaringStep<LayoutConfigJson> { class ApplyOverrideAll extends DesugaringStep<ThemeConfigJson> {
constructor() { constructor() {
super( super(
"Applies 'overrideAll' onto every 'layer'. The 'overrideAll'-field is removed afterwards", "Applies 'overrideAll' onto every 'layer'. The 'overrideAll'-field is removed afterwards",
@ -237,7 +237,7 @@ class ApplyOverrideAll extends DesugaringStep<LayoutConfigJson> {
) )
} }
convert(json: LayoutConfigJson, ctx: ConversionContext): LayoutConfigJson { convert(json: ThemeConfigJson, ctx: ConversionContext): ThemeConfigJson {
const overrideAll = json.overrideAll const overrideAll = json.overrideAll
if (overrideAll === undefined) { if (overrideAll === undefined) {
return json return json
@ -278,7 +278,7 @@ class ApplyOverrideAll extends DesugaringStep<LayoutConfigJson> {
} }
} }
class AddDependencyLayersToTheme extends DesugaringStep<LayoutConfigJson> { class AddDependencyLayersToTheme extends DesugaringStep<ThemeConfigJson> {
private readonly _state: DesugaringContext private readonly _state: DesugaringContext
constructor(state: DesugaringContext) { constructor(state: DesugaringContext) {
@ -390,7 +390,7 @@ class AddDependencyLayersToTheme extends DesugaringStep<LayoutConfigJson> {
return dependenciesToAdd return dependenciesToAdd
} }
convert(theme: LayoutConfigJson, context: ConversionContext): LayoutConfigJson { convert(theme: ThemeConfigJson, context: ConversionContext): ThemeConfigJson {
const state = this._state const state = this._state
const allKnownLayers: Map<string, LayerConfigJson> = state.sharedLayers const allKnownLayers: Map<string, LayerConfigJson> = state.sharedLayers
const knownTagRenderings: Map<string, TagRenderingConfigJson> = state.tagRenderings const knownTagRenderings: Map<string, TagRenderingConfigJson> = state.tagRenderings
@ -428,7 +428,7 @@ class AddDependencyLayersToTheme extends DesugaringStep<LayoutConfigJson> {
} }
} }
class PreparePersonalTheme extends DesugaringStep<LayoutConfigJson> { class PreparePersonalTheme extends DesugaringStep<ThemeConfigJson> {
private readonly _state: DesugaringContext private readonly _state: DesugaringContext
constructor(state: DesugaringContext) { constructor(state: DesugaringContext) {
@ -436,7 +436,7 @@ class PreparePersonalTheme extends DesugaringStep<LayoutConfigJson> {
this._state = state this._state = state
} }
convert(json: LayoutConfigJson, context: ConversionContext): LayoutConfigJson { convert(json: ThemeConfigJson, context: ConversionContext): ThemeConfigJson {
if (json.id !== "personal") { if (json.id !== "personal") {
return json return json
} }
@ -452,7 +452,7 @@ class PreparePersonalTheme extends DesugaringStep<LayoutConfigJson> {
} }
} }
class WarnForUnsubstitutedLayersInTheme extends DesugaringStep<LayoutConfigJson> { class WarnForUnsubstitutedLayersInTheme extends DesugaringStep<ThemeConfigJson> {
constructor() { constructor() {
super( super(
"Generates a warning if a theme uses an unsubstituted layer", "Generates a warning if a theme uses an unsubstituted layer",
@ -461,7 +461,7 @@ class WarnForUnsubstitutedLayersInTheme extends DesugaringStep<LayoutConfigJson>
) )
} }
convert(json: LayoutConfigJson, context: ConversionContext): LayoutConfigJson { convert(json: ThemeConfigJson, context: ConversionContext): ThemeConfigJson {
if (json.hideFromOverview === true) { if (json.hideFromOverview === true) {
return json return json
} }
@ -503,7 +503,7 @@ class WarnForUnsubstitutedLayersInTheme extends DesugaringStep<LayoutConfigJson>
} }
} }
class PostvalidateTheme extends DesugaringStep<LayoutConfigJson> { class PostvalidateTheme extends DesugaringStep<ThemeConfigJson> {
private readonly _state: DesugaringContext private readonly _state: DesugaringContext
constructor(state: DesugaringContext) { constructor(state: DesugaringContext) {
@ -511,7 +511,7 @@ class PostvalidateTheme extends DesugaringStep<LayoutConfigJson> {
this._state = state this._state = state
} }
convert(json: LayoutConfigJson, context: ConversionContext): LayoutConfigJson { convert(json: ThemeConfigJson, context: ConversionContext): ThemeConfigJson {
for (const l of json.layers) { for (const l of json.layers) {
const layer = <LayerConfigJson>l const layer = <LayerConfigJson>l
const basedOn = <string>layer["_basedOn"] const basedOn = <string>layer["_basedOn"]
@ -582,7 +582,7 @@ class PostvalidateTheme extends DesugaringStep<LayoutConfigJson> {
} }
} }
export class PrepareTheme extends Fuse<LayoutConfigJson> { export class PrepareTheme extends Fuse<ThemeConfigJson> {
private state: DesugaringContext private state: DesugaringContext
constructor( constructor(
@ -616,7 +616,7 @@ export class PrepareTheme extends Fuse<LayoutConfigJson> {
this.state = state this.state = state
} }
convert(json: LayoutConfigJson, context: ConversionContext): LayoutConfigJson { convert(json: ThemeConfigJson, context: ConversionContext): ThemeConfigJson {
const result = super.convert(json, context) const result = super.convert(json, context)
if ((this.state.publicLayers?.size ?? 0) === 0) { if ((this.state.publicLayers?.size ?? 0) === 0) {
// THis is a bootstrapping run, no need to already set this flag // THis is a bootstrapping run, no need to already set this flag

View file

@ -1,13 +1,13 @@
import { DesugaringStep } from "./Conversion" import { DesugaringStep } from "./Conversion"
import { LayoutConfigJson } from "../Json/LayoutConfigJson" import { ThemeConfigJson } from "../Json/ThemeConfigJson"
import { AvailableRasterLayers } from "../../RasterLayers" import { AvailableRasterLayers } from "../../RasterLayers"
import { ExtractImages } from "./FixImages" import { ExtractImages } from "./FixImages"
import { ConversionContext } from "./ConversionContext" import { ConversionContext } from "./ConversionContext"
import LayoutConfig from "../LayoutConfig" import ThemeConfig from "../ThemeConfig"
import { Utils } from "../../../Utils" import { Utils } from "../../../Utils"
import { DetectDuplicatePresets, DoesImageExist, ValidateLanguageCompleteness } from "./Validation" import { DetectDuplicatePresets, DoesImageExist, ValidateLanguageCompleteness } from "./Validation"
export class ValidateTheme extends DesugaringStep<LayoutConfigJson> { export class ValidateTheme extends DesugaringStep<ThemeConfigJson> {
/** /**
* The paths where this layer is originally saved. Triggers some extra checks * The paths where this layer is originally saved. Triggers some extra checks
* @private * @private
@ -33,8 +33,8 @@ export class ValidateTheme extends DesugaringStep<LayoutConfigJson> {
} }
} }
convert(json: LayoutConfigJson, context: ConversionContext): LayoutConfigJson { convert(json: ThemeConfigJson, context: ConversionContext): ThemeConfigJson {
const theme = new LayoutConfig(json, this._isBuiltin) const theme = new ThemeConfig(json, this._isBuiltin)
{ {
// Legacy format checks // Legacy format checks
if (this._isBuiltin) { if (this._isBuiltin) {

View file

@ -1,10 +1,10 @@
import { Bypass, Each, Fuse, On } from "./Conversion" import { Bypass, Each, Fuse, On } from "./Conversion"
import { LayoutConfigJson } from "../Json/LayoutConfigJson" import { ThemeConfigJson } from "../Json/ThemeConfigJson"
import Constants from "../../Constants" import Constants from "../../Constants"
import { DoesImageExist, ValidateLayerConfig } from "./Validation" import { DoesImageExist, ValidateLayerConfig } from "./Validation"
import { ValidateTheme } from "./ValidateTheme" import { ValidateTheme } from "./ValidateTheme"
export class ValidateThemeAndLayers extends Fuse<LayoutConfigJson> { export class ValidateThemeAndLayers extends Fuse<ThemeConfigJson> {
constructor( constructor(
doesImageExist: DoesImageExist, doesImageExist: DoesImageExist,
path: string, path: string,

View file

@ -4,8 +4,8 @@ import LayerConfig from "../LayerConfig"
import { Utils } from "../../../Utils" import { Utils } from "../../../Utils"
import Constants from "../../Constants" import Constants from "../../Constants"
import { Translation } from "../../../UI/i18n/Translation" import { Translation } from "../../../UI/i18n/Translation"
import { LayoutConfigJson } from "../Json/LayoutConfigJson" import { ThemeConfigJson } from "../Json/ThemeConfigJson"
import LayoutConfig from "../LayoutConfig" import ThemeConfig from "../ThemeConfig"
import { TagRenderingConfigJson } from "../Json/TagRenderingConfigJson" import { TagRenderingConfigJson } from "../Json/TagRenderingConfigJson"
import { TagUtils } from "../../../Logic/Tags/TagUtils" import { TagUtils } from "../../../Logic/Tags/TagUtils"
import { And } from "../../../Logic/Tags/And" import { And } from "../../../Logic/Tags/And"
@ -23,7 +23,7 @@ import { PrevalidateLayer } from "./PrevalidateLayer"
import { AvailableRasterLayers } from "../../RasterLayers" import { AvailableRasterLayers } from "../../RasterLayers"
import { eliCategory } from "../../RasterLayerProperties" import { eliCategory } from "../../RasterLayerProperties"
export class ValidateLanguageCompleteness extends DesugaringStep<LayoutConfig> { export class ValidateLanguageCompleteness extends DesugaringStep<ThemeConfig> {
private readonly _languages: string[] private readonly _languages: string[]
constructor(...languages: string[]) { constructor(...languages: string[]) {
@ -35,7 +35,7 @@ export class ValidateLanguageCompleteness extends DesugaringStep<LayoutConfig> {
this._languages = languages ?? ["en"] this._languages = languages ?? ["en"]
} }
convert(obj: LayoutConfig, context: ConversionContext): LayoutConfig { convert(obj: ThemeConfig, context: ConversionContext): ThemeConfig {
const origLayers = obj.layers const origLayers = obj.layers
obj.layers = [...obj.layers].filter((l) => l["id"] !== "favourite") obj.layers = [...obj.layers].filter((l) => l["id"] !== "favourite")
const translations = Translation.ExtractAllTranslationsFrom(obj) const translations = Translation.ExtractAllTranslationsFrom(obj)
@ -128,7 +128,7 @@ export class DoesImageExist extends DesugaringStep<string> {
} }
} }
class OverrideShadowingCheck extends DesugaringStep<LayoutConfigJson> { class OverrideShadowingCheck extends DesugaringStep<ThemeConfigJson> {
constructor() { constructor() {
super( super(
"Checks that an 'overrideAll' does not override a single override", "Checks that an 'overrideAll' does not override a single override",
@ -137,7 +137,7 @@ class OverrideShadowingCheck extends DesugaringStep<LayoutConfigJson> {
) )
} }
convert(json: LayoutConfigJson, context: ConversionContext): LayoutConfigJson { convert(json: ThemeConfigJson, context: ConversionContext): ThemeConfigJson {
const overrideAll = json.overrideAll const overrideAll = json.overrideAll
if (overrideAll === undefined) { if (overrideAll === undefined) {
return json return json
@ -170,12 +170,12 @@ class OverrideShadowingCheck extends DesugaringStep<LayoutConfigJson> {
} }
} }
class MiscThemeChecks extends DesugaringStep<LayoutConfigJson> { class MiscThemeChecks extends DesugaringStep<ThemeConfigJson> {
constructor() { constructor() {
super("Miscelleanous checks on the theme", [], "MiscThemesChecks") super("Miscelleanous checks on the theme", [], "MiscThemesChecks")
} }
convert(json: LayoutConfigJson, context: ConversionContext): LayoutConfigJson { convert(json: ThemeConfigJson, context: ConversionContext): ThemeConfigJson {
if (json.id !== "personal" && (json.layers === undefined || json.layers.length === 0)) { if (json.id !== "personal" && (json.layers === undefined || json.layers.length === 0)) {
context.err("The theme " + json.id + " has no 'layers' defined") context.err("The theme " + json.id + " has no 'layers' defined")
} }
@ -240,7 +240,7 @@ class MiscThemeChecks extends DesugaringStep<LayoutConfigJson> {
} }
} }
export class PrevalidateTheme extends Fuse<LayoutConfigJson> { export class PrevalidateTheme extends Fuse<ThemeConfigJson> {
constructor() { constructor() {
super( super(
"Various consistency checks on the raw JSON", "Various consistency checks on the raw JSON",
@ -905,7 +905,7 @@ export class ValidateFilter extends DesugaringStep<FilterConfigJson> {
export class DetectDuplicateFilters extends DesugaringStep<{ export class DetectDuplicateFilters extends DesugaringStep<{
layers: LayerConfigJson[] layers: LayerConfigJson[]
themes: LayoutConfigJson[] themes: ThemeConfigJson[]
}> { }> {
constructor() { constructor() {
super( super(
@ -916,15 +916,15 @@ export class DetectDuplicateFilters extends DesugaringStep<{
} }
convert( convert(
json: { layers: LayerConfigJson[]; themes: LayoutConfigJson[] }, json: { layers: LayerConfigJson[]; themes: ThemeConfigJson[] },
context: ConversionContext, context: ConversionContext,
): { layers: LayerConfigJson[]; themes: LayoutConfigJson[] } { ): { layers: LayerConfigJson[]; themes: ThemeConfigJson[] } {
const { layers, themes } = json const { layers, themes } = json
const perOsmTag = new Map< const perOsmTag = new Map<
string, string,
{ {
layer: LayerConfigJson layer: LayerConfigJson
layout: LayoutConfigJson | undefined theme: ThemeConfigJson | undefined
filter: FilterConfigJson filter: FilterConfigJson
}[] }[]
>() >()
@ -955,10 +955,10 @@ export class DetectDuplicateFilters extends DesugaringStep<{
return return
} }
let msg = "Possible duplicate filter: " + key let msg = "Possible duplicate filter: " + key
for (const { filter, layer, layout } of value) { for (const { filter, layer, theme } of value) {
let id = "" let id = ""
if (layout !== undefined) { if (theme !== undefined) {
id = layout.id + ":" id = theme.id + ":"
} }
msg += `\n - ${id}${layer.id}.${filter.id}` msg += `\n - ${id}${layer.id}.${filter.id}`
} }
@ -977,11 +977,11 @@ export class DetectDuplicateFilters extends DesugaringStep<{
string, string,
{ {
layer: LayerConfigJson layer: LayerConfigJson
layout: LayoutConfigJson | undefined theme: ThemeConfigJson | undefined
filter: FilterConfigJson filter: FilterConfigJson
}[] }[]
>, >,
layout?: LayoutConfigJson | undefined, theme?: ThemeConfigJson | undefined,
): void { ): void {
if (layer.filter === undefined || layer.filter === null) { if (layer.filter === undefined || layer.filter === null) {
return return
@ -1009,14 +1009,14 @@ export class DetectDuplicateFilters extends DesugaringStep<{
perOsmTag.get(key).push({ perOsmTag.get(key).push({
layer, layer,
filter, filter,
layout, theme,
}) })
} }
} }
} }
} }
export class DetectDuplicatePresets extends DesugaringStep<LayoutConfig> { export class DetectDuplicatePresets extends DesugaringStep<ThemeConfig> {
constructor() { constructor() {
super( super(
"Detects mappings which have identical (english) names or identical mappings.", "Detects mappings which have identical (english) names or identical mappings.",
@ -1025,7 +1025,7 @@ export class DetectDuplicatePresets extends DesugaringStep<LayoutConfig> {
) )
} }
convert(json: LayoutConfig, context: ConversionContext): LayoutConfig { convert(json: ThemeConfig, context: ConversionContext): ThemeConfig {
const presets: PresetConfig[] = [].concat(...json.layers.map((l) => l.presets)) const presets: PresetConfig[] = [].concat(...json.layers.map((l) => l.presets))
const enNames = presets.map((p) => p.title.textFor("en")) const enNames = presets.map((p) => p.title.textFor("en"))
@ -1074,7 +1074,7 @@ export class DetectDuplicatePresets extends DesugaringStep<LayoutConfig> {
} }
export class ValidateThemeEnsemble extends Conversion< export class ValidateThemeEnsemble extends Conversion<
LayoutConfig[], ThemeConfig[],
Map< Map<
string, string,
{ {
@ -1093,7 +1093,7 @@ export class ValidateThemeEnsemble extends Conversion<
} }
convert( convert(
json: LayoutConfig[], json: ThemeConfig[],
context: ConversionContext, context: ConversionContext,
): Map< ): Map<
string, string,

View file

@ -62,7 +62,7 @@ export default class FilterConfig {
const fields: { name: string; type: ValidatorType }[] = (option.fields ?? []).map((f, i) => { const fields: { name: string; type: ValidatorType }[] = (option.fields ?? []).map((f, i) => {
const type = <ValidatorType> f.type ?? "regex" const type = <ValidatorType> f.type ?? "regex"
if(Validators.availableTypes.indexOf(type) < 0){ if(Validators.availableTypes.indexOf(type) < 0){
throw `Invalid filter: type is not a valid validator. Did you mean one of ${Utils.sortedByLevenshteinDistance(type, <any>Validators.availableTypes, x => x).slice(0, 3)}` throw `Invalid filter: type is not a valid validator. Did you mean one of ${Utils.sortedByLevenshteinDistance(type, <ReadonlyArray<string>>Validators.availableTypes, x => x).slice(0, 3)}`
} }
// Type is validated against 'ValidatedTextField' in Validation.ts, in ValidateFilterConfig // Type is validated against 'ValidatedTextField' in Validation.ts, in ValidateFilterConfig
if (f.name === undefined || f.name === "" || f.name.match(/[a-z0-9_-]+/) == null) { if (f.name === undefined || f.name === "" || f.name.match(/[a-z0-9_-]+/) == null) {

View file

@ -16,7 +16,7 @@ import { Translatable } from "./Translatable"
* *
* General remark: a type (string | any) indicates either a fixed or a translatable string. * General remark: a type (string | any) indicates either a fixed or a translatable string.
*/ */
export interface LayoutConfigJson { export interface ThemeConfigJson {
/** /**
* question: What is the id of this layout? * question: What is the id of this layout?
* *

View file

@ -1,5 +1,5 @@
import { Translation } from "../../UI/i18n/Translation" import { Translation } from "../../UI/i18n/Translation"
import { LayoutConfigJson } from "./Json/LayoutConfigJson" import { ThemeConfigJson } from "./Json/ThemeConfigJson"
import LayerConfig from "./LayerConfig" import LayerConfig from "./LayerConfig"
import { LayerConfigJson } from "./Json/LayerConfigJson" import { LayerConfigJson } from "./Json/LayerConfigJson"
import Constants from "../Constants" import Constants from "../Constants"
@ -13,7 +13,7 @@ import { Translatable } from "./Json/Translatable"
/** /**
* Minimal information about a theme * Minimal information about a theme
**/ **/
export class MinimalLayoutInformation { export class MinimalThemeInformation {
id: string id: string
icon: string icon: string
title: Translatable title: Translatable
@ -27,7 +27,7 @@ export class MinimalLayoutInformation {
/** /**
* Minimal information about a theme * Minimal information about a theme
**/ **/
export class LayoutInformation { export class ThemeInformation {
id: string id: string
icon: string icon: string
title: Translatable | Translation title: Translatable | Translation
@ -39,7 +39,7 @@ export class LayoutInformation {
} }
export default class LayoutConfig implements LayoutInformation { export default class ThemeConfig implements ThemeInformation {
public static readonly defaultSocialImage = "assets/SocialImage.png" public static readonly defaultSocialImage = "assets/SocialImage.png"
public readonly id: string public readonly id: string
public readonly credits?: string public readonly credits?: string
@ -57,7 +57,6 @@ export default class LayoutConfig implements LayoutInformation {
public readonly startZoom: number public readonly startZoom: number
public readonly startLat: number public readonly startLat: number
public readonly startLon: number public readonly startLon: number
public widenFactor: number
public defaultBackgroundId?: string public defaultBackgroundId?: string
public layers: LayerConfig[] public layers: LayerConfig[]
public tileLayerSources: (RasterLayerProperties & { defaultState?: true | boolean })[] public tileLayerSources: (RasterLayerProperties & { defaultState?: true | boolean })[]
@ -92,11 +91,11 @@ export default class LayoutConfig implements LayoutInformation {
public readonly definitionRaw?: string public readonly definitionRaw?: string
private readonly layersDict: Map<string, LayerConfig> private readonly layersDict: Map<string, LayerConfig>
private readonly source: LayoutConfigJson private readonly source: ThemeConfigJson
public readonly enableCache: boolean public readonly enableCache: boolean
constructor( constructor(
json: LayoutConfigJson, json: ThemeConfigJson,
official = true, official = true,
options?: { options?: {
definedAtUrl?: string definedAtUrl?: string
@ -167,7 +166,7 @@ export default class LayoutConfig implements LayoutInformation {
? undefined ? undefined
: new Translation(json.descriptionTail, "themes:" + context + ".descriptionTail") : new Translation(json.descriptionTail, "themes:" + context + ".descriptionTail")
this.icon = json.icon this.icon = json.icon
this.socialImage = json.socialImage ?? LayoutConfig.defaultSocialImage this.socialImage = json.socialImage ?? ThemeConfig.defaultSocialImage
if (this.socialImage === "") { if (this.socialImage === "") {
if (official) { if (official) {
throw "Theme " + json.id + " has empty string as social image" throw "Theme " + json.id + " has empty string as social image"
@ -176,7 +175,6 @@ export default class LayoutConfig implements LayoutInformation {
this.startZoom = json.startZoom this.startZoom = json.startZoom
this.startLat = json.startLat this.startLat = json.startLat
this.startLon = json.startLon this.startLon = json.startLon
this.widenFactor = 1.5
this.defaultBackgroundId = json.defaultBackgroundId this.defaultBackgroundId = json.defaultBackgroundId
this.tileLayerSources = json.tileLayerSources ?? [] this.tileLayerSources = json.tileLayerSources ?? []

View file

@ -1,4 +1,4 @@
import LayoutConfig from "./ThemeConfig/LayoutConfig" import ThemeConfig from "./ThemeConfig/ThemeConfig"
import { SpecialVisualizationState } from "../UI/SpecialVisualization" import { SpecialVisualizationState } from "../UI/SpecialVisualization"
import { Changes } from "../Logic/Osm/Changes" import { Changes } from "../Logic/Osm/Changes"
import { Store, UIEventSource } from "../Logic/UIEventSource" import { Store, UIEventSource } from "../Logic/UIEventSource"
@ -18,7 +18,7 @@ import UserRelatedState from "../Logic/State/UserRelatedState"
import LayerConfig from "./ThemeConfig/LayerConfig" import LayerConfig from "./ThemeConfig/LayerConfig"
import GeoLocationHandler from "../Logic/Actors/GeoLocationHandler" import GeoLocationHandler from "../Logic/Actors/GeoLocationHandler"
import { AvailableRasterLayers, RasterLayerPolygon, RasterLayerUtils } from "./RasterLayers" import { AvailableRasterLayers, RasterLayerPolygon, RasterLayerUtils } from "./RasterLayers"
import LayoutSource from "../Logic/FeatureSource/Sources/LayoutSource" import ThemeSource from "../Logic/FeatureSource/Sources/ThemeSource"
import StaticFeatureSource from "../Logic/FeatureSource/Sources/StaticFeatureSource" import StaticFeatureSource from "../Logic/FeatureSource/Sources/StaticFeatureSource"
import FeaturePropertiesStore from "../Logic/FeatureSource/Actors/FeaturePropertiesStore" import FeaturePropertiesStore from "../Logic/FeatureSource/Actors/FeaturePropertiesStore"
import PerLayerFeatureSourceSplitter from "../Logic/FeatureSource/PerLayerFeatureSourceSplitter" import PerLayerFeatureSourceSplitter from "../Logic/FeatureSource/PerLayerFeatureSourceSplitter"
@ -80,7 +80,7 @@ import { PanoramaxUploader } from "../Logic/ImageProviders/Panoramax"
* It ties up all the needed elements and starts some actors. * It ties up all the needed elements and starts some actors.
*/ */
export default class ThemeViewState implements SpecialVisualizationState { export default class ThemeViewState implements SpecialVisualizationState {
readonly layout: LayoutConfig readonly theme: ThemeConfig
readonly map: UIEventSource<MlMap> readonly map: UIEventSource<MlMap>
readonly changes: Changes readonly changes: Changes
readonly featureSwitches: FeatureSwitchState readonly featureSwitches: FeatureSwitchState
@ -104,7 +104,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
readonly fullNodeDatabase?: FullNodeDatabaseSource readonly fullNodeDatabase?: FullNodeDatabaseSource
readonly historicalUserLocations: WritableFeatureSource<Feature<Point>> readonly historicalUserLocations: WritableFeatureSource<Feature<Point>>
readonly indexedFeatures: IndexedFeatureSource & LayoutSource readonly indexedFeatures: IndexedFeatureSource & ThemeSource
readonly currentView: FeatureSource<Feature<Polygon>> readonly currentView: FeatureSource<Feature<Polygon>>
readonly featuresInView: FeatureSource readonly featuresInView: FeatureSource
readonly favourites: FavouritesFeatureSource readonly favourites: FavouritesFeatureSource
@ -160,9 +160,9 @@ export default class ThemeViewState implements SpecialVisualizationState {
*/ */
public readonly featureSummary: SummaryTileSourceRewriter public readonly featureSummary: SummaryTileSourceRewriter
constructor(layout: LayoutConfig, mvtAvailableLayers: Set<string>) { constructor(layout: ThemeConfig, mvtAvailableLayers: Set<string>) {
Utils.initDomPurify() Utils.initDomPurify()
this.layout = layout this.theme = layout
this.featureSwitches = new FeatureSwitchState(layout) this.featureSwitches = new FeatureSwitchState(layout)
this.guistate = new MenuState( this.guistate = new MenuState(
this.featureSwitches.featureSwitchWelcomeMessage.data, this.featureSwitches.featureSwitchWelcomeMessage.data,
@ -218,7 +218,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
{ {
const overlayLayerStates = new Map<string, { isDisplayed: UIEventSource<boolean> }>() const overlayLayerStates = new Map<string, { isDisplayed: UIEventSource<boolean> }>()
for (const rasterInfo of this.layout.tileLayerSources) { for (const rasterInfo of this.theme.tileLayerSources) {
const isDisplayed = QueryParameters.GetBooleanQueryParameter( const isDisplayed = QueryParameters.GetBooleanQueryParameter(
"overlay-" + rasterInfo.id, "overlay-" + rasterInfo.id,
rasterInfo.defaultState ?? true, rasterInfo.defaultState ?? true,
@ -236,11 +236,11 @@ export default class ThemeViewState implements SpecialVisualizationState {
* A bit tricky, as this is heavily intertwined with the 'changes'-element, which generates a stream of new and changed features too * A bit tricky, as this is heavily intertwined with the 'changes'-element, which generates a stream of new and changed features too
*/ */
if (this.layout.layers.some((l) => l._needsFullNodeDatabase)) { if (this.theme.layers.some((l) => l._needsFullNodeDatabase)) {
this.fullNodeDatabase = new FullNodeDatabaseSource() this.fullNodeDatabase = new FullNodeDatabaseSource()
} }
const layoutSource = new LayoutSource( const layoutSource = new ThemeSource(
layout.layers, layout.layers,
this.featureSwitches, this.featureSwitches,
this.mapProperties, this.mapProperties,
@ -340,7 +340,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
}) })
this.lastClickObject = new LastClickFeatureSource( this.lastClickObject = new LastClickFeatureSource(
this.layout, this.theme,
this.mapProperties.lastClickLocation, this.mapProperties.lastClickLocation,
this.userRelatedState.addNewFeatureMode, this.userRelatedState.addNewFeatureMode,
) )
@ -414,7 +414,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
const storage = new SaveFeatureSourceToLocalStorage( const storage = new SaveFeatureSourceToLocalStorage(
this.osmConnection.Backend(), this.osmConnection.Backend(),
fs.layer.layerDef.id, fs.layer.layerDef.id,
LayoutSource.fromCacheZoomLevel, ThemeSource.fromCacheZoomLevel,
fs, fs,
this.featureProperties, this.featureProperties,
fs.layer.layerDef.maxAgeOfCache, fs.layer.layerDef.maxAgeOfCache,
@ -509,7 +509,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
} }
}) })
this.userRelatedState.markLayoutAsVisited(this.layout) this.userRelatedState.markLayoutAsVisited(this.theme)
this.selectedElement.addCallback((selected) => { this.selectedElement.addCallback((selected) => {
if (selected === undefined) { if (selected === undefined) {
@ -517,8 +517,8 @@ export default class ThemeViewState implements SpecialVisualizationState {
} }
}) })
if (this.layout.customCss !== undefined && window.location.pathname.indexOf("theme") >= 0) { if (this.theme.customCss !== undefined && window.location.pathname.indexOf("theme") >= 0) {
Utils.LoadCustomCss(this.layout.customCss) Utils.LoadCustomCss(this.theme.customCss)
} }
Hash.hash.addCallbackAndRunD((hash) => { Hash.hash.addCallbackAndRunD((hash) => {
@ -738,11 +738,11 @@ export default class ThemeViewState implements SpecialVisualizationState {
/** /**
* MaxZoom for the summary layer * MaxZoom for the summary layer
*/ */
const normalLayers = this.layout.layers.filter(l => l.isNormal()) const normalLayers = this.theme.layers.filter(l => l.isNormal())
const maxzoom = Math.min(...normalLayers.map((l) => l.minzoom)) const maxzoom = Math.min(...normalLayers.map((l) => l.minzoom))
const layers = this.layout.layers.filter( const layers = this.theme.layers.filter(
(l) => (l) =>
Constants.priviliged_layers.indexOf(<any>l.id) < 0 && Constants.priviliged_layers.indexOf(<any>l.id) < 0 &&
l.source.geojsonSource === undefined && l.source.geojsonSource === undefined &&
@ -796,8 +796,8 @@ export default class ThemeViewState implements SpecialVisualizationState {
this.closestFeatures.registerSource(specialLayers.favourite, "favourite") this.closestFeatures.registerSource(specialLayers.favourite, "favourite")
if (this.layout?.lockLocation) { if (this.theme?.lockLocation) {
const bbox = new BBox(<any>this.layout.lockLocation) const bbox = new BBox(<any>this.theme.lockLocation)
this.mapProperties.maxbounds.setData(bbox) this.mapProperties.maxbounds.setData(bbox)
ShowDataLayer.showRange( ShowDataLayer.showRange(
this.map, this.map,
@ -805,7 +805,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
this.featureSwitches.featureSwitchIsTesting, this.featureSwitches.featureSwitchIsTesting,
) )
} }
const currentViewLayer = this.layout.layers.find((l) => l.id === "current_view") const currentViewLayer = this.theme.layers.find((l) => l.id === "current_view")
if (currentViewLayer?.tagRenderings?.length > 0) { if (currentViewLayer?.tagRenderings?.length > 0) {
const params = MetaTagging.createExtraFuncParams(this) const params = MetaTagging.createExtraFuncParams(this)
this.featureProperties.trackFeatureSource(specialLayers.current_view) this.featureProperties.trackFeatureSource(specialLayers.current_view)
@ -814,7 +814,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
features, features,
params, params,
currentViewLayer, currentViewLayer,
this.layout, this.theme,
this.osmObjectDownloader, this.osmObjectDownloader,
this.featureProperties, this.featureProperties,
) )
@ -909,9 +909,9 @@ export default class ThemeViewState implements SpecialVisualizationState {
*/ */
private initActors() { private initActors() {
if (!this.layout.official) { if (!this.theme.official) {
// Add custom themes to the "visited custom themes" // Add custom themes to the "visited custom themes"
const th = this.layout const th = this.theme
this.userRelatedState.addUnofficialTheme({ this.userRelatedState.addUnofficialTheme({
id: th.id, id: th.id,
icon: th.icon, icon: th.icon,
@ -945,7 +945,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
this.selectedElement.addCallbackD(selected => { this.selectedElement.addCallbackD(selected => {
const [osm_type, osm_id] = selected.properties.id.split("/") const [osm_type, osm_id] = selected.properties.id.split("/")
const [lon, lat] = GeoOperations.centerpointCoordinates(selected) const [lon, lat] = GeoOperations.centerpointCoordinates(selected)
const layer = this.layout.getMatchingLayer(selected.properties) const layer = this.theme.getMatchingLayer(selected.properties)
const nameOptions = [ const nameOptions = [
selected?.properties?.name, selected?.properties?.name,
@ -987,7 +987,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
} }
/** /**
* Searches the appropriate layer - will first try if a special layer matches; if not, a normal layer will be used by delegating to the layout * Searches the appropriate layer - will first try if a special layer matches; if not, a normal layer will be used by delegating to the theme
*/ */
public getMatchingLayer(properties: Record<string, string>) { public getMatchingLayer(properties: Record<string, string>) {
@ -1002,15 +1002,15 @@ export default class ThemeViewState implements SpecialVisualizationState {
return UserRelatedState.usersettingsConfig return UserRelatedState.usersettingsConfig
} }
if (id.startsWith(LastClickFeatureSource.newPointElementId)) { if (id.startsWith(LastClickFeatureSource.newPointElementId)) {
return this.layout.layers.find((l) => l.id === "last_click") return this.theme.layers.find((l) => l.id === "last_click")
} }
if (id.startsWith("search_result")) { if (id.startsWith("search_result")) {
return GeocodingUtils.searchLayer return GeocodingUtils.searchLayer
} }
if (id === "location_track") { if (id === "location_track") {
return this.layout.layers.find((l) => l.id === "gps_track") return this.theme.layers.find((l) => l.id === "gps_track")
} }
return this.layout.getMatchingLayer(properties) return this.theme.getMatchingLayer(properties)
} }
public async reportError(message: string | Error | XMLHttpRequest, extramessage: string = "") { public async reportError(message: string | Error | XMLHttpRequest, extramessage: string = "") {
@ -1059,7 +1059,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
body: JSON.stringify({ body: JSON.stringify({
stacktrace, stacktrace,
message: "" + message, message: "" + message,
layout: this.layout.id, theme: this.theme.id,
version: Constants.vNumber, version: Constants.vNumber,
language: this.userRelatedState.language.data, language: this.userRelatedState.language.data,
username: this.osmConnection.userDetails.data?.name, username: this.osmConnection.userDetails.data?.name,

View file

@ -12,7 +12,7 @@
import Constants from "../Models/Constants" import Constants from "../Models/Constants"
import { ImmutableStore, Store, Stores, UIEventSource } from "../Logic/UIEventSource" import { ImmutableStore, Store, Stores, UIEventSource } from "../Logic/UIEventSource"
import ThemesList from "./BigComponents/ThemesList.svelte" import ThemesList from "./BigComponents/ThemesList.svelte"
import { MinimalLayoutInformation } from "../Models/ThemeConfig/LayoutConfig" import { MinimalThemeInformation } from "../Models/ThemeConfig/ThemeConfig"
import Eye from "../assets/svg/Eye.svelte" import Eye from "../assets/svg/Eye.svelte"
import LoginButton from "./Base/LoginButton.svelte" import LoginButton from "./Base/LoginButton.svelte"
import Mastodon from "../assets/svg/Mastodon.svelte" import Mastodon from "../assets/svg/Mastodon.svelte"
@ -46,16 +46,16 @@
let searchIsFocused = new UIEventSource(true) let searchIsFocused = new UIEventSource(true)
const officialThemes: MinimalLayoutInformation[] = ThemeSearch.officialThemes.themes.filter(th => th.hideFromOverview === false) const officialThemes: MinimalThemeInformation[] = ThemeSearch.officialThemes.themes.filter(th => th.hideFromOverview === false)
const hiddenThemes: MinimalLayoutInformation[] = ThemeSearch.officialThemes.themes.filter(th => th.hideFromOverview === true) const hiddenThemes: MinimalThemeInformation[] = ThemeSearch.officialThemes.themes.filter(th => th.hideFromOverview === true)
let visitedHiddenThemes: Store<MinimalLayoutInformation[]> = UserRelatedState.initDiscoveredHiddenThemes(state.osmConnection) let visitedHiddenThemes: Store<MinimalThemeInformation[]> = UserRelatedState.initDiscoveredHiddenThemes(state.osmConnection)
.map((knownIds) => hiddenThemes.filter((theme) => .map((knownIds) => hiddenThemes.filter((theme) =>
knownIds.indexOf(theme.id) >= 0 || state.osmConnection.userDetails.data.name === "Pieter Vander Vennet" knownIds.indexOf(theme.id) >= 0 || state.osmConnection.userDetails.data.name === "Pieter Vander Vennet"
)) ))
const customThemes: Store<MinimalLayoutInformation[]> = Stores.ListStabilized<string>(state.installedUserThemes) const customThemes: Store<MinimalThemeInformation[]> = Stores.ListStabilized<string>(state.installedUserThemes)
.mapD(stableIds => Utils.NoNullInplace(stableIds.map(id => state.getUnofficialTheme(id)))) .mapD(stableIds => Utils.NoNullInplace(stableIds.map(id => state.getUnofficialTheme(id))))
function filtered(themes: Store<MinimalLayoutInformation[]>): Store<MinimalLayoutInformation[]> { function filtered(themes: Store<MinimalThemeInformation[]>): Store<MinimalThemeInformation[]> {
return searchStable.map(search => { return searchStable.map(search => {
if (!search) { if (!search) {
return themes.data return themes.data
@ -74,9 +74,9 @@
} }
let officialSearched : Store<MinimalLayoutInformation[]>= filtered(new ImmutableStore(officialThemes)) let officialSearched : Store<MinimalThemeInformation[]>= filtered(new ImmutableStore(officialThemes))
let hiddenSearched: Store<MinimalLayoutInformation[]> = filtered(visitedHiddenThemes) let hiddenSearched: Store<MinimalThemeInformation[]> = filtered(visitedHiddenThemes)
let customSearched: Store<MinimalLayoutInformation[]> = filtered(customThemes) let customSearched: Store<MinimalThemeInformation[]> = filtered(customThemes)
let searchIsFocussed = new UIEventSource(false) let searchIsFocussed = new UIEventSource(false)

View file

@ -6,7 +6,7 @@
export let state: SpecialVisualizationState export let state: SpecialVisualizationState
let layoutToUse = state.layout let layoutToUse = state.theme
let iconAttributions: string[] = layoutToUse.getUsedImages() let iconAttributions: string[] = layoutToUse.getUsedImages()
const allLicenses = {} const allLicenses = {}

View file

@ -18,7 +18,7 @@
export let state: SpecialVisualizationState export let state: SpecialVisualizationState
const t = Translations.t.general.attribution const t = Translations.t.general.attribution
const layoutToUse = state.layout const layoutToUse = state.theme
let maintainer: Translation = undefined let maintainer: Translation = undefined
if (layoutToUse.credits !== undefined && layoutToUse.credits !== "") { if (layoutToUse.credits !== undefined && layoutToUse.credits !== "") {
@ -122,7 +122,7 @@
{/if} {/if}
{#if maintainer !== undefined} {#if maintainer !== undefined}
<div class="flex items-center gap-x-2"> <div class="flex items-center gap-x-2">
<Marker icons={state.layout.icon} size="h-8 w-8 shrink-0" /> <Marker icons={state.theme.icon} size="h-8 w-8 shrink-0" />
<Tr t={maintainer} /> <Tr t={maintainer} />
</div> </div>
{/if} {/if}

View file

@ -9,8 +9,8 @@
import Icon from "../Map/Icon.svelte" import Icon from "../Map/Icon.svelte"
export let state: SpecialVisualizationState export let state: SpecialVisualizationState
let theme = state.layout?.id ?? "" let theme = state.theme?.id ?? ""
let config: ExtraLinkConfig = state.layout.extraLink let config: ExtraLinkConfig = state.theme.extraLink
let basepath = window.location.host let basepath = window.location.host
let showWelcomeMessageSwitch = state.featureSwitches.featureSwitchWelcomeMessage let showWelcomeMessageSwitch = state.featureSwitches.featureSwitchWelcomeMessage
const isIframe = Utils.isIframe const isIframe = Utils.isIframe
@ -42,7 +42,7 @@
{#if config.text} {#if config.text}
<Tr t={config.text} /> <Tr t={config.text} />
{:else} {:else}
<Tr t={t.screenToSmall.Subs({ theme: state.layout.title })} /> <Tr t={t.screenToSmall.Subs({ theme: state.theme.title })} />
{/if} {/if}
</a> </a>
</div> </div>

View file

@ -14,7 +14,7 @@
export let state: ThemeViewState export let state: ThemeViewState
export let onlyLink: boolean export let onlyLink: boolean
let layout = state.layout let theme = state.theme
let allEnabled: boolean let allEnabled: boolean
let allDisabled: boolean let allDisabled: boolean
@ -70,7 +70,7 @@
</div> </div>
</div> </div>
{#each layout.layers as layer} {#each theme.layers as layer}
<Filterview <Filterview
{state} {state}
zoomlevel={state.mapProperties.zoom} zoomlevel={state.mapProperties.zoom}
@ -79,7 +79,7 @@
/> />
{/each} {/each}
{#each layout.tileLayerSources as tilesource} {#each theme.tileLayerSources as tilesource}
<OverlayToggle <OverlayToggle
layerproperties={tilesource} layerproperties={tilesource}
state={state.overlayLayerStates.get(tilesource.id)} state={state.overlayLayerStates.get(tilesource.id)}

View file

@ -56,7 +56,7 @@
let usersettingslayer = new LayerConfig(<LayerConfigJson>usersettings, "usersettings", true) let usersettingslayer = new LayerConfig(<LayerConfigJson>usersettings, "usersettings", true)
let layout = state.layout let theme = state.theme
let featureSwitches = state.featureSwitches let featureSwitches = state.featureSwitches
let showHome = featureSwitches.featureSwitchBackToThemeOverview let showHome = featureSwitches.featureSwitchBackToThemeOverview
let pg = state.guistate.pageStates let pg = state.guistate.pageStates
@ -108,7 +108,7 @@
<!-- All shown components are set by 'usersettings.json', which happily uses some special visualisations created specifically for it --> <!-- All shown components are set by 'usersettings.json', which happily uses some special visualisations created specifically for it -->
<LoginToggle {state}> <LoginToggle {state}>
<div class="flex flex-col" slot="not-logged-in"> <div class="flex flex-col" slot="not-logged-in">
<LanguagePicker availableLanguages={layout.language} /> <LanguagePicker availableLanguages={theme.language} />
<Tr cls="alert" t={Translations.t.userinfo.notLoggedIn} /> <Tr cls="alert" t={Translations.t.userinfo.notLoggedIn} />
<LoginButton clss="primary" osmConnection={state.osmConnection} /> <LoginButton clss="primary" osmConnection={state.osmConnection} />
</div> </div>
@ -161,12 +161,12 @@
<Page {onlyLink} shown={pg.about_theme}> <Page {onlyLink} shown={pg.about_theme}>
<svelte:fragment slot="link"> <svelte:fragment slot="link">
<Marker size="h-7 w-7" icons={layout.icon} /> <Marker size="h-7 w-7" icons={theme.icon} />
<Tr t={t.showIntroduction} /> <Tr t={t.showIntroduction} />
</svelte:fragment> </svelte:fragment>
<svelte:fragment slot="header"> <svelte:fragment slot="header">
<Marker size="h-8 w-8 mr-3" icons={layout.icon} /> <Marker size="h-8 w-8 mr-3" icons={theme.icon} />
<Tr t={layout.title} /> <Tr t={theme.title} />
</svelte:fragment> </svelte:fragment>
<ThemeIntroPanel {state} /> <ThemeIntroPanel {state} />
</Page> </Page>
@ -193,25 +193,25 @@
</Page> </Page>
{/if} {/if}
{#if layout.official} {#if theme.official}
<a <a
class="flex" class="flex"
href={"https://github.com/pietervdvn/MapComplete/blob/develop/Docs/Themes/" + href={"https://github.com/pietervdvn/MapComplete/blob/develop/Docs/Themes/" +
layout.id + theme.id +
".md"} ".md"}
target="_blank" target="_blank"
> >
<DocumentMagnifyingGlass class="h-6 w-6" /> <DocumentMagnifyingGlass class="h-6 w-6" />
<Tr <Tr
t={Translations.t.general.attribution.openThemeDocumentation.Subs({ t={Translations.t.general.attribution.openThemeDocumentation.Subs({
name: layout.title, name: theme.title,
})} })}
/> />
</a> </a>
<a class="flex" href={Utils.OsmChaLinkFor(31, layout.id)} target="_blank"> <a class="flex" href={Utils.OsmChaLinkFor(31, theme.id)} target="_blank">
<DocumentChartBar class="h-6 w-6" /> <DocumentChartBar class="h-6 w-6" />
<Tr t={Translations.t.general.attribution.openOsmcha.Subs({ theme: layout.title })} /> <Tr t={Translations.t.general.attribution.openOsmcha.Subs({ theme: theme.title })} />
</a> </a>
{/if} {/if}
</SidebarUnit> </SidebarUnit>

View file

@ -25,8 +25,8 @@
* In some cases (local deploys, custom themes), we need to set the URL to `/theme.html?layout=xyz` instead of `/xyz?...` * In some cases (local deploys, custom themes), we need to set the URL to `/theme.html?layout=xyz` instead of `/xyz?...`
* Note that the 'layout='-param will be included automatically * Note that the 'layout='-param will be included automatically
*/ */
let needsThemeRedirect = url.port !== "" || url.hostname.match(/^[0-9]/) || !state.layout.official let needsThemeRedirect = url.port !== "" || url.hostname.match(/^[0-9]/) || !state.theme.official
let layoutId = state.layout.id let layoutId = state.theme.id
let baseLink = `${url.protocol}//${url.host}/${needsThemeRedirect ? "theme.html" : layoutId}?` let baseLink = `${url.protocol}//${url.host}/${needsThemeRedirect ? "theme.html" : layoutId}?`
let showWelcomeMessage = true let showWelcomeMessage = true
@ -44,7 +44,7 @@
enableBackground: boolean, enableBackground: boolean,
enableGeolocation: boolean enableGeolocation: boolean
) { ) {
const layout = state.layout const layout = state.theme
let excluded = Utils.NoNull([ let excluded = Utils.NoNull([
showWelcomeMessage ? undefined : "fs-welcome-message", showWelcomeMessage ? undefined : "fs-welcome-message",
enableLogin ? undefined : "fs-enable-login", enableLogin ? undefined : "fs-enable-login",
@ -99,7 +99,7 @@
${ ${
enableGeolocation ? 'allow="geolocation"' : "" enableGeolocation ? 'allow="geolocation"' : ""
} width="100%" height="100%" style="min-width: 250px; min-height: 250px" } width="100%" height="100%" style="min-width: 250px; min-height: 250px"
title="${state.layout.title?.txt ?? "MapComplete"} with MapComplete"> title="${state.theme.title?.txt ?? "MapComplete"} with MapComplete">
</iframe>` </iframe>`
Array.from(state.layerState.filteredLayers.values()).forEach((flayer) => Array.from(state.layerState.filteredLayers.values()).forEach((flayer) =>
@ -163,7 +163,7 @@
<Tr t={tr.stateIsIncluded} /> <Tr t={tr.stateIsIncluded} />
<a <a
class="inline-block w-fit cursor-pointer" class="inline-block w-fit cursor-pointer"
on:click={() => state.guistate.filtersPanelIsOpened.set(true)} on:click={() => state.guistate.pageStates.filter.set(true)}
> >
<Tr t={tr.openLayers} /> <Tr t={tr.openLayers} />
</a> </a>

View file

@ -10,7 +10,7 @@
export let i: number = undefined export let i: number = undefined
let id = feature.properties.id let id = feature.properties.id
let tags = state.featureProperties.getStore(id) let tags = state.featureProperties.getStore(id)
let layer: LayerConfig = state.layout.getMatchingLayer(tags.data) let layer: LayerConfig = state.theme.getMatchingLayer(tags.data)
</script> </script>
<span class="inline-flex gap-x-1"> <span class="inline-flex gap-x-1">

View file

@ -1,12 +1,12 @@
<script lang="ts"> <script lang="ts">
import { ImmutableStore, Store } from "../../Logic/UIEventSource" import { ImmutableStore, Store } from "../../Logic/UIEventSource"
import { OsmConnection } from "../../Logic/Osm/OsmConnection" import { OsmConnection } from "../../Logic/Osm/OsmConnection"
import type { MinimalLayoutInformation } from "../../Models/ThemeConfig/LayoutConfig" import type { MinimalThemeInformation } from "../../Models/ThemeConfig/ThemeConfig"
import Tr from "../Base/Tr.svelte" import Tr from "../Base/Tr.svelte"
import Translations from "../i18n/Translations" import Translations from "../i18n/Translations"
import Marker from "../Map/Marker.svelte" import Marker from "../Map/Marker.svelte"
export let theme: MinimalLayoutInformation & {isOfficial?: boolean} export let theme: MinimalThemeInformation & {isOfficial?: boolean}
let isCustom: boolean = theme.id.startsWith("https://") || theme.id.startsWith("http://") let isCustom: boolean = theme.id.startsWith("https://") || theme.id.startsWith("http://")
export let state: { layoutToUse?: { id: string }; osmConnection: OsmConnection } export let state: { layoutToUse?: { id: string }; osmConnection: OsmConnection }

View file

@ -14,7 +14,7 @@
* The theme introduction panel * The theme introduction panel
*/ */
export let state: ThemeViewState export let state: ThemeViewState
let layout = state.layout let theme = state.theme
let geolocation = state.geolocation.geolocationState let geolocation = state.geolocation.geolocationState
let geopermission: Store<GeolocationPermissionState> = geolocation.permission let geopermission: Store<GeolocationPermissionState> = geolocation.permission
@ -42,16 +42,16 @@
<div> <div>
<!-- Intro, description, ... --> <!-- Intro, description, ... -->
<Tr t={layout.description} /> <Tr t={theme.description} />
<If condition={state.featureSwitches.featureSwitchEnableLogin}> <If condition={state.featureSwitches.featureSwitchEnableLogin}>
<Tr t={Translations.t.general.welcomeExplanation.general} /> <Tr t={Translations.t.general.welcomeExplanation.general} />
{#if layout.layers.some((l) => l.presets?.length > 0)} {#if theme.layers.some((l) => l.presets?.length > 0)}
<Tr t={Translations.t.general.welcomeExplanation.addNew} /> <Tr t={Translations.t.general.welcomeExplanation.addNew} />
{/if} {/if}
</If> </If>
<Tr t={layout.descriptionTail} /> <Tr t={theme.descriptionTail} />
<!-- Buttons: open map, go to location, search --> <!-- Buttons: open map, go to location, search -->
<NextButton <NextButton

View file

@ -4,12 +4,12 @@
import { OsmConnection } from "../../Logic/Osm/OsmConnection" import { OsmConnection } from "../../Logic/Osm/OsmConnection"
import { UIEventSource } from "../../Logic/UIEventSource" import { UIEventSource } from "../../Logic/UIEventSource"
import ThemeButton from "./ThemeButton.svelte" import ThemeButton from "./ThemeButton.svelte"
import { MinimalLayoutInformation } from "../../Models/ThemeConfig/LayoutConfig" import { MinimalThemeInformation } from "../../Models/ThemeConfig/ThemeConfig"
import Translations from "../i18n/Translations" import Translations from "../i18n/Translations"
import Tr from "../Base/Tr.svelte" import Tr from "../Base/Tr.svelte"
export let search: UIEventSource<string> export let search: UIEventSource<string>
export let themes: MinimalLayoutInformation[] export let themes: MinimalThemeInformation[]
export let state: { osmConnection: OsmConnection } export let state: { osmConnection: OsmConnection }
export let hasSelection : boolean = true export let hasSelection : boolean = true

View file

@ -3,7 +3,7 @@
import Translations from "../i18n/Translations" import Translations from "../i18n/Translations"
import { Store, UIEventSource } from "../../Logic/UIEventSource" import { Store, UIEventSource } from "../../Logic/UIEventSource"
import { Translation } from "../i18n/Translation" import { Translation } from "../i18n/Translation"
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig" import ThemeConfig from "../../Models/ThemeConfig/ThemeConfig"
import { OsmConnection } from "../../Logic/Osm/OsmConnection" import { OsmConnection } from "../../Logic/Osm/OsmConnection"
import Invalid from "../../assets/svg/Invalid.svelte" import Invalid from "../../assets/svg/Invalid.svelte"
import Tr from "../Base/Tr.svelte" import Tr from "../Base/Tr.svelte"
@ -17,7 +17,7 @@
export let trace: (title: string) => string export let trace: (title: string) => string
export let state: { export let state: {
layout: LayoutConfig layout: ThemeConfig
osmConnection: OsmConnection osmConnection: OsmConnection
readonly featureSwitchUserbadge: Store<boolean> readonly featureSwitchUserbadge: Store<boolean>
} }

View file

@ -37,7 +37,7 @@
new Tag(key, externalProperties[key]), new Tag(key, externalProperties[key]),
tags.data, tags.data,
{ {
theme: state.layout.id, theme: state.theme.id,
changeType: "import", changeType: "import",
} }
) )

View file

@ -42,7 +42,7 @@
currentStep = "applying_all" currentStep = "applying_all"
const tagsToApply = missing.data.map((k) => new Tag(k, externalProperties[k])) const tagsToApply = missing.data.map((k) => new Tag(k, externalProperties[k]))
const change = new ChangeTagAction(tags.data.id, new And(tagsToApply), tags.data, { const change = new ChangeTagAction(tags.data.id, new And(tagsToApply), tags.data, {
theme: state.layout.id, theme: state.theme.id,
changeType: "import", changeType: "import",
}) })
await state.changes.applyChanges(await change.CreateChangeDescriptions()) await state.changes.applyChanges(await change.CreateChangeDescriptions())

View file

@ -32,7 +32,7 @@
const gpsIsDisplayed = gpsLayer.isDisplayed.data const gpsIsDisplayed = gpsLayer.isDisplayed.data
try { try {
gpsLayer.isDisplayed.setData(false) gpsLayer.isDisplayed.setData(false)
const name = state.layout.id const name = state.theme.id
const title = `MapComplete_${name}_export_${new Date() const title = `MapComplete_${name}_export_${new Date()
.toISOString() .toISOString()

View file

@ -156,7 +156,7 @@ export default class DownloadHelper {
private getCleanGeoJsonPerLayer(includeMetaData: boolean): Map<string, Feature[]> { private getCleanGeoJsonPerLayer(includeMetaData: boolean): Map<string, Feature[]> {
const state = this._state const state = this._state
const featuresPerLayer = new Map<string, any[]>() const featuresPerLayer = new Map<string, any[]>()
const neededLayers = state.layout.layers.filter((l) => l.source !== null).map((l) => l.id) const neededLayers = state.theme.layers.filter((l) => l.source !== null).map((l) => l.id)
const bbox = state.mapProperties.bounds.data const bbox = state.mapProperties.bounds.data
for (const neededLayer of neededLayers) { for (const neededLayer of neededLayers) {
@ -186,7 +186,7 @@ export default class DownloadHelper {
createImage(key: string, width: string, height: string): HTMLImageElement { createImage(key: string, width: string, height: string): HTMLImageElement {
const img = document.createElement("img") const img = document.createElement("img")
const sources = { const sources = {
layouticon: this._state.layout.icon, layouticon: this._state.theme.icon,
} }
img.src = sources[key] img.src = sources[key]
if (!img.src) { if (!img.src) {

View file

@ -38,10 +38,10 @@
return downloadHelper.createImage(key, width, height) return downloadHelper.createImage(key, width, height)
}, },
textSubstitutions: <Record<string, string | Translation>>{ textSubstitutions: <Record<string, string | Translation>>{
"layout.title": state.layout.title, "layout.title": state.theme.title,
layoutid: state.layout.id, layoutid: state.theme.id,
title: state.layout.title, title: state.theme.title,
layoutImg: state.layout.icon, layoutImg: state.theme.icon,
version: Constants.vNumber, version: Constants.vNumber,
date: new Date().toISOString().substring(0, 16), date: new Date().toISOString().substring(0, 16),
background: new Translation(bg.properties.name).txt, background: new Translation(bg.properties.name).txt,

View file

@ -6,7 +6,7 @@ import { Tag } from "../../Logic/Tags/Tag"
import ChangeTagAction from "../../Logic/Osm/Actions/ChangeTagAction" import ChangeTagAction from "../../Logic/Osm/Actions/ChangeTagAction"
import { Changes } from "../../Logic/Osm/Changes" import { Changes } from "../../Logic/Osm/Changes"
import { OsmConnection } from "../../Logic/Osm/OsmConnection" import { OsmConnection } from "../../Logic/Osm/OsmConnection"
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig" import ThemeConfig from "../../Models/ThemeConfig/ThemeConfig"
import SvelteUIElement from "../Base/SvelteUIElement" import SvelteUIElement from "../Base/SvelteUIElement"
import Delete_icon from "../../assets/svg/Delete_icon.svelte" import Delete_icon from "../../assets/svg/Delete_icon.svelte"
@ -14,7 +14,7 @@ export default class DeleteImage extends Toggle {
constructor( constructor(
key: string, key: string,
tags: Store<any>, tags: Store<any>,
state: { layout: LayoutConfig; changes?: Changes; osmConnection?: OsmConnection } state: { theme: ThemeConfig; changes?: Changes; osmConnection?: OsmConnection }
) { ) {
const oldValue = tags.data[key] const oldValue = tags.data[key]
const isDeletedBadge = Translations.t.image.isDeleted const isDeletedBadge = Translations.t.image.isDeleted
@ -25,7 +25,7 @@ export default class DeleteImage extends Toggle {
await state?.changes?.applyAction( await state?.changes?.applyAction(
new ChangeTagAction(tags.data.id, new Tag(key, oldValue), tags.data, { new ChangeTagAction(tags.data.id, new Tag(key, oldValue), tags.data, {
changeType: "delete-image", changeType: "delete-image",
theme: state.layout.id, theme: state.theme.id,
}) })
) )
}) })
@ -40,7 +40,7 @@ export default class DeleteImage extends Toggle {
await state?.changes?.applyAction( await state?.changes?.applyAction(
new ChangeTagAction(tags.data.id, new Tag(key, ""), tags.data, { new ChangeTagAction(tags.data.id, new Tag(key, ""), tags.data, {
changeType: "answer", changeType: "answer",
theme: state.layout.id, theme: state.theme.id,
}) })
) )
}) })

View file

@ -7,7 +7,7 @@ import Toggle from "../Input/Toggle"
import ImageProvider, { ProvidedImage } from "../../Logic/ImageProviders/ImageProvider" import ImageProvider, { ProvidedImage } from "../../Logic/ImageProviders/ImageProvider"
import { OsmConnection } from "../../Logic/Osm/OsmConnection" import { OsmConnection } from "../../Logic/Osm/OsmConnection"
import { Changes } from "../../Logic/Osm/Changes" import { Changes } from "../../Logic/Osm/Changes"
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig" import ThemeConfig from "../../Models/ThemeConfig/ThemeConfig"
import SvelteUIElement from "../Base/SvelteUIElement" import SvelteUIElement from "../Base/SvelteUIElement"
import AttributedImage from "./AttributedImage.svelte" import AttributedImage from "./AttributedImage.svelte"
@ -18,7 +18,7 @@ export class ImageCarousel extends Toggle {
state: { state: {
osmConnection?: OsmConnection osmConnection?: OsmConnection
changes?: Changes changes?: Changes
layout: LayoutConfig theme: ThemeConfig
previewedImage?: UIEventSource<ProvidedImage> previewedImage?: UIEventSource<ProvidedImage>
} }
) { ) {

View file

@ -45,7 +45,7 @@
const url = targetValue const url = targetValue
if (isLinked) { if (isLinked) {
const action = new LinkImageAction(currentTags.id, key, url, tags, { const action = new LinkImageAction(currentTags.id, key, url, tags, {
theme: tags.data._orig_theme ?? state.layout.id, theme: tags.data._orig_theme ?? state.theme.id,
changeType: "link-image" changeType: "link-image"
}) })
await state.changes.applyAction(action) await state.changes.applyAction(action)
@ -54,7 +54,7 @@
const v = currentTags[k] const v = currentTags[k]
if (v === url) { if (v === url) {
const action = new ChangeTagAction(currentTags.id, new Tag(k, ""), currentTags, { const action = new ChangeTagAction(currentTags.id, new Tag(k, ""), currentTags, {
theme: tags.data._orig_theme ?? state.layout.id, theme: tags.data._orig_theme ?? state.theme.id,
changeType: "remove-image" changeType: "remove-image"
}) })
state.changes.applyAction(action) state.changes.applyAction(action)

View file

@ -119,7 +119,7 @@
ShowDataLayer.showMultipleLayers( ShowDataLayer.showMultipleLayers(
map, map,
new StaticFeatureSource([feature]), new StaticFeatureSource([feature]),
state.layout.layers, state.theme.layers,
) )
onDestroy( onDestroy(

View file

@ -39,7 +39,7 @@
const maproulette_id = tags.data[maproulette_id_key] ?? tags.data.mr_taskId ?? tags.data.id const maproulette_id = tags.data[maproulette_id_key] ?? tags.data.mr_taskId ?? tags.data.id
try { try {
await Maproulette.singleton.closeTask(Number(maproulette_id), Number(statusToSet), { await Maproulette.singleton.closeTask(Number(maproulette_id), Number(statusToSet), {
tags: `MapComplete MapComplete:${state.layout.id}`, tags: `MapComplete MapComplete:${state.theme.id}`,
comment: feedback, comment: feedback,
}) })
tags.data["mr_taskStatus"] = Maproulette.STATUS_MEANING[Number(statusToSet)] tags.data["mr_taskStatus"] = Maproulette.STATUS_MEANING[Number(statusToSet)]

View file

@ -114,7 +114,7 @@
} }
const newElementAction = new CreateNewNodeAction(tags, location.lat, location.lon, { const newElementAction = new CreateNewNodeAction(tags, location.lat, location.lon, {
theme: state.layout?.id ?? "unkown", theme: state.theme?.id ?? "unkown",
changeType: "create", changeType: "create",
snapOnto: snapToWay, snapOnto: snapToWay,
reusePointWithinMeters: 1, reusePointWithinMeters: 1,

View file

@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import LayoutConfig from "../../../Models/ThemeConfig/LayoutConfig" import ThemeConfig from "../../../Models/ThemeConfig/ThemeConfig"
import { createEventDispatcher } from "svelte" import { createEventDispatcher } from "svelte"
import type PresetConfig from "../../../Models/ThemeConfig/PresetConfig" import type PresetConfig from "../../../Models/ThemeConfig/PresetConfig"
import Tr from "../../Base/Tr.svelte" import Tr from "../../Base/Tr.svelte"
@ -19,7 +19,7 @@
* This component lists all the presets and allows the user to select one * This component lists all the presets and allows the user to select one
*/ */
export let state: SpecialVisualizationState export let state: SpecialVisualizationState
let layout: LayoutConfig = state.layout let layout: ThemeConfig = state.theme
let presets: { let presets: {
preset: PresetConfig preset: PresetConfig
layer: LayerConfig layer: LayerConfig

View file

@ -10,7 +10,7 @@ import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeature
import { VariableUiElement } from "../Base/VariableUIElement" import { VariableUiElement } from "../Base/VariableUIElement"
import Loading from "../Base/Loading" import Loading from "../Base/Loading"
import Translations from "../i18n/Translations" import Translations from "../i18n/Translations"
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig" import ThemeConfig from "../../Models/ThemeConfig/ThemeConfig"
import { Changes } from "../../Logic/Osm/Changes" import { Changes } from "../../Logic/Osm/Changes"
import { UIElement } from "../UIElement" import { UIElement } from "../UIElement"
import FilteredLayer from "../../Models/FilteredLayer" import FilteredLayer from "../../Models/FilteredLayer"
@ -32,7 +32,7 @@ export interface AutoAction extends SpecialVisualization {
applyActionOn( applyActionOn(
feature: Feature, feature: Feature,
state: { state: {
layout: LayoutConfig theme: ThemeConfig
changes: Changes changes: Changes
indexedFeatures: IndexedFeatureSource indexedFeatures: IndexedFeatureSource
}, },
@ -282,7 +282,7 @@ export default class AutoApplyButton implements SpecialVisualization {
argument: string[] argument: string[]
): BaseUIElement { ): BaseUIElement {
try { try {
if (!state.layout.official && !state.featureSwitchIsTesting.data) { if (!state.theme.official && !state.featureSwitchIsTesting.data) {
const t = Translations.t.general.add.import const t = Translations.t.general.add.import
return new Combine([ return new Combine([
new FixedUiElement( new FixedUiElement(

View file

@ -65,7 +65,7 @@
featureId, featureId,
deleteConfig.softDeletionTags, deleteConfig.softDeletionTags,
{ {
theme: state?.layout?.id ?? "unknown", theme: state?.theme?.id ?? "unknown",
specialMotivation: deleteReason, specialMotivation: deleteReason,
}, },
canBeDeleted.data canBeDeleted.data
@ -73,7 +73,7 @@
} else { } else {
// no _delete_reason is given, which implies that this is _not_ a deletion but merely a retagging via a nonDeleteMapping // no _delete_reason is given, which implies that this is _not_ a deletion but merely a retagging via a nonDeleteMapping
actionToTake = new ChangeTagAction(featureId, selectedTags, tags.data, { actionToTake = new ChangeTagAction(featureId, selectedTags, tags.data, {
theme: state?.layout?.id ?? "unkown", theme: state?.theme?.id ?? "unkown",
changeType: "special-delete", changeType: "special-delete",
}) })
} }

View file

@ -1,7 +1,6 @@
import { SpecialVisualization, SpecialVisualizationState } from "../../SpecialVisualization" import { SpecialVisualization, SpecialVisualizationState } from "../../SpecialVisualization"
import { UIEventSource } from "../../../Logic/UIEventSource" import { UIEventSource } from "../../../Logic/UIEventSource"
import { Feature, Geometry, LineString, Polygon } from "geojson" import { Feature, Geometry, LineString, Polygon } from "geojson"
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"
import BaseUIElement from "../../BaseUIElement" import BaseUIElement from "../../BaseUIElement"
import { ImportFlowArguments, ImportFlowUtils } from "./ImportFlow" import { ImportFlowArguments, ImportFlowUtils } from "./ImportFlow"
import Translations from "../../i18n/Translations" import Translations from "../../i18n/Translations"
@ -12,7 +11,7 @@ import ConflateImportFlowState from "./ConflateImportFlowState"
import { AutoAction } from "../AutoApplyButton" import { AutoAction } from "../AutoApplyButton"
import { IndexedFeatureSource } from "../../../Logic/FeatureSource/FeatureSource" import { IndexedFeatureSource } from "../../../Logic/FeatureSource/FeatureSource"
import { Changes } from "../../../Logic/Osm/Changes" import { Changes } from "../../../Logic/Osm/Changes"
import LayoutConfig from "../../../Models/ThemeConfig/LayoutConfig" import ThemeConfig from "../../../Models/ThemeConfig/ThemeConfig"
import { OsmConnection } from "../../../Logic/Osm/OsmConnection" import { OsmConnection } from "../../../Logic/Osm/OsmConnection"
export interface ConflateFlowArguments extends ImportFlowArguments { export interface ConflateFlowArguments extends ImportFlowArguments {
@ -47,7 +46,7 @@ export default class ConflateImportButtonViz implements SpecialVisualization, Au
feature: Feature<Geometry, { [name: string]: any }>, feature: Feature<Geometry, { [name: string]: any }>,
state: { state: {
osmConnection: OsmConnection osmConnection: OsmConnection
layout: LayoutConfig theme: ThemeConfig
changes: Changes changes: Changes
indexedFeatures: IndexedFeatureSource indexedFeatures: IndexedFeatureSource
}, },

View file

@ -10,7 +10,7 @@ import { GeoOperations } from "../../../Logic/GeoOperations"
import { TagUtils } from "../../../Logic/Tags/TagUtils" import { TagUtils } from "../../../Logic/Tags/TagUtils"
import { MergePointConfig } from "../../../Logic/Osm/Actions/CreateWayWithPointReuseAction" import { MergePointConfig } from "../../../Logic/Osm/Actions/CreateWayWithPointReuseAction"
import { And } from "../../../Logic/Tags/And" import { And } from "../../../Logic/Tags/And"
import LayoutConfig from "../../../Models/ThemeConfig/LayoutConfig" import ThemeConfig from "../../../Models/ThemeConfig/ThemeConfig"
import { Changes } from "../../../Logic/Osm/Changes" import { Changes } from "../../../Logic/Osm/Changes"
import { FeatureSource, IndexedFeatureSource } from "../../../Logic/FeatureSource/FeatureSource" import { FeatureSource, IndexedFeatureSource } from "../../../Logic/FeatureSource/FeatureSource"
import FullNodeDatabaseSource from "../../../Logic/FeatureSource/TiledFeatureSource/FullNodeDatabaseSource" import FullNodeDatabaseSource from "../../../Logic/FeatureSource/TiledFeatureSource/FullNodeDatabaseSource"
@ -61,7 +61,7 @@ export default class ConflateImportFlowState extends ImportFlow<ConflateFlowArgu
args: ConflateFlowArguments, args: ConflateFlowArguments,
state: { state: {
osmConnection: OsmConnection osmConnection: OsmConnection
layout: LayoutConfig theme: ThemeConfig
changes: Changes changes: Changes
indexedFeatures: IndexedFeatureSource indexedFeatures: IndexedFeatureSource
fullNodeDatabase?: FullNodeDatabaseSource fullNodeDatabase?: FullNodeDatabaseSource
@ -88,7 +88,7 @@ export default class ConflateImportFlowState extends ImportFlow<ConflateFlowArgu
GeoOperations.removeOvernoding(feature), GeoOperations.removeOvernoding(feature),
idOfFeatureToReplaceGeometry, idOfFeatureToReplaceGeometry,
{ {
theme: state.layout.id, theme: state.theme.id,
newTags: tagsToApply.data, newTags: tagsToApply.data,
} }
) )

View file

@ -174,7 +174,7 @@ export default abstract class ImportFlow<ArgT extends ImportFlowArguments> {
return { error: t.hasBeenImported } return { error: t.hasBeenImported }
} }
if (!state.layout.official && !isTesting) { if (!state.theme.official && !isTesting) {
// Unofficial theme - imports not allowed // Unofficial theme - imports not allowed
return { return {
error: t.officialThemesOnly, error: t.officialThemesOnly,
@ -183,7 +183,7 @@ export default abstract class ImportFlow<ArgT extends ImportFlowArguments> {
} }
if (this.targetLayer === undefined) { if (this.targetLayer === undefined) {
const e = `Target layer not defined: error in import button for theme: ${this.state.layout.id}: layer ${this.args.targetLayer} not found` const e = `Target layer not defined: error in import button for theme: ${this.state.theme.id}: layer ${this.args.targetLayer} not found`
console.error(e) console.error(e)
return { error: new Translation({ "*": e }) } return { error: new Translation({ "*": e }) }
} }

View file

@ -15,7 +15,7 @@
// The following variables are used for the map // The following variables are used for the map
const targetLayers: LayerConfig[] = args.targetLayer const targetLayers: LayerConfig[] = args.targetLayer
.split(" ") .split(" ")
.map((tl) => state.layout.layers.find((l) => l.id === tl)) .map((tl) => state.theme.layers.find((l) => l.id === tl))
const snapToLayers: string[] | undefined = const snapToLayers: string[] | undefined =
args.snap_onto_layers?.split(",")?.map((l) => l.trim()) ?? [] args.snap_onto_layers?.split(",")?.map((l) => l.trim()) ?? []
const maxSnapDistance: number = Number(args.max_snap_distance ?? 25) ?? 25 const maxSnapDistance: number = Number(args.max_snap_distance ?? 25) ?? 25

View file

@ -63,7 +63,7 @@ export class PointImportFlowState extends ImportFlow<PointImportFlowArguments> {
} }
const newElementAction = new CreateNewNodeAction(tags, location.lat, location.lon, { const newElementAction = new CreateNewNodeAction(tags, location.lat, location.lon, {
theme: this.state.layout.id, theme: this.state.theme.id,
changeType: "import", changeType: "import",
snapOnto: <OsmWay>snapOnto, snapOnto: <OsmWay>snapOnto,
specialMotivation: specialMotivation, specialMotivation: specialMotivation,

View file

@ -10,7 +10,7 @@ import { FixedUiElement } from "../../Base/FixedUiElement"
import WayImportFlow from "./WayImportFlow.svelte" import WayImportFlow from "./WayImportFlow.svelte"
import WayImportFlowState, { WayImportFlowArguments } from "./WayImportFlowState" import WayImportFlowState, { WayImportFlowArguments } from "./WayImportFlowState"
import { Utils } from "../../../Utils" import { Utils } from "../../../Utils"
import LayoutConfig from "../../../Models/ThemeConfig/LayoutConfig" import ThemeConfig from "../../../Models/ThemeConfig/ThemeConfig"
import { Changes } from "../../../Logic/Osm/Changes" import { Changes } from "../../../Logic/Osm/Changes"
import { IndexedFeatureSource } from "../../../Logic/FeatureSource/FeatureSource" import { IndexedFeatureSource } from "../../../Logic/FeatureSource/FeatureSource"
import FullNodeDatabaseSource from "../../../Logic/FeatureSource/TiledFeatureSource/FullNodeDatabaseSource" import FullNodeDatabaseSource from "../../../Logic/FeatureSource/TiledFeatureSource/FullNodeDatabaseSource"
@ -87,7 +87,7 @@ export default class WayImportButtonViz implements AutoAction, SpecialVisualizat
public async applyActionOn( public async applyActionOn(
feature: Feature, feature: Feature,
state: { state: {
layout: LayoutConfig theme: ThemeConfig
changes: Changes changes: Changes
indexedFeatures: IndexedFeatureSource indexedFeatures: IndexedFeatureSource
fullNodeDatabase: FullNodeDatabaseSource fullNodeDatabase: FullNodeDatabaseSource

View file

@ -32,7 +32,7 @@
ShowDataLayer.showMultipleLayers( ShowDataLayer.showMultipleLayers(
map, map,
new StaticFeatureSource([importFlow.originalFeature]), new StaticFeatureSource([importFlow.originalFeature]),
state.layout.layers, state.theme.layers,
{ zoomToFeatures: false } { zoomToFeatures: false }
) )

View file

@ -11,7 +11,7 @@ import { TagUtils } from "../../../Logic/Tags/TagUtils"
import { OsmCreateAction, PreviewableAction } from "../../../Logic/Osm/Actions/OsmChangeAction" import { OsmCreateAction, PreviewableAction } from "../../../Logic/Osm/Actions/OsmChangeAction"
import { FeatureSource, IndexedFeatureSource } from "../../../Logic/FeatureSource/FeatureSource" import { FeatureSource, IndexedFeatureSource } from "../../../Logic/FeatureSource/FeatureSource"
import CreateMultiPolygonWithPointReuseAction from "../../../Logic/Osm/Actions/CreateMultiPolygonWithPointReuseAction" import CreateMultiPolygonWithPointReuseAction from "../../../Logic/Osm/Actions/CreateMultiPolygonWithPointReuseAction"
import LayoutConfig from "../../../Models/ThemeConfig/LayoutConfig" import ThemeConfig from "../../../Models/ThemeConfig/ThemeConfig"
import { Changes } from "../../../Logic/Osm/Changes" import { Changes } from "../../../Logic/Osm/Changes"
import FullNodeDatabaseSource from "../../../Logic/FeatureSource/TiledFeatureSource/FullNodeDatabaseSource" import FullNodeDatabaseSource from "../../../Logic/FeatureSource/TiledFeatureSource/FullNodeDatabaseSource"
@ -52,7 +52,7 @@ export default class WayImportFlowState extends ImportFlow<WayImportFlowArgument
feature: Feature<LineString | Polygon>, feature: Feature<LineString | Polygon>,
args: WayImportFlowArguments, args: WayImportFlowArguments,
state: { state: {
layout: LayoutConfig theme: ThemeConfig
changes: Changes changes: Changes
indexedFeatures: IndexedFeatureSource indexedFeatures: IndexedFeatureSource
fullNodeDatabase?: FullNodeDatabaseSource fullNodeDatabase?: FullNodeDatabaseSource

View file

@ -60,7 +60,7 @@
} else if (state.changes) { } else if (state.changes) {
await state.changes.applyAction( await state.changes.applyAction(
new ChangeTagAction(tags.data.id, new And(selection), tags.data, { new ChangeTagAction(tags.data.id, new And(selection), tags.data, {
theme: state?.layout?.id ?? "unkown", theme: state?.theme?.id ?? "unkown",
changeType: "answer", changeType: "answer",
}) })
) )

View file

@ -19,7 +19,7 @@
const t = Translations.t.favouritePoi const t = Translations.t.favouritePoi
function markFavourite(isFavourite: boolean) { function markFavourite(isFavourite: boolean) {
state.favourites.markAsFavourite(feature, layer.id, state.layout.id, tags, isFavourite) state.favourites.markAsFavourite(feature, layer.id, state.theme.id, tags, isFavourite)
} }
</script> </script>

View file

@ -20,7 +20,7 @@
const t = Translations.t.favouritePoi const t = Translations.t.favouritePoi
function markFavourite(isFavourite: boolean) { function markFavourite(isFavourite: boolean) {
state.favourites.markAsFavourite(feature, layer.id, state.layout.id, tags, isFavourite) state.favourites.markAsFavourite(feature, layer.id, state.theme.id, tags, isFavourite)
} }
</script> </script>

View file

@ -72,7 +72,7 @@
ShowDataLayer.showMultipleLayers( ShowDataLayer.showMultipleLayers(
mlmap, mlmap,
new StaticFeatureSource(featuresToShow), new StaticFeatureSource(featuresToShow),
state.layout.layers, state.theme.layers,
{ zoomToFeatures: true } { zoomToFeatures: true }
) )
</script> </script>

View file

@ -94,7 +94,7 @@ export class MoveWizardState {
const matchingPresets = this.layer.presets.filter(preset => preset.preciseInput.snapToLayers && new And(preset.tags).matchesProperties(tags)) const matchingPresets = this.layer.presets.filter(preset => preset.preciseInput.snapToLayers && new And(preset.tags).matchesProperties(tags))
const matchingPreset = matchingPresets.flatMap(pr => pr.preciseInput?.snapToLayers) const matchingPreset = matchingPresets.flatMap(pr => pr.preciseInput?.snapToLayers)
for (const layerId of matchingPreset) { for (const layerId of matchingPreset) {
const snapOntoLayer = this._state.layout.getLayer(layerId) const snapOntoLayer = this._state.theme.getLayer(layerId)
const text = <Translation> t.reasons.reasonSnapTo.PartialSubsTr("name", snapOntoLayer.snapName) const text = <Translation> t.reasons.reasonSnapTo.PartialSubsTr("name", snapOntoLayer.snapName)
reasons.push({ reasons.push({
text, text,
@ -133,7 +133,7 @@ export class MoveWizardState {
snappedTo, snappedTo,
{ {
reason: reason.changesetCommentValue, reason: reason.changesetCommentValue,
theme: state.layout.id, theme: state.theme.id,
}), }),
) )
featureToMove.properties._lat = loc.lat featureToMove.properties._lat = loc.lat
@ -152,7 +152,7 @@ export class MoveWizardState {
featureToMove.properties, featureToMove.properties,
{ {
changeType: "relocated", changeType: "relocated",
theme: state.layout.id, theme: state.theme.id,
}, },
), ),
) )

View file

@ -64,7 +64,7 @@ class MultiApplyExecutor {
const keysToChange = this.params.keysToApply const keysToChange = this.params.keysToApply
const overwrite = this.params.overwrite const overwrite = this.params.overwrite
const selfTags = this.params.tagsSource.data const selfTags = this.params.tagsSource.data
const theme = this.params.state.layout.id const theme = this.params.state.theme.id
for (const id of featuresToChange) { for (const id of featuresToChange) {
const tagsToApply: Tag[] = [] const tagsToApply: Tag[] = []
const otherFeatureTags = allElements.getStore(id).data const otherFeatureTags = allElements.getStore(id).data

View file

@ -47,7 +47,7 @@
return return
} }
const loc = coordinate.data const loc = coordinate.data
txt += "\n\n #MapComplete #" + state?.layout?.id txt += "\n\n #MapComplete #" + state?.theme?.id
const id = await state?.osmConnection?.openNote(loc.lat, loc.lon, txt) const id = await state?.osmConnection?.openNote(loc.lat, loc.lon, txt)
console.log("Created a note, got id", id) console.log("Created a note, got id", id)
const feature = <Feature<Point, OsmTags>>{ const feature = <Feature<Point, OsmTags>>{

View file

@ -49,7 +49,7 @@ export class PlantNetDetectionViz implements SpecialVisualization {
]), ]),
tags.data, tags.data,
{ {
theme: state.layout.id, theme: state.theme.id,
changeType: "plantnet-ai-detection", changeType: "plantnet-ai-detection",
} }
) )

View file

@ -17,7 +17,7 @@
let [lon, lat] = GeoOperations.centerpointCoordinates(feature) let [lon, lat] = GeoOperations.centerpointCoordinates(feature)
const includeLayout = window.location.pathname.split("/").at(-1).startsWith("theme") const includeLayout = window.location.pathname.split("/").at(-1).startsWith("theme")
const layout = includeLayout ? "layout=" + state.layout.id + "&" : "" const layout = includeLayout ? "layout=" + state.theme.id + "&" : ""
let id: Store<string> = tags.mapD((tags) => tags.id) let id: Store<string> = tags.mapD((tags) => tags.id)
let url = id.mapD( let url = id.mapD(
(id) => (id) =>

View file

@ -30,9 +30,9 @@ export class ShareLinkViz implements SpecialVisualization {
const text = args[1] const text = args[1]
const generateShareData = () => { const generateShareData = () => {
const title = state?.layout?.title?.txt ?? "MapComplete" const title = state?.theme?.title?.txt ?? "MapComplete"
let matchingLayer: LayerConfig = state?.layout?.getMatchingLayer(tagSource?.data) let matchingLayer: LayerConfig = state?.theme?.getMatchingLayer(tagSource?.data)
let name = let name =
matchingLayer?.title?.GetRenderValue(tagSource.data)?.Subs(tagSource.data)?.txt ?? matchingLayer?.title?.GetRenderValue(tagSource.data)?.Subs(tagSource.data)?.txt ??
tagSource.data?.name ?? tagSource.data?.name ??
@ -49,7 +49,7 @@ export class ShareLinkViz implements SpecialVisualization {
return { return {
title: name, title: name,
url: url, url: url,
text: state?.layout?.shortDescription?.txt ?? "MapComplete", text: state?.theme?.shortDescription?.txt ?? "MapComplete",
} }
} }

View file

@ -58,7 +58,7 @@
id, id,
splitPoints.data.map((ff) => <[number, number]>(<Point>ff.geometry).coordinates), splitPoints.data.map((ff) => <[number, number]>(<Point>ff.geometry).coordinates),
{ {
theme: state?.layout?.id, theme: state?.theme?.id,
}, },
5 5
) )

View file

@ -144,7 +144,7 @@ export default class TagApplyButton implements AutoAction, SpecialVisualization
new And(tagsToApply.data), new And(tagsToApply.data),
tags.data, // We pass in the tags of the selected element, not the tags of the target element! tags.data, // We pass in the tags of the selected element, not the tags of the target element!
{ {
theme: state.layout.id, theme: state.theme.id,
changeType: "answer", changeType: "answer",
} }
) )

View file

@ -283,7 +283,7 @@
} }
dispatch("saved", { config, applied: selectedTags }) dispatch("saved", { config, applied: selectedTags })
const change = new ChangeTagAction(tags.data.id, selectedTags, tags.data, { const change = new ChangeTagAction(tags.data.id, selectedTags, tags.data, {
theme: tags.data["_orig_theme"] ?? state.layout.id, theme: tags.data["_orig_theme"] ?? state.theme.id,
changeType: "answer", changeType: "answer",
}) })
freeformInput.set(undefined) freeformInput.set(undefined)
@ -327,7 +327,7 @@
function clearAnswer() { function clearAnswer() {
const tagsToSet = settableKeys.data.map(k => new Tag(k, "")) const tagsToSet = settableKeys.data.map(k => new Tag(k, ""))
const change = new ChangeTagAction(tags.data.id, new And(tagsToSet), tags.data, { const change = new ChangeTagAction(tags.data.id, new And(tagsToSet), tags.data, {
theme: tags.data["_orig_theme"] ?? state.layout.id, theme: tags.data["_orig_theme"] ?? state.theme.id,
changeType: "answer", changeType: "answer",
}) })
freeformInput.set(undefined) freeformInput.set(undefined)

View file

@ -5,7 +5,7 @@ import List from "./Base/List"
import Translations from "./i18n/Translations" import Translations from "./i18n/Translations"
import { QueryParameters } from "../Logic/Web/QueryParameters" import { QueryParameters } from "../Logic/Web/QueryParameters"
import FeatureSwitchState from "../Logic/State/FeatureSwitchState" import FeatureSwitchState from "../Logic/State/FeatureSwitchState"
import LayoutConfig from "../Models/ThemeConfig/LayoutConfig" import ThemeConfig from "../Models/ThemeConfig/ThemeConfig"
import ThemeViewStateHashActor from "../Logic/Web/ThemeViewStateHashActor" import ThemeViewStateHashActor from "../Logic/Web/ThemeViewStateHashActor"
import MarkdownUtils from "../Utils/MarkdownUtils" import MarkdownUtils from "../Utils/MarkdownUtils"
@ -27,7 +27,7 @@ export default class QueryParameterDocumentation {
] ]
public static UrlParamDocs(): Map<string, string> { public static UrlParamDocs(): Map<string, string> {
const dummyLayout = new LayoutConfig(<any>{ const dummyLayout = new ThemeConfig(<any>{
id: "&gt;theme&lt;", id: "&gt;theme&lt;",
title: { en: "<theme>" }, title: { en: "<theme>" },
description: "A theme to generate docs with", description: "A theme to generate docs with",

View file

@ -20,7 +20,7 @@
let tags: UIEventSource<Record<string, string>> let tags: UIEventSource<Record<string, string>>
let descriptionTr: TagRenderingConfig = undefined let descriptionTr: TagRenderingConfig = undefined
if (entry.feature?.properties?.id) { if (entry.feature?.properties?.id) {
layer = state.layout.getMatchingLayer(entry.feature.properties) layer = state.theme.getMatchingLayer(entry.feature.properties)
tags = state.featureProperties.getStore(entry.feature.properties.id) tags = state.featureProperties.getStore(entry.feature.properties.id)
descriptionTr = layer?.tagRenderings?.find(tr => tr.labels.indexOf("description") >= 0) descriptionTr = layer?.tagRenderings?.find(tr => tr.labels.indexOf("description") >= 0)
} }

View file

@ -1,11 +1,11 @@
<script lang="ts"> <script lang="ts">
import { MinimalLayoutInformation } from "../../Models/ThemeConfig/LayoutConfig" import { MinimalThemeInformation } from "../../Models/ThemeConfig/ThemeConfig"
import { Translation } from "../i18n/Translation" import { Translation } from "../i18n/Translation"
import Icon from "../Map/Icon.svelte" import Icon from "../Map/Icon.svelte"
import Tr from "../Base/Tr.svelte" import Tr from "../Base/Tr.svelte"
import ThemeSearch from "../../Logic/Search/ThemeSearch" import ThemeSearch from "../../Logic/Search/ThemeSearch"
export let entry: MinimalLayoutInformation export let entry: MinimalThemeInformation
let otherTheme = entry let otherTheme = entry
</script> </script>
{#if entry} {#if entry}

View file

@ -14,7 +14,7 @@
export let state: SpecialVisualizationState export let state: SpecialVisualizationState
let searchTerm = state.searchState.searchTerm let searchTerm = state.searchState.searchTerm
let recentThemes = state.userRelatedState.recentlyVisitedThemes.value.map(themes => themes.filter(th => th !== state.layout.id).slice(0, 6)) let recentThemes = state.userRelatedState.recentlyVisitedThemes.value.map(themes => themes.filter(th => th !== state.theme.id).slice(0, 6))
let themeResults = state.searchState.themeSuggestions let themeResults = state.searchState.themeSuggestions
const t =Translations.t.general.search const t =Translations.t.general.search

View file

@ -1,6 +1,6 @@
import { Store, UIEventSource } from "../Logic/UIEventSource" import { Store, UIEventSource } from "../Logic/UIEventSource"
import BaseUIElement from "./BaseUIElement" import BaseUIElement from "./BaseUIElement"
import LayoutConfig from "../Models/ThemeConfig/LayoutConfig" import ThemeConfig from "../Models/ThemeConfig/ThemeConfig"
import { FeatureSource, IndexedFeatureSource, WritableFeatureSource } from "../Logic/FeatureSource/FeatureSource" import { FeatureSource, IndexedFeatureSource, WritableFeatureSource } from "../Logic/FeatureSource/FeatureSource"
import { OsmConnection } from "../Logic/Osm/OsmConnection" import { OsmConnection } from "../Logic/Osm/OsmConnection"
import { Changes } from "../Logic/Osm/Changes" import { Changes } from "../Logic/Osm/Changes"
@ -19,7 +19,7 @@ import FavouritesFeatureSource from "../Logic/FeatureSource/Sources/FavouritesFe
import { ProvidedImage } from "../Logic/ImageProviders/ImageProvider" import { ProvidedImage } from "../Logic/ImageProviders/ImageProvider"
import GeoLocationHandler from "../Logic/Actors/GeoLocationHandler" import GeoLocationHandler from "../Logic/Actors/GeoLocationHandler"
import { SummaryTileSourceRewriter } from "../Logic/FeatureSource/TiledFeatureSource/SummaryTileSource" import { SummaryTileSourceRewriter } from "../Logic/FeatureSource/TiledFeatureSource/SummaryTileSource"
import LayoutSource from "../Logic/FeatureSource/Sources/LayoutSource" import ThemeSource from "../Logic/FeatureSource/Sources/ThemeSource"
import { Map as MlMap } from "maplibre-gl" import { Map as MlMap } from "maplibre-gl"
import ShowDataLayer from "./Map/ShowDataLayer" import ShowDataLayer from "./Map/ShowDataLayer"
import { CombinedFetcher } from "../Logic/Web/NearbyImagesSearch" import { CombinedFetcher } from "../Logic/Web/NearbyImagesSearch"
@ -33,13 +33,13 @@ import FeaturePropertiesStore from "../Logic/FeatureSource/Actors/FeaturePropert
*/ */
export interface SpecialVisualizationState { export interface SpecialVisualizationState {
readonly guistate: MenuState readonly guistate: MenuState
readonly layout: LayoutConfig readonly theme: ThemeConfig
readonly featureSwitches: FeatureSwitchState readonly featureSwitches: FeatureSwitchState
readonly layerState: LayerState readonly layerState: LayerState
readonly featureProperties: FeaturePropertiesStore readonly featureProperties: FeaturePropertiesStore
readonly indexedFeatures: IndexedFeatureSource & LayoutSource readonly indexedFeatures: IndexedFeatureSource & ThemeSource
/** /**
* Some features will create a new element that should be displayed. * Some features will create a new element that should be displayed.
* These can be injected by appending them to this featuresource (and pinging it) * These can be injected by appending them to this featuresource (and pinging it)

View file

@ -164,7 +164,7 @@ class StealViz implements SpecialVisualization {
const tagRenderings: [LayerConfig, TagRenderingConfig][] = [] const tagRenderings: [LayerConfig, TagRenderingConfig][] = []
for (const layerAndTagRenderingId of layerAndtagRenderingIds.split(";")) { for (const layerAndTagRenderingId of layerAndtagRenderingIds.split(";")) {
const [layerId, tagRenderingId] = layerAndTagRenderingId.trim().split(".") const [layerId, tagRenderingId] = layerAndTagRenderingId.trim().split(".")
const layer = state.layout.layers.find((l) => l.id === layerId) const layer = state.theme.layers.find((l) => l.id === layerId)
const tagRendering = layer.tagRenderings.find((tr) => tr.id === tagRenderingId) const tagRendering = layer.tagRenderings.find((tr) => tr.id === tagRenderingId)
tagRenderings.push([layer, tagRendering]) tagRenderings.push([layer, tagRendering])
} }
@ -444,7 +444,7 @@ export default class SpecialVisualizations {
Locale.showLinkToWeblate.map((showTranslations) => { Locale.showLinkToWeblate.map((showTranslations) => {
const languages = showTranslations const languages = showTranslations
? LanguageUtils.usedLanguagesSorted ? LanguageUtils.usedLanguagesSorted
: state.layout.language : state.theme.language
return new SvelteUIElement(LanguagePicker, { return new SvelteUIElement(LanguagePicker, {
assignTo: state.userRelatedState.language, assignTo: state.userRelatedState.language,
availableLanguages: languages, availableLanguages: languages,
@ -971,7 +971,7 @@ export default class SpecialVisualizations {
return undefined return undefined
} }
const allUnits: Unit[] = [].concat( const allUnits: Unit[] = [].concat(
...(state?.layout?.layers?.map((lyr) => lyr.units) ?? []) ...(state?.theme?.layers?.map((lyr) => lyr.units) ?? [])
) )
const unit = allUnits.filter((unit) => const unit = allUnits.filter((unit) =>
unit.isApplicableToKey(key) unit.isApplicableToKey(key)
@ -1125,7 +1125,7 @@ export default class SpecialVisualizations {
) => ) =>
new VariableUiElement( new VariableUiElement(
tagsSource.map((tags) => { tagsSource.map((tags) => {
if (state.layout === undefined) { if (state.theme === undefined) {
return "<feature title>" return "<feature title>"
} }
const title = layer?.title?.GetRenderValue(tags) const title = layer?.title?.GetRenderValue(tags)
@ -1276,7 +1276,7 @@ export default class SpecialVisualizations {
constr: (state) => { constr: (state) => {
return new Combine( return new Combine(
state.layout.layers state.theme.layers
.filter( .filter(
(l) => (l) =>
l.name !== null && l.name !== null &&
@ -1995,7 +1995,7 @@ export default class SpecialVisualizations {
layer: LayerConfig layer: LayerConfig
): BaseUIElement { ): BaseUIElement {
const translation = tagSource.map((tags) => { const translation = tagSource.map((tags) => {
const layer = state.layout.getMatchingLayer(tags) const layer = state.theme.getMatchingLayer(tags)
return layer?.getMostMatchingPreset(tags)?.description return layer?.getMostMatchingPreset(tags)?.description
}) })
return new VariableUiElement(translation) return new VariableUiElement(translation)

View file

@ -11,7 +11,7 @@ import BaseUIElement from "./BaseUIElement"
import Title from "./Base/Title" import Title from "./Base/Title"
import { FixedUiElement } from "./Base/FixedUiElement" import { FixedUiElement } from "./Base/FixedUiElement"
import List from "./Base/List" import List from "./Base/List"
import LayoutConfig from "../Models/ThemeConfig/LayoutConfig" import ThemeConfig from "../Models/ThemeConfig/ThemeConfig"
import mcChanges from "../../src/assets/generated/themes/mapcomplete-changes.json" import mcChanges from "../../src/assets/generated/themes/mapcomplete-changes.json"
import SvelteUIElement from "./Base/SvelteUIElement" import SvelteUIElement from "./Base/SvelteUIElement"
import Filterview from "./BigComponents/Filterview.svelte" import Filterview from "./BigComponents/Filterview.svelte"
@ -24,7 +24,7 @@ import { Feature } from "geojson"
class StatsticsForOverviewFile extends Combine { class StatsticsForOverviewFile extends Combine {
constructor(homeUrl: string, paths: string[]) { constructor(homeUrl: string, paths: string[]) {
paths = paths.filter((p) => !p.endsWith("file-overview.json")) paths = paths.filter((p) => !p.endsWith("file-overview.json"))
const layer = new LayoutConfig(<any>mcChanges, true).layers[0] const layer = new ThemeConfig(<any>mcChanges, true).layers[0]
const filteredLayer = new FilteredLayer(layer) const filteredLayer = new FilteredLayer(layer)
const filterPanel = new Combine([ const filterPanel = new Combine([
new Title("Filters"), new Title("Filters"),

View file

@ -18,7 +18,7 @@ import { OsmConnection } from "../../Logic/Osm/OsmConnection"
import { OsmTags } from "../../Models/OsmFeature" import { OsmTags } from "../../Models/OsmFeature"
import { Feature, Point } from "geojson" import { Feature, Point } from "geojson"
import LayerConfig from "../../Models/ThemeConfig/LayerConfig" import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
import { LayoutConfigJson } from "../../Models/ThemeConfig/Json/LayoutConfigJson" import { ThemeConfigJson } from "../../Models/ThemeConfig/Json/ThemeConfigJson"
import { PrepareTheme } from "../../Models/ThemeConfig/Conversion/PrepareTheme" import { PrepareTheme } from "../../Models/ThemeConfig/Conversion/PrepareTheme"
import { ConversionContext } from "../../Models/ThemeConfig/Conversion/ConversionContext" import { ConversionContext } from "../../Models/ThemeConfig/Conversion/ConversionContext"
import { LocalStorageSource } from "../../Logic/Web/LocalStorageSource" import { LocalStorageSource } from "../../Logic/Web/LocalStorageSource"
@ -334,7 +334,7 @@ export default class EditLayerState extends EditJsonState<LayerConfigJson> {
return 0 return 0
} }
} }
public readonly layout: { getMatchingLayer: (key: any) => LayerConfig } public readonly theme: { getMatchingLayer: (key: any) => LayerConfig }
public readonly featureSwitches: { public readonly featureSwitches: {
featureSwitchIsDebugging: UIEventSource<boolean> featureSwitchIsDebugging: UIEventSource<boolean>
} }
@ -359,7 +359,7 @@ export default class EditLayerState extends EditJsonState<LayerConfigJson> {
options: { expertMode: UIEventSource<boolean> } options: { expertMode: UIEventSource<boolean> }
) { ) {
super(schema, server, "layers", osmConnection, options) super(schema, server, "layers", osmConnection, options)
this.layout = { this.theme = {
getMatchingLayer: () => { getMatchingLayer: () => {
try { try {
return new LayerConfig(<LayerConfigJson>this.configuration.data, "dynamic") return new LayerConfig(<LayerConfigJson>this.configuration.data, "dynamic")
@ -458,7 +458,8 @@ export default class EditLayerState extends EditJsonState<LayerConfigJson> {
} }
const state: DesugaringContext = { const state: DesugaringContext = {
tagRenderings: sharedQuestions, tagRenderings: sharedQuestions,
sharedLayers: layers sharedLayers: layers,
tagRenderingOrder: []
} }
const prepare = this.buildValidation(state) const prepare = this.buildValidation(state)
const context = ConversionContext.construct([], ["prepare"]) const context = ConversionContext.construct([], ["prepare"])
@ -472,7 +473,7 @@ export default class EditLayerState extends EditJsonState<LayerConfigJson> {
} }
} }
export class EditThemeState extends EditJsonState<LayoutConfigJson> { export class EditThemeState extends EditJsonState<ThemeConfigJson> {
constructor( constructor(
schema: ConfigMeta[], schema: ConfigMeta[],
server: StudioServer, server: StudioServer,
@ -483,7 +484,7 @@ export class EditThemeState extends EditJsonState<LayoutConfigJson> {
this.setupFixers() this.setupFixers()
} }
protected buildValidation(state: DesugaringContext): Conversion<LayoutConfigJson, any> { protected buildValidation(state: DesugaringContext): Conversion<ThemeConfigJson, any> {
return new Pipe( return new Pipe(
new PrevalidateTheme(), new PrevalidateTheme(),
new Pipe( new Pipe(
@ -513,7 +514,7 @@ export class EditThemeState extends EditJsonState<LayoutConfigJson> {
}) })
} }
protected async validate(configuration: Partial<LayoutConfigJson>) { protected async validate(configuration: Partial<ThemeConfigJson>) {
const layers = AllSharedLayers.getSharedLayersConfigs() const layers = AllSharedLayers.getSharedLayersConfigs()
for (const l of configuration.layers ?? []) { for (const l of configuration.layers ?? []) {
@ -534,7 +535,8 @@ export class EditThemeState extends EditJsonState<LayoutConfigJson> {
} }
const state: DesugaringContext = { const state: DesugaringContext = {
tagRenderings: sharedQuestions, tagRenderings: sharedQuestions,
sharedLayers: layers sharedLayers: layers,
tagRenderingOrder: []
} }
const prepare = this.buildValidation(state) const prepare = this.buildValidation(state)
const context = ConversionContext.construct([], ["prepare"]) const context = ConversionContext.construct([], ["prepare"])
@ -542,7 +544,7 @@ export class EditThemeState extends EditJsonState<LayoutConfigJson> {
Utils.NoNullInplace(configuration.layers) Utils.NoNullInplace(configuration.layers)
} }
try { try {
prepare.convert(<LayoutConfigJson>configuration, context) prepare.convert(<ThemeConfigJson>configuration, context)
} catch (e) { } catch (e) {
console.error(e) console.error(e)
context.err(e) context.err(e)

View file

@ -2,7 +2,7 @@ import { Utils } from "../../Utils"
import Constants from "../../Models/Constants" import Constants from "../../Models/Constants"
import { LayerConfigJson } from "../../Models/ThemeConfig/Json/LayerConfigJson" import { LayerConfigJson } from "../../Models/ThemeConfig/Json/LayerConfigJson"
import { Store, UIEventSource } from "../../Logic/UIEventSource" import { Store, UIEventSource } from "../../Logic/UIEventSource"
import { LayoutConfigJson } from "../../Models/ThemeConfig/Json/LayoutConfigJson" import { ThemeConfigJson } from "../../Models/ThemeConfig/Json/ThemeConfigJson"
/** /**
* A small class wrapping around the Server API. * A small class wrapping around the Server API.
@ -71,12 +71,12 @@ export default class StudioServer {
} }
async fetch(layerId: string, category: "layers", uid?: number): Promise<LayerConfigJson> async fetch(layerId: string, category: "layers", uid?: number): Promise<LayerConfigJson>
async fetch(layerId: string, category: "themes", uid?: number): Promise<LayoutConfigJson> async fetch(layerId: string, category: "themes", uid?: number): Promise<ThemeConfigJson>
async fetch( async fetch(
layerId: string, layerId: string,
category: "layers" | "themes", category: "layers" | "themes",
uid?: number uid?: number
): Promise<LayerConfigJson | LayoutConfigJson> { ): Promise<LayerConfigJson | ThemeConfigJson> {
try { try {
return <any>await Utils.downloadJson(this.urlFor(layerId, category, uid)) return <any>await Utils.downloadJson(this.urlFor(layerId, category, uid))
} catch (e) { } catch (e) {

View file

@ -53,7 +53,7 @@
export let state: ThemeViewState export let state: ThemeViewState
let layout = state.layout let theme = state.theme
let maplibremap: UIEventSource<MlMap> = state.map let maplibremap: UIEventSource<MlMap> = state.map
let state_selectedElement = state.selectedElement let state_selectedElement = state.selectedElement
let selectedElement: UIEventSource<Feature> = new UIEventSource<Feature>(undefined) let selectedElement: UIEventSource<Feature> = new UIEventSource<Feature>(undefined)
@ -65,7 +65,7 @@
let gpsButtonAriaLabel = state.geolocation.geolocationState.gpsStateExplanation let gpsButtonAriaLabel = state.geolocation.geolocationState.gpsStateExplanation
let debug = state.featureSwitches.featureSwitchIsDebugging let debug = state.featureSwitches.featureSwitchIsDebugging
let featureSwitches: FeatureSwitchState = state.featureSwitches let featureSwitches: FeatureSwitchState = state.featureSwitches
let currentViewLayer: LayerConfig = layout.layers.find((l) => l.id === "current_view") let currentViewLayer: LayerConfig = theme.layers.find((l) => l.id === "current_view")
let rasterLayer: Store<RasterLayerPolygon> = state.mapProperties.rasterLayer let rasterLayer: Store<RasterLayerPolygon> = state.mapProperties.rasterLayer
let currentZoom = state.mapProperties.zoom let currentZoom = state.mapProperties.zoom
let showCrosshair = state.userRelatedState.showCrosshair let showCrosshair = state.userRelatedState.showCrosshair
@ -213,7 +213,7 @@
<div class="flex w-full items-end justify-between px-4"> <div class="flex w-full items-end justify-between px-4">
<div class="flex flex-col"> <div class="flex flex-col">
<If condition={featureSwitches.featureSwitchEnableLogin}> <If condition={featureSwitches.featureSwitchEnableLogin}>
{#if $addNewFeatureMode.indexOf("button") >= 0 && ((state.layout.hasPresets() && state.layout.enableAddNewPoints) || state.layout.hasNoteLayer())} {#if $addNewFeatureMode.indexOf("button") >= 0 && ((state.theme.hasPresets() && state.theme.enableAddNewPoints) || state.theme.hasNoteLayer())}
<button <button
class="low-interaction pointer-events-auto w-fit" class="low-interaction pointer-events-auto w-fit"
class:disabled={$currentZoom < Constants.minZoomLevelToAddNewPoint} class:disabled={$currentZoom < Constants.minZoomLevelToAddNewPoint}
@ -224,7 +224,7 @@
> >
{#if $currentZoom < Constants.minZoomLevelToAddNewPoint} {#if $currentZoom < Constants.minZoomLevelToAddNewPoint}
<Tr t={Translations.t.general.add.zoomInFurther} /> <Tr t={Translations.t.general.add.zoomInFurther} />
{:else if state.layout.hasPresets()} {:else if state.theme.hasPresets()}
<Tr t={Translations.t.general.add.title} /> <Tr t={Translations.t.general.add.title} />
{:else} {:else}
<Tr t={Translations.t.notes.addAComment} /> <Tr t={Translations.t.notes.addAComment} />
@ -351,9 +351,9 @@
<div <div
class="m-0.5 mx-1 flex cursor-pointer items-center max-[480px]:w-full sm:mx-1 mr-2" class="m-0.5 mx-1 flex cursor-pointer items-center max-[480px]:w-full sm:mx-1 mr-2"
> >
<Marker icons={layout.icon} size="h-6 w-6 shrink-0 mr-0.5 sm:mr-1 md:mr-2" /> <Marker icons={theme.icon} size="h-6 w-6 shrink-0 mr-0.5 sm:mr-1 md:mr-2" />
<b class="mr-1"> <b class="mr-1">
<Tr t={layout.title} /> <Tr t={theme.title} />
</b> </b>
</div> </div>
</MapControlButton> </MapControlButton>

View file

@ -52,7 +52,7 @@ export class PngMapCreator {
PngMapCreator.id++ PngMapCreator.id++
try { try {
const layout = this._state.layout const layout = this._state.theme
function setState(msg: string) { function setState(msg: string) {
status?.setData(layout.id + ": " + msg) status?.setData(layout.id + ": " + msg)

View file

@ -1,10 +1,10 @@
import { QueryParameters } from "./Logic/Web/QueryParameters" import { QueryParameters } from "./Logic/Web/QueryParameters"
import AllThemesGui from "./UI/AllThemesGui.svelte" import AllThemesGui from "./UI/AllThemesGui.svelte"
const layout = QueryParameters.GetQueryParameter("layout", undefined).data ?? "" const theme = QueryParameters.GetQueryParameter("layout", undefined).data ?? ""
const customLayout = QueryParameters.GetQueryParameter("userlayout", undefined).data ?? "" const customLayout = QueryParameters.GetQueryParameter("userlayout", undefined).data ?? ""
const l = window.location const l = window.location
if (layout !== "") { if (theme !== "") {
if (window.location.host.startsWith("127.0.0.1")) { if (window.location.host.startsWith("127.0.0.1")) {
window.location.replace( window.location.replace(
l.protocol + l.protocol +
@ -13,12 +13,12 @@ if (layout !== "") {
"/theme.html" + "/theme.html" +
l.search + l.search +
"&layout=" + "&layout=" +
layout + theme +
l.hash l.hash
) )
} else { } else {
window.location.replace( window.location.replace(
l.protocol + "//" + window.location.host + "/" + layout + ".html" + l.search + l.hash l.protocol + "//" + window.location.host + "/" + theme + ".html" + l.search + l.hash
) )
} }
} else if (customLayout !== "") { } else if (customLayout !== "") {

Some files were not shown because too many files have changed in this diff Show more