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?
---------------------
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**.
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:
- [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)
- [The `TagRendering`](/src/Models/ThemeConfig/Json/TagRenderingConfigJson.ts)
- 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

View file

@ -61,7 +61,7 @@
"minzoom": 18,
"isCounted": false,
"calculatedTags": [
"_surface:strict:=feat(get)('_surface')"
"_surface:strict:=get(feat)('_surface')"
],
"tagRenderings": [
{
@ -224,19 +224,19 @@
"minzoom": 18,
"calculatedTags": [
"_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_properties=feat(get)('_overlaps_with')?.feat?.properties",
"_overlap_percentage=Math.round(100 * (feat(get)('_overlaps_with')?.overlap / feat(get)('_overlaps_with_properties')['_surface:strict']))",
"_reverse_overlap_percentage=Math.round(100 * (feat(get)('_overlaps_with')?.overlap / feat(get)('_surface')))",
"_overlaps_with=get(feat)('_overlaps_with_buildings').find(f => f.overlap > 1 /* square meter */ )",
"_overlaps_with_properties=get(feat)('_overlaps_with')?.feat?.properties",
"_overlap_percentage=Math.round(100 * (get(feat)('_overlaps_with')?.overlap / get(feat)('_overlaps_with_properties')['_surface:strict']))",
"_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: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:ref:bag=Number(feat.properties.identificatie)",
"_bag_obj:source:date=new Date().toISOString().split('T')[0]",
"_bag_obj:start_date=feat.properties.bouwjaar",
"_osm_obj:id=feat(get)('_overlaps_with_properties')?.id",
"_osm_obj:building=feat(get)('_overlaps_with_properties')?.building",
"_imported_osm_object_found:=Number(feat.properties.identificatie)==Number(feat(get)('_overlaps_with_properties')['ref:bag'])"
"_osm_obj:id=get(feat)('_overlaps_with_properties')?.id",
"_osm_obj:building=get(feat)('_overlaps_with_properties')?.building",
"_imported_osm_object_found:=Number(feat.properties.identificatie)==Number(get(feat)('_overlaps_with_properties')['ref:bag'])"
],
"tagRenderings": [
{
@ -435,11 +435,11 @@
"_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:source:date=new Date().toISOString().split('T')[0]",
"_osm_obj:addr:city:=feat(get)('_closed_osm_addr')['addr:city']",
"_osm_obj:addr:housenumber:=feat(get)('_closed_osm_addr')['addr:housenumber']",
"_osm_obj:addr:postcode:=feat(get)('_closed_osm_addr')['addr:postcode']",
"_osm_obj:addr:street:=feat(get)('_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'])"
"_osm_obj:addr:city:=get(feat)('_closed_osm_addr')['addr:city']",
"_osm_obj:addr:housenumber:=get(feat)('_closed_osm_addr')['addr:housenumber']",
"_osm_obj:addr:postcode:=get(feat)('_closed_osm_addr')['addr:postcode']",
"_osm_obj:addr:street:=get(feat)('_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": [
{

View file

@ -2,7 +2,7 @@ import * as fs from "fs"
import { existsSync, lstatSync, readdirSync, readFileSync } from "fs"
import { Utils } from "../src/Utils"
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 xml2js from "xml2js"
@ -111,7 +111,7 @@ export default class ScriptUtils {
}
public static getThemeFiles(useTranslationPaths = false): {
parsed: LayoutConfigJson;
parsed: ThemeConfigJson;
path: string;
raw: string
}[] {

View file

@ -8,7 +8,7 @@ import QueryParameterDocumentation from "../src/UI/QueryParameterDocumentation"
import ScriptUtils from "./ScriptUtils"
import Translations from "../src/UI/i18n/Translations"
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 fakedom from "fake-dom"
import unit from "../src/assets/generated/layers/unit.json"
@ -253,7 +253,7 @@ export class GenerateDocs extends Script {
}
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(), [
"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 layersToShow = theme.layers.filter(
(l) =>

View file

@ -1,7 +1,7 @@
import ScriptUtils from "./ScriptUtils"
import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "fs"
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 Constants from "../src/Models/Constants"
import {
@ -29,7 +29,7 @@ import LayerConfig from "../src/Models/ThemeConfig/LayerConfig"
import PointRenderingConfig from "../src/Models/ThemeConfig/PointRenderingConfig"
import { ConversionContext } from "../src/Models/ThemeConfig/Conversion/ConversionContext"
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 { Translatable } from "../src/Models/ThemeConfig/Json/Translatable"
import { ValidateThemeAndLayers } from "../src/Models/ThemeConfig/Conversion/ValidateThemeAndLayers"
@ -148,14 +148,14 @@ class LayerOverviewUtils extends Script {
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))
return new Set([].concat(...publicThemes.map((th) => this.extractLayerIdsFrom(th))))
}
private static extractLayerIdsFrom(
themeFile: LayoutConfigJson,
themeFile: ThemeConfigJson,
includeInlineLayers = true,
): string[] {
const publicLayerIds: string[] = []
@ -295,7 +295,7 @@ class LayerOverviewUtils extends Script {
layerKeywords[id] = this.layerKeywords(layer)
})
const perId = new Map<string, MinimalLayoutInformation>()
const perId = new Map<string, MinimalThemeInformation>()
for (const theme of themes) {
const keywords: Record<string, string[]> = {}
@ -308,7 +308,7 @@ class LayerOverviewUtils extends Script {
}
const data = <MinimalLayoutInformation> {
const data = <MinimalThemeInformation> {
id: theme.id,
title: theme.title,
shortDescription: LayerOverviewUtils.cleanTranslation(theme.shortDescription),
@ -342,7 +342,7 @@ class LayerOverviewUtils extends Script {
)
}
writeTheme(theme: LayoutConfigJson) {
writeTheme(theme: ThemeConfigJson) {
if (!existsSync(LayerOverviewUtils.themePath)) {
mkdirSync(LayerOverviewUtils.themePath)
}
@ -519,7 +519,7 @@ class LayerOverviewUtils extends Script {
)
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) {
@ -545,7 +545,7 @@ class LayerOverviewUtils extends Script {
if: "theme=" + th.id,
then: th.icon,
}))
const proto: LayoutConfigJson = JSON.parse(
const proto: ThemeConfigJson = JSON.parse(
readFileSync("./assets/themes/mapcomplete-changes/mapcomplete-changes.proto.json", {
encoding: "utf8",
}),
@ -689,7 +689,7 @@ class LayerOverviewUtils extends Script {
* @param themeFile
* @private
*/
private extractJavascriptCode(themeFile: LayoutConfigJson) {
private extractJavascriptCode(themeFile: ThemeConfigJson) {
const allCode = [
"import {Feature} from 'geojson'",
"import { ExtraFuncType } from \"../../../Logic/ExtraFunctions\";",
@ -804,10 +804,10 @@ class LayerOverviewUtils extends Script {
recompiledThemes: string[],
forceReload: boolean,
whitelist: Set<string>,
): Map<string, LayoutConfigJson> {
): Map<string, ThemeConfigJson> {
console.log(" ---------- VALIDATING BUILTIN THEMES ---------")
const themeFiles = ScriptUtils.getThemeFiles()
const fixed = new Map<string, LayoutConfigJson>()
const fixed = new Map<string, ThemeConfigJson>()
const publicLayers = LayerOverviewUtils.publicLayerIdsFrom(
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 { Translation } from "../src/UI/i18n/Translation"
import all_known_layouts from "../src/assets/generated/known_themes.json"
import { LayoutConfigJson } from "../src/Models/ThemeConfig/Json/LayoutConfigJson"
import LayoutConfig from "../src/Models/ThemeConfig/LayoutConfig"
import { ThemeConfigJson } from "../src/Models/ThemeConfig/Json/ThemeConfigJson"
import ThemeConfig from "../src/Models/ThemeConfig/ThemeConfig"
import xml2js from "xml2js"
import ScriptUtils from "./ScriptUtils"
import { Utils } from "../src/Utils"
@ -100,7 +100,7 @@ class GenerateLayouts extends Script {
return newname
}
async createSocialImage(layout: LayoutConfig, template: "" | "Wide"): Promise<string> {
async createSocialImage(layout: ThemeConfig, template: "" | "Wide"): Promise<string> {
if (!layout.icon.endsWith(".svg")) {
console.warn(
"Not creating a social image for " +
@ -160,7 +160,7 @@ class GenerateLayouts extends Script {
}
async createManifest(
layout: LayoutConfig,
layout: ThemeConfig,
alreadyWritten: string[]
): Promise<{
manifest: any
@ -319,8 +319,8 @@ class GenerateLayouts extends Script {
}
async generateCsp(
layout: LayoutConfig,
layoutJson: LayoutConfigJson,
layout: ThemeConfig,
layoutJson: ThemeConfigJson,
options: {
scriptSrcs: string[]
}
@ -441,8 +441,8 @@ class GenerateLayouts extends Script {
}
async createLandingPage(
layout: LayoutConfig,
layoutJson: LayoutConfigJson,
layout: ThemeConfig,
layoutJson: ThemeConfigJson,
whiteIcons,
alreadyWritten
) {
@ -456,7 +456,7 @@ class GenerateLayouts extends Script {
.replace(/"/g, '\\"')
let ogImage = layout.socialImage
let twitterImage = ogImage
if (ogImage === LayoutConfig.defaultSocialImage && layout.official) {
if (ogImage === ThemeConfig.defaultSocialImage && layout.official) {
try {
ogImage = (await this.createSocialImage(layout, "")) ?? 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 imports = [
@ -628,18 +628,18 @@ class GenerateLayouts extends Script {
"theme",
]
// @ts-ignore
const all: LayoutConfigJson[] = all_known_layouts.themes
const all: ThemeConfigJson[] = all_known_layouts.themes
const args = process.argv
const theme = args[2]
if (theme !== undefined) {
console.warn("Only generating layout " + theme)
}
for (const i in all) {
const layoutConfigJson: LayoutConfigJson = all[i]
const layoutConfigJson: ThemeConfigJson = all[i]
if (theme !== undefined && layoutConfigJson.id !== theme) {
continue
}
const layout = new LayoutConfig(layoutConfigJson, true)
const layout = new ThemeConfig(layoutConfigJson, true)
const layoutName = layout.id
if (blacklist.indexOf(layoutName.toLowerCase()) >= 0) {
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(
new LayoutConfig({
new ThemeConfig({
icon: "./assets/svg/mapcomplete_logo.svg",
id: "index",
layers: [],

View file

@ -2,7 +2,7 @@ import { AllKnownLayouts } from "../src/Customizations/AllKnownLayouts"
import Locale from "../src/UI/i18n/Locale"
import { Translation } from "../src/UI/i18n/Translation"
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 { Utils } from "../src/Utils"
@ -33,7 +33,7 @@ function generateTagOverview(
return overview
}
function generateLayerUsage(layer: LayerConfig, layout: LayoutConfig): any[] {
function generateLayerUsage(layer: LayerConfig, layout: ThemeConfig): any[] {
if (layer.name === undefined) {
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
* @param layout
*/
function generateTagInfoEntry(layout: LayoutConfig): any {
function generateTagInfoEntry(layout: ThemeConfig): any {
const usedTags = []
for (const layer of layout.layers) {
if (layer.source === null) {

View file

@ -15,7 +15,7 @@ type ErrorMessage = {
message: {
stacktrace: string
message: string
layout: string
theme: string
version: string
language: string
username: string
@ -40,7 +40,7 @@ class HandleErrors extends Script {
) {
console.log(
parsed.message.username,
parsed.message.layout,
parsed.message.theme,
parsed.message.message,
parsed.date
)
@ -82,7 +82,7 @@ class HandleErrors extends Script {
} = changesObj.CreateChangesetObjects(toUpload, objects, true)
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 (
changeset ===
`<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 LayoutConfig from "../Models/ThemeConfig/LayoutConfig"
import ThemeConfig from "../Models/ThemeConfig/ThemeConfig"
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 Constants from "../Models/Constants"
@ -9,8 +9,8 @@ import Constants from "../Models/Constants"
* Somewhat of a dictionary, which lazily parses needed themes
*/
export class AllKnownLayoutsLazy {
private readonly raw: Map<string, LayoutConfigJson> = new Map()
private readonly dict: Map<string, LayoutConfig> = new Map()
private readonly raw: Map<string, ThemeConfigJson> = new Map()
private readonly dict: Map<string, ThemeConfig> = new Map()
constructor(includeFavouriteLayer = true) {
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)
}
@ -40,13 +40,13 @@ export class AllKnownLayoutsLazy {
return this.raw.size
}
public get(key: string): LayoutConfig {
public get(key: string): ThemeConfig {
const cached = this.dict.get(key)
if (cached !== undefined) {
return cached
}
const layout = new LayoutConfig(this.getConfig(key))
const layout = new ThemeConfig(this.getConfig(key))
this.dict.set(key, layout)
return layout
}

View file

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

View file

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

View file

@ -34,7 +34,7 @@ export default class SelectedElementTagsUpdater {
public static applyUpdate(latestTags: OsmTags, id: string, state: SpecialVisualizationState) {
try {
const leftRightSensitive = state.layout.isLeftRightSensitive()
const leftRightSensitive = state.theme.isLeftRightSensitive()
if (leftRightSensitive) {
SimpleMetaTagger.removeBothTagging(latestTags)
@ -111,7 +111,7 @@ export default class SelectedElementTagsUpdater {
}
private invalidateCache(s: Feature) {
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))
}
private installCallback() {

View file

@ -12,11 +12,11 @@ export default class TitleHandler {
const currentTitle: Store<string> = selectedElement.map(
(selected) => {
const lng = Locale.language.data
const defaultTitle = state.layout?.title?.textFor(lng) ?? "MapComplete"
const defaultTitle = state.theme?.title?.textFor(lng) ?? "MapComplete"
if (selected === undefined) {
return defaultTitle
}
const layer = state.layout.getMatchingLayer(selected.properties)
const layer = state.theme.getMatchingLayer(selected.properties)
if (layer === undefined) {
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 { AllKnownLayouts } from "../Customizations/AllKnownLayouts"
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 Hash from "./Web/Hash"
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"
export default class DetermineLayout {
export default class DetermineTheme {
private static readonly _knownImages = new Set(Array.from(licenses).map((l) => l.path))
private static readonly loadCustomThemeParam = QueryParameters.GetQueryParameter(
"userlayout",
@ -31,7 +31,7 @@ export default class DetermineLayout {
)
public static getCustomDefinition(): string {
const layoutFromBase64 = decodeURIComponent(DetermineLayout.loadCustomThemeParam.data)
const layoutFromBase64 = decodeURIComponent(DetermineTheme.loadCustomThemeParam.data)
if (layoutFromBase64.startsWith("http")) {
return layoutFromBase64
@ -53,8 +53,8 @@ export default class DetermineLayout {
}
private static async expandRemoteLayers(
layoutConfig: LayoutConfigJson
): Promise<LayoutConfigJson> {
layoutConfig: ThemeConfigJson
): Promise<ThemeConfigJson> {
if (!layoutConfig.layers) {
// This is probably a layer in 'layer-only-mode'
return layoutConfig
@ -79,16 +79,16 @@ export default class DetermineLayout {
/**
* Gets the correct layout for this website
*/
public static async GetLayout(): Promise<LayoutConfig | undefined> {
const layoutFromBase64 = decodeURIComponent(DetermineLayout.loadCustomThemeParam.data)
public static async getTheme(): Promise<ThemeConfig | undefined> {
const layoutFromBase64 = decodeURIComponent(DetermineTheme.loadCustomThemeParam.data)
if (layoutFromBase64.startsWith("http")) {
return await DetermineLayout.LoadRemoteTheme(layoutFromBase64)
return await DetermineTheme.LoadRemoteTheme(layoutFromBase64)
}
if (layoutFromBase64 !== "false") {
// 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
@ -127,7 +127,7 @@ export default class DetermineLayout {
public static async LoadLayoutFromHash(
userLayoutParam: UIEventSource<string>
): Promise<LayoutConfig | null> {
): Promise<ThemeConfig | null> {
let hash = location.hash.substr(1)
let json: any
@ -157,7 +157,7 @@ export default class DetermineLayout {
json = await this.expandRemoteLayers(json)
const layoutToUse = DetermineLayout.prepCustomTheme(json)
const layoutToUse = DetermineTheme.prepCustomTheme(json)
userLayoutParam.setData(layoutToUse.id)
return layoutToUse
}
@ -166,7 +166,7 @@ export default class DetermineLayout {
const dict = new Map<string, QuestionableTagRenderingConfigJson>()
for (const tagRendering of questions.tagRenderings) {
dict.set(tagRendering.id, tagRendering)
dict.set(tagRendering.id, <QuestionableTagRenderingConfigJson> tagRendering)
}
return dict
@ -176,7 +176,7 @@ export default class DetermineLayout {
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) {
// We got fed a layer instead of a theme
const layerConfig = <LayerConfigJson>json
@ -224,15 +224,15 @@ export default class DetermineLayout {
knownLayersDict.set(layer.id, <LayerConfigJson>layer)
}
const convertState: DesugaringContext = {
tagRenderings: DetermineLayout.getSharedTagRenderings(),
tagRenderingOrder: DetermineLayout.getSharedTagRenderingOrder(),
tagRenderings: DetermineTheme.getSharedTagRenderings(),
tagRenderingOrder: DetermineTheme.getSharedTagRenderingOrder(),
sharedLayers: knownLayersDict,
publicLayers: new Set<string>(),
}
json = new FixLegacyTheme().convertStrict(json)
const raw = json
json = new FixImages(DetermineLayout._knownImages).convertStrict(json)
json = new FixImages(DetermineTheme._knownImages).convertStrict(json)
json.enableNoteImports = json.enableNoteImports ?? false
json = new PrepareTheme(convertState).convertStrict(json)
console.log("The layoutconfig is ", json)
@ -249,20 +249,20 @@ export default class DetermineLayout {
false
).convertStrict(json)
}
return new LayoutConfig(json, false, {
return new ThemeConfig(json, false, {
definitionRaw: JSON.stringify(raw, null, " "),
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)
new FixedUiElement(`Downloading the theme from the <a href="${link}">link</a>...`).AttachTo(
"maindiv"
)
let parsed = <LayoutConfigJson>await Utils.downloadJson(link)
let parsed = <ThemeConfigJson>await Utils.downloadJson(link)
let forcedId = parsed.id
const url = new URL(link)
if (!(url.hostname === "localhost" || url.hostname === "127.0.0.1")) {
@ -270,6 +270,6 @@ export default class DetermineLayout {
}
console.log("Loaded remote link:", link)
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) =>
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 { Feature, Point } from "geojson"
import { TagUtils } from "../../Tags/TagUtils"
@ -22,7 +22,7 @@ export class LastClickFeatureSource implements FeatureSource {
private _usermode: UIEventSource<string>
private _enabledAddMorePoints: UIEventSource<boolean>
constructor(
layout: LayoutConfig,
layout: ThemeConfig,
clickSource: Store<{ lon: number; lat: number; mode: "left" | "right" | "middle" }>,
usermode?: UIEventSource<string>,
enabledAddMorePoints?: UIEventSource<boolean>

View file

@ -18,7 +18,7 @@ import FeatureSourceMerger from "./FeatureSourceMerger"
*
* 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
*/
@ -51,7 +51,7 @@ export default class LayoutSource extends FeatureSourceMerger {
const src = new LocalStorageFeatureSource(
backend,
layer,
LayoutSource.fromCacheZoomLevel,
ThemeSource.fromCacheZoomLevel,
mapProperties,
{
isActive: isDisplayed(layer.id),
@ -63,13 +63,13 @@ export default class LayoutSource extends FeatureSourceMerger {
}
const mvtSources: UpdatableFeatureSource[] = osmLayers
.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 nonMvtLayers = osmLayers.filter((l) => !mvtAvailableLayers.has(l.id))
const isLoading = new UIEventSource(false)
const osmApiSource = LayoutSource.setupOsmApiSource(
const osmApiSource = ThemeSource.setupOsmApiSource(
osmLayers,
bounds,
zoom,
@ -86,7 +86,7 @@ export default class LayoutSource extends FeatureSourceMerger {
nonMvtLayers.map((l) => l.id),
" 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)
supportsForceDownload.push(overpassSource)
}
@ -100,7 +100,7 @@ export default class LayoutSource extends FeatureSourceMerger {
osmApiSource?.isRunning?.addCallbackAndRun(() => setIsLoading())
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)
@ -198,7 +198,7 @@ export default class LayoutSource extends FeatureSourceMerger {
zoom,
bounds,
layers: osmLayers,
widenFactor: featureSwitches.layoutToUse.widenFactor,
widenFactor: 1.5,
overpassUrl: featureSwitches.overpassUrl,
overpassTimeout: featureSwitches.overpassTimeout,
overpassMaxZoom: featureSwitches.overpassMaxZoom,

View file

@ -2,7 +2,7 @@ import { ImageUploader, UploadResult } from "./ImageUploader"
import LinkImageAction from "../Osm/Actions/LinkImageAction"
import FeaturePropertiesStore from "../FeatureSource/Actors/FeaturePropertiesStore"
import { OsmId, OsmTags } from "../../Models/OsmFeature"
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"
import ThemeConfig from "../../Models/ThemeConfig/ThemeConfig"
import { Store, UIEventSource } from "../UIEventSource"
import { OsmConnection } from "../Osm/OsmConnection"
import { Changes } from "../Osm/Changes"
@ -17,7 +17,7 @@ import { GeoOperations } from "../GeoOperations"
export class ImageUploadManager {
private readonly _uploader: ImageUploader
private readonly _featureProperties: FeaturePropertiesStore
private readonly _layout: LayoutConfig
private readonly _theme: ThemeConfig
private readonly _indexedFeatures: IndexedFeatureSource
private readonly _gps: Store<GeolocationCoordinates | undefined>
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>
constructor(
layout: LayoutConfig,
layout: ThemeConfig,
uploader: ImageUploader,
featureProperties: FeaturePropertiesStore,
osmConnection: OsmConnection,
@ -42,7 +42,7 @@ export class ImageUploadManager {
) {
this._uploader = uploader
this._featureProperties = featureProperties
this._layout = layout
this._theme = layout
this._osmConnection = osmConnection
this._changes = changes
this._indexedFeatures = allFeatures
@ -131,7 +131,7 @@ export class ImageUploadManager {
const properties = this._featureProperties.getStore(featureId)
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",
})

View file

@ -3,7 +3,7 @@ import { ExtraFuncParams, ExtraFunctions, ExtraFuncType } from "./ExtraFunctions
import LayerConfig from "../Models/ThemeConfig/LayerConfig"
import { Feature } from "geojson"
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 { IndexedFeatureSource } from "./FeatureSource/FeatureSource"
import OsmObjectDownloader from "./Osm/OsmObjectDownloader"
@ -27,7 +27,7 @@ export default class MetaTagging {
>()
private state: {
readonly selectedElement: Store<Feature>
readonly layout: LayoutConfig
readonly theme: ThemeConfig
readonly osmObjectDownloader: OsmObjectDownloader
readonly perLayer: ReadonlyMap<string, GeoIndexedStoreForLayer>
readonly indexedFeatures: IndexedFeatureSource
@ -40,7 +40,7 @@ export default class MetaTagging {
constructor(state: {
readonly selectedElement: Store<Feature>
readonly layout: LayoutConfig
readonly theme: ThemeConfig
readonly osmObjectDownloader: OsmObjectDownloader
readonly perLayer: ReadonlyMap<string, GeoIndexedStoreForLayer>
readonly indexedFeatures: IndexedFeatureSource
@ -48,7 +48,7 @@ export default class MetaTagging {
}) {
this.state = 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) {
continue
}
@ -69,7 +69,7 @@ export default class MetaTagging {
features,
params,
layer,
state.layout,
state.theme,
state.osmObjectDownloader,
state.featureProperties
)
@ -115,7 +115,7 @@ export default class MetaTagging {
return
}
const state = this.state
const layer = state.layout.getMatchingLayer(feature.properties)
const layer = state.theme.getMatchingLayer(feature.properties)
if (!layer) {
return
}
@ -124,7 +124,7 @@ export default class MetaTagging {
[feature],
this.params,
layer,
state.layout,
state.theme,
state.osmObjectDownloader,
state.featureProperties,
{
@ -161,7 +161,7 @@ export default class MetaTagging {
features: Feature[],
params: ExtraFuncParams,
layer: LayerConfig,
layout: LayoutConfig,
theme: ThemeConfig,
osmObjectDownloader: OsmObjectDownloader,
featurePropertiesStores?: FeaturePropertiesStore,
options?: {
@ -190,7 +190,7 @@ export default class MetaTagging {
// The calculated functions - per layer - which add the new keys
// Calculated functions are defined by the layer
const layerFuncs = this.createRetaggingFunc(layer, ExtraFunctions.constructHelpers(params))
const state: MetataggingState = { layout, osmObjectDownloader }
const state: MetataggingState = { theme: theme, osmObjectDownloader }
let atLeastOneFeatureChanged = false
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(
"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 { TagUtils } from "../../Tags/TagUtils"
import { FeatureSource, IndexedFeatureSource } from "../../FeatureSource/FeatureSource"
import LayoutConfig from "../../../Models/ThemeConfig/LayoutConfig"
import ThemeConfig from "../../../Models/ThemeConfig/ThemeConfig"
import { Position } from "geojson"
import FullNodeDatabaseSource from "../../FeatureSource/TiledFeatureSource/FullNodeDatabaseSource"
@ -32,7 +32,7 @@ export default class CreateMultiPolygonWithPointReuseAction
outerRingCoordinates: Position[],
innerRingsCoordinates: Position[][],
state: {
layout: LayoutConfig
theme: ThemeConfig
changes: Changes
indexedFeatures: IndexedFeatureSource
fullNodeDatabase?: FullNodeDatabaseSource
@ -43,7 +43,7 @@ export default class CreateMultiPolygonWithPointReuseAction
super(null, true)
this._tags = [...tags, new Tag("type", "multipolygon")]
this.changeType = changeType
this.theme = state?.layout?.id ?? ""
this.theme = state?.theme?.id ?? ""
this.createOuterWay = new CreateWayWithPointReuseAction(
[],
<[number, number][]>outerRingCoordinates,
@ -55,7 +55,7 @@ export default class CreateMultiPolygonWithPointReuseAction
new CreateNewWayAction(
[],
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 CreateNewNodeAction from "./CreateNewNodeAction"
import CreateNewWayAction from "./CreateNewWayAction"
import LayoutConfig from "../../../Models/ThemeConfig/LayoutConfig"
import ThemeConfig from "../../../Models/ThemeConfig/ThemeConfig"
import FullNodeDatabaseSource from "../../FeatureSource/TiledFeatureSource/FullNodeDatabaseSource"
import { Position } from "geojson"
@ -69,7 +69,7 @@ export default class CreateWayWithPointReuseAction
*/
private readonly _coordinateInfo: CoordinateInfo[]
private readonly _state: {
layout: LayoutConfig
theme: ThemeConfig
changes: Changes
indexedFeatures: IndexedFeatureSource
fullNodeDatabase?: FullNodeDatabaseSource
@ -80,7 +80,7 @@ export default class CreateWayWithPointReuseAction
tags: Tag[],
coordinates: Position[],
state: {
layout: LayoutConfig
theme: ThemeConfig
changes: Changes
indexedFeatures: IndexedFeatureSource
fullNodeDatabase?: FullNodeDatabaseSource
@ -203,7 +203,7 @@ export default class CreateWayWithPointReuseAction
}
public async CreateChangeDescriptions(changes: Changes): Promise<ChangeDescription[]> {
const theme = this._state?.layout?.id
const theme = this._state?.theme?.id
const allChanges: ChangeDescription[] = []
const nodeIdsToUse: { lat: number; lon: number; nodeId?: number }[] = []
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 = `${
this.state.osmConnection?._oauth_config?.url ?? "https://api.openstreetmap.org"
}/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)
}
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 LayerConfig from "../../Models/ThemeConfig/LayerConfig"
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 }
@ -13,9 +13,9 @@ export type FilterSearchResult = { option: FilterConfigOption, filter: FilterCon
* Searches matching filters
*/
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
}
@ -30,7 +30,7 @@ export default class FilterSearch {
return query
}).filter(q => q.length > 0)
const possibleFilters: FilterSearchResult[] = []
for (const layer of this._state.layout.layers) {
for (const layer of this._state.theme.layers) {
if (!Array.isArray(layer.filters)) {
continue
}

View file

@ -1,17 +1,17 @@
import SearchUtils from "./SearchUtils"
import ThemeSearch from "./ThemeSearch"
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"
import ThemeConfig from "../../Models/ThemeConfig/ThemeConfig"
import { Utils } from "../../Utils"
export default class LayerSearch {
private readonly _layout: LayoutConfig
private readonly _theme: ThemeConfig
private readonly _layerWhitelist: Set<string>
constructor(layout: LayoutConfig) {
this._layout = layout
this._layerWhitelist = new Set(layout.layers
constructor(theme: ThemeConfig) {
this._theme = theme
this._layerWhitelist = new Set(theme.layers
.filter(l => l.isNormal())
.map(l => l.id))
}
@ -29,8 +29,7 @@ export default class LayerSearch {
continue
}
const keywords = ThemeSearch.officialThemes.layers[id]
const distance = Math.min(...queryParts.map(q => SearchUtils.scoreKeywords(q, keywords)))
result[id] = distance
result[id] = Math.min(...queryParts.map(q => SearchUtils.scoreKeywords(q, keywords)))
}
return result
}
@ -44,7 +43,7 @@ export default class LayerSearch {
const asList: ({ layer: LayerConfig, score: number })[] = []
for (const layer in scores) {
asList.push({
layer: this._layout.getLayer(layer),
layer: this._theme.getLayer(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 UserRelatedState from "../State/UserRelatedState"
import { Utils } from "../../Utils"
@ -10,7 +10,7 @@ import { OsmConnection } from "../Osm/OsmConnection"
type ThemeSearchScore = {
theme: MinimalLayoutInformation,
theme: MinimalThemeInformation,
lowest: number,
perLayer?: Record<string, number>,
other: number,
@ -20,10 +20,10 @@ type ThemeSearchScore = {
export default class ThemeSearch {
public static readonly officialThemes: {
themes: MinimalLayoutInformation[],
themes: MinimalThemeInformation[],
layers: Record<string, Record<string, string[]>>
} = <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 {
for (const th of ThemeSearch.officialThemes.themes ?? []) {
ThemeSearch.officialThemesById.set(th.id, th)
@ -33,17 +33,17 @@ export default class ThemeSearch {
private readonly _knownHiddenThemes: Store<Set<string>>
private readonly _layersToIgnore: string[]
private readonly _otherThemes: MinimalLayoutInformation[]
private readonly _otherThemes: MinimalThemeInformation[]
constructor(state: {osmConnection: OsmConnection, layout: LayoutConfig}) {
this._layersToIgnore = state.layout.layers.filter(l => l.isNormal()).map(l => l.id)
constructor(state: {osmConnection: OsmConnection, theme: ThemeConfig}) {
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._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) {
return []
}
@ -101,7 +101,7 @@ export default class ThemeSearch {
* @param ignoreLayers
* @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) {
return undefined
}
@ -147,13 +147,13 @@ export default class ThemeSearch {
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))
scored.sort((a, b) => a.lowest - b.lowest)
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)
.map(th => th.theme)
}

View file

@ -8,7 +8,7 @@ import { TagUtils } from "./Tags/TagUtils"
import { Feature, LineString } from "geojson"
import { OsmTags } from "../Models/OsmFeature"
import { UIEventSource } from "./UIEventSource"
import LayoutConfig from "../Models/ThemeConfig/LayoutConfig"
import ThemeConfig from "../Models/ThemeConfig/ThemeConfig"
import OsmObjectDownloader from "./Osm/OsmObjectDownloader"
import countryToCurrency from "country-to-currency"
@ -16,7 +16,7 @@ import countryToCurrency from "country-to-currency"
* All elements that are needed to perform metatagging
*/
export interface MetataggingState {
layout: LayoutConfig
theme: ThemeConfig
osmObjectDownloader: OsmObjectDownloader
}
@ -399,7 +399,7 @@ export default class SimpleMetaTaggers {
},
(feature, _, __, state) => {
const units = Utils.NoNull(
[].concat(...(state?.layout?.layers?.map((layer) => layer.units) ?? []))
[].concat(...(state?.theme?.layers?.map((layer) => layer.units) ?? []))
)
if (units.length == 0) {
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 { QueryParameters } from "../Web/QueryParameters"
import Constants from "../../Models/Constants"
@ -45,10 +45,6 @@ export class 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 featureSwitchSearch: UIEventSource<boolean>
@ -74,9 +70,8 @@ export default class FeatureSwitchState extends OsmConnectionFeatureSwitches {
public readonly featureSwitchMorePrivacy: UIEventSource<boolean>
public readonly featureSwitchLayerDefault: UIEventSource<boolean>
public constructor(layoutToUse?: LayoutConfig) {
public constructor(theme?: ThemeConfig) {
super()
this.layoutToUse = layoutToUse
const legacyRewrite: Record<string, string | string[]> = {
"fs-userbadge": "fs-enable-login",
@ -102,7 +97,7 @@ export default class FeatureSwitchState extends OsmConnectionFeatureSwitches {
this.featureSwitchEnableLogin = FeatureSwitchUtils.initSwitch(
"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."
)
{
@ -117,18 +112,18 @@ export default class FeatureSwitchState extends OsmConnectionFeatureSwitches {
this.featureSwitchSearch = FeatureSwitchUtils.initSwitch(
"fs-search",
layoutToUse?.enableSearch ?? true,
theme?.enableSearch ?? true,
"Disables/Enables the search bar"
)
this.featureSwitchBackgroundSelection = FeatureSwitchUtils.initSwitch(
"fs-background",
layoutToUse?.enableBackgroundLayerSelection ?? true,
theme?.enableBackgroundLayerSelection ?? true,
"Disables/Enables the background layer control where a user can enable e.g. aerial imagery"
)
this.featureSwitchFilter = FeatureSwitchUtils.initSwitch(
"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"
)
@ -149,17 +144,17 @@ export default class FeatureSwitchState extends OsmConnectionFeatureSwitches {
)
this.featureSwitchBackToThemeOverview = FeatureSwitchUtils.initSwitch(
"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"
)
this.featureSwitchShareScreen = FeatureSwitchUtils.initSwitch(
"fs-share-screen",
layoutToUse?.enableShareScreen ?? true,
theme?.enableShareScreen ?? true,
"Disables/Enables the 'Share-screen'-tab in the welcome message"
)
this.featureSwitchGeolocation = FeatureSwitchUtils.initSwitch(
"fs-geolocation",
layoutToUse?.enableGeolocation ?? true,
theme?.enableGeolocation ?? true,
"Disables/Enables the geolocation button"
)
@ -170,19 +165,19 @@ export default class FeatureSwitchState extends OsmConnectionFeatureSwitches {
)
this.featureSwitchShowAllQuestions = FeatureSwitchUtils.initSwitch(
"fs-all-questions",
layoutToUse?.enableShowAllQuestions ?? false,
theme?.enableShowAllQuestions ?? false,
"Always show all questions"
)
this.featureSwitchEnableExport = FeatureSwitchUtils.initSwitch(
"fs-export",
layoutToUse?.enableExportButton ?? true,
theme?.enableExportButton ?? true,
"Enable the export as GeoJSON and CSV button"
)
this.featureSwitchCache = FeatureSwitchUtils.initSwitch(
"fs-cache",
layoutToUse?.enableCache ?? true,
theme?.enableCache ?? true,
"Enable/disable caching from localStorage"
)
@ -209,13 +204,13 @@ export default class FeatureSwitchState extends OsmConnectionFeatureSwitches {
this.featureSwitchMorePrivacy = QueryParameters.GetBooleanQueryParameter(
"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."
)
this.overpassUrl = QueryParameters.GetQueryParameter(
"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"
).sync(
(param) => param?.split(","),
@ -226,7 +221,7 @@ export default class FeatureSwitchState extends OsmConnectionFeatureSwitches {
this.overpassTimeout = UIEventSource.asInt(
QueryParameters.GetQueryParameter(
"overpassTimeout",
"" + layoutToUse?.overpassTimeout,
"" + theme?.overpassTimeout,
"Set a different timeout (in seconds) for queries in overpass"
)
)
@ -234,7 +229,7 @@ export default class FeatureSwitchState extends OsmConnectionFeatureSwitches {
this.overpassMaxZoom = UIEventSource.asFloat(
QueryParameters.GetQueryParameter(
"overpassMaxZoom",
"" + layoutToUse?.overpassMaxZoom,
"" + theme?.overpassMaxZoom,
" point to switch between OSM-api and overpass"
)
)
@ -242,14 +237,14 @@ export default class FeatureSwitchState extends OsmConnectionFeatureSwitches {
this.osmApiTileSize = UIEventSource.asInt(
QueryParameters.GetQueryParameter(
"osmApiTileSize",
"" + layoutToUse?.osmApiTileSize,
"" + theme?.osmApiTileSize,
"Tilesize when the OSM-API is used to fetch data within a BBOX"
)
)
this.backgroundLayerId = QueryParameters.GetQueryParameter(
"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.",
"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 PhotonSearch from "../Search/PhotonSearch"
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 GeocodingFeatureSource from "../Search/GeocodingFeatureSource"
import LayerSearch from "../Search/LayerSearch"
@ -23,7 +23,7 @@ export default class SearchState {
public readonly searchIsFocused = new UIEventSource(false)
public readonly suggestions: Store<SearchResult[]>
public readonly filterSuggestions: Store<FilterSearchResult[]>
public readonly themeSuggestions: Store<MinimalLayoutInformation[]>
public readonly themeSuggestions: Store<MinimalThemeInformation[]>
public readonly layerSuggestions: Store<LayerConfig[]>
public readonly locationSearchers: ReadonlyArray<GeocodingProvider>
@ -64,7 +64,7 @@ export default class SearchState {
const themeSearch = new ThemeSearch(state)
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))
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 { MangroveIdentity } from "../Web/MangroveReviews"
import { Store, Stores, UIEventSource } from "../UIEventSource"
@ -183,7 +183,7 @@ export default class UserRelatedState {
constructor(
osmConnection: OsmConnection,
layout?: LayoutConfig,
layout?: ThemeConfig,
featureSwitches?: FeatureSwitchState,
mapProperties?: MapProperties,
) {
@ -277,14 +277,14 @@ export default class UserRelatedState {
*
* @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)
this.osmConnection.isLoggedIn.when(
() => 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 str = pref.data
@ -307,7 +307,7 @@ export default class UserRelatedState {
}
}
public markLayoutAsVisited(layout: LayoutConfig) {
public markLayoutAsVisited(layout: ThemeConfig) {
if (!layout) {
console.error("Trying to mark a layout as visited, but ", layout, " got passed")
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
* */
private initAmendedPrefs(
layout?: LayoutConfig,
layout?: ThemeConfig,
featureSwitches?: FeatureSwitchState,
): UIEventSource<Record<string, string>> {
const amendedPrefs = new UIEventSource<Record<string, string>>({

View file

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

View file

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

View file

@ -1,5 +1,5 @@
import { Conversion, DesugaringStep } from "./Conversion"
import { LayoutConfigJson } from "../Json/LayoutConfigJson"
import { ThemeConfigJson } from "../Json/ThemeConfigJson"
import { Utils } from "../../../Utils"
import metapaths from "../../../assets/schemas/layoutconfigmeta.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"
export class ExtractImages extends Conversion<
LayoutConfigJson,
ThemeConfigJson,
{ path: string; context: string }[]
> {
private static readonly layoutMetaPaths = metapaths.filter((mp) => {
@ -109,7 +109,7 @@ export class ExtractImages extends Conversion<
*
*/
convert(
json: LayoutConfigJson,
json: ThemeConfigJson,
context: ConversionContext
): { 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>
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].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
try {
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 LineRenderingConfigJson from "../Json/LineRenderingConfigJson"
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() {
super("Small fixes in the theme config", ["roamingRenderings"], "UpdateLegacyTheme")
}
convert(json: LayoutConfigJson, context: ConversionContext): LayoutConfigJson {
convert(json: ThemeConfigJson, context: ConversionContext): ThemeConfigJson {
const oldThemeConfig = { ...json }
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() {
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)",

View file

@ -1,5 +1,5 @@
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 { LayerConfigJson } from "../Json/LayerConfigJson"
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
constructor(state: DesugaringContext) {
@ -177,7 +177,7 @@ class AddDefaultLayers extends DesugaringStep<LayoutConfigJson> {
this._state = state
}
convert(json: LayoutConfigJson, context: ConversionContext): LayoutConfigJson {
convert(json: ThemeConfigJson, context: ConversionContext): ThemeConfigJson {
const state = this._state
json.layers = Utils.NoNull([...(json.layers ?? [])])
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() {
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",
@ -218,8 +218,8 @@ class AddContextToTranslationsInLayout extends DesugaringStep<LayoutConfigJson>
)
}
convert(json: LayoutConfigJson): LayoutConfigJson {
const conversion = new AddContextToTranslations<LayoutConfigJson>("themes:")
convert(json: ThemeConfigJson): ThemeConfigJson {
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
return conversion.convert(
json,
@ -228,7 +228,7 @@ class AddContextToTranslationsInLayout extends DesugaringStep<LayoutConfigJson>
}
}
class ApplyOverrideAll extends DesugaringStep<LayoutConfigJson> {
class ApplyOverrideAll extends DesugaringStep<ThemeConfigJson> {
constructor() {
super(
"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
if (overrideAll === undefined) {
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
constructor(state: DesugaringContext) {
@ -390,7 +390,7 @@ class AddDependencyLayersToTheme extends DesugaringStep<LayoutConfigJson> {
return dependenciesToAdd
}
convert(theme: LayoutConfigJson, context: ConversionContext): LayoutConfigJson {
convert(theme: ThemeConfigJson, context: ConversionContext): ThemeConfigJson {
const state = this._state
const allKnownLayers: Map<string, LayerConfigJson> = state.sharedLayers
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
constructor(state: DesugaringContext) {
@ -436,7 +436,7 @@ class PreparePersonalTheme extends DesugaringStep<LayoutConfigJson> {
this._state = state
}
convert(json: LayoutConfigJson, context: ConversionContext): LayoutConfigJson {
convert(json: ThemeConfigJson, context: ConversionContext): ThemeConfigJson {
if (json.id !== "personal") {
return json
}
@ -452,7 +452,7 @@ class PreparePersonalTheme extends DesugaringStep<LayoutConfigJson> {
}
}
class WarnForUnsubstitutedLayersInTheme extends DesugaringStep<LayoutConfigJson> {
class WarnForUnsubstitutedLayersInTheme extends DesugaringStep<ThemeConfigJson> {
constructor() {
super(
"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) {
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
constructor(state: DesugaringContext) {
@ -511,7 +511,7 @@ class PostvalidateTheme extends DesugaringStep<LayoutConfigJson> {
this._state = state
}
convert(json: LayoutConfigJson, context: ConversionContext): LayoutConfigJson {
convert(json: ThemeConfigJson, context: ConversionContext): ThemeConfigJson {
for (const l of json.layers) {
const layer = <LayerConfigJson>l
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
constructor(
@ -616,7 +616,7 @@ export class PrepareTheme extends Fuse<LayoutConfigJson> {
this.state = state
}
convert(json: LayoutConfigJson, context: ConversionContext): LayoutConfigJson {
convert(json: ThemeConfigJson, context: ConversionContext): ThemeConfigJson {
const result = super.convert(json, context)
if ((this.state.publicLayers?.size ?? 0) === 0) {
// THis is a bootstrapping run, no need to already set this flag

View file

@ -1,13 +1,13 @@
import { DesugaringStep } from "./Conversion"
import { LayoutConfigJson } from "../Json/LayoutConfigJson"
import { ThemeConfigJson } from "../Json/ThemeConfigJson"
import { AvailableRasterLayers } from "../../RasterLayers"
import { ExtractImages } from "./FixImages"
import { ConversionContext } from "./ConversionContext"
import LayoutConfig from "../LayoutConfig"
import ThemeConfig from "../ThemeConfig"
import { Utils } from "../../../Utils"
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
* @private
@ -33,8 +33,8 @@ export class ValidateTheme extends DesugaringStep<LayoutConfigJson> {
}
}
convert(json: LayoutConfigJson, context: ConversionContext): LayoutConfigJson {
const theme = new LayoutConfig(json, this._isBuiltin)
convert(json: ThemeConfigJson, context: ConversionContext): ThemeConfigJson {
const theme = new ThemeConfig(json, this._isBuiltin)
{
// Legacy format checks
if (this._isBuiltin) {

View file

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

View file

@ -4,8 +4,8 @@ import LayerConfig from "../LayerConfig"
import { Utils } from "../../../Utils"
import Constants from "../../Constants"
import { Translation } from "../../../UI/i18n/Translation"
import { LayoutConfigJson } from "../Json/LayoutConfigJson"
import LayoutConfig from "../LayoutConfig"
import { ThemeConfigJson } from "../Json/ThemeConfigJson"
import ThemeConfig from "../ThemeConfig"
import { TagRenderingConfigJson } from "../Json/TagRenderingConfigJson"
import { TagUtils } from "../../../Logic/Tags/TagUtils"
import { And } from "../../../Logic/Tags/And"
@ -23,7 +23,7 @@ import { PrevalidateLayer } from "./PrevalidateLayer"
import { AvailableRasterLayers } from "../../RasterLayers"
import { eliCategory } from "../../RasterLayerProperties"
export class ValidateLanguageCompleteness extends DesugaringStep<LayoutConfig> {
export class ValidateLanguageCompleteness extends DesugaringStep<ThemeConfig> {
private readonly _languages: string[]
constructor(...languages: string[]) {
@ -35,7 +35,7 @@ export class ValidateLanguageCompleteness extends DesugaringStep<LayoutConfig> {
this._languages = languages ?? ["en"]
}
convert(obj: LayoutConfig, context: ConversionContext): LayoutConfig {
convert(obj: ThemeConfig, context: ConversionContext): ThemeConfig {
const origLayers = obj.layers
obj.layers = [...obj.layers].filter((l) => l["id"] !== "favourite")
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() {
super(
"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
if (overrideAll === undefined) {
return json
@ -170,12 +170,12 @@ class OverrideShadowingCheck extends DesugaringStep<LayoutConfigJson> {
}
}
class MiscThemeChecks extends DesugaringStep<LayoutConfigJson> {
class MiscThemeChecks extends DesugaringStep<ThemeConfigJson> {
constructor() {
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)) {
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() {
super(
"Various consistency checks on the raw JSON",
@ -905,7 +905,7 @@ export class ValidateFilter extends DesugaringStep<FilterConfigJson> {
export class DetectDuplicateFilters extends DesugaringStep<{
layers: LayerConfigJson[]
themes: LayoutConfigJson[]
themes: ThemeConfigJson[]
}> {
constructor() {
super(
@ -916,15 +916,15 @@ export class DetectDuplicateFilters extends DesugaringStep<{
}
convert(
json: { layers: LayerConfigJson[]; themes: LayoutConfigJson[] },
json: { layers: LayerConfigJson[]; themes: ThemeConfigJson[] },
context: ConversionContext,
): { layers: LayerConfigJson[]; themes: LayoutConfigJson[] } {
): { layers: LayerConfigJson[]; themes: ThemeConfigJson[] } {
const { layers, themes } = json
const perOsmTag = new Map<
string,
{
layer: LayerConfigJson
layout: LayoutConfigJson | undefined
theme: ThemeConfigJson | undefined
filter: FilterConfigJson
}[]
>()
@ -955,10 +955,10 @@ export class DetectDuplicateFilters extends DesugaringStep<{
return
}
let msg = "Possible duplicate filter: " + key
for (const { filter, layer, layout } of value) {
for (const { filter, layer, theme } of value) {
let id = ""
if (layout !== undefined) {
id = layout.id + ":"
if (theme !== undefined) {
id = theme.id + ":"
}
msg += `\n - ${id}${layer.id}.${filter.id}`
}
@ -977,11 +977,11 @@ export class DetectDuplicateFilters extends DesugaringStep<{
string,
{
layer: LayerConfigJson
layout: LayoutConfigJson | undefined
theme: ThemeConfigJson | undefined
filter: FilterConfigJson
}[]
>,
layout?: LayoutConfigJson | undefined,
theme?: ThemeConfigJson | undefined,
): void {
if (layer.filter === undefined || layer.filter === null) {
return
@ -1009,14 +1009,14 @@ export class DetectDuplicateFilters extends DesugaringStep<{
perOsmTag.get(key).push({
layer,
filter,
layout,
theme,
})
}
}
}
}
export class DetectDuplicatePresets extends DesugaringStep<LayoutConfig> {
export class DetectDuplicatePresets extends DesugaringStep<ThemeConfig> {
constructor() {
super(
"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 enNames = presets.map((p) => p.title.textFor("en"))
@ -1074,7 +1074,7 @@ export class DetectDuplicatePresets extends DesugaringStep<LayoutConfig> {
}
export class ValidateThemeEnsemble extends Conversion<
LayoutConfig[],
ThemeConfig[],
Map<
string,
{
@ -1093,7 +1093,7 @@ export class ValidateThemeEnsemble extends Conversion<
}
convert(
json: LayoutConfig[],
json: ThemeConfig[],
context: ConversionContext,
): Map<
string,

View file

@ -62,7 +62,7 @@ export default class FilterConfig {
const fields: { name: string; type: ValidatorType }[] = (option.fields ?? []).map((f, i) => {
const type = <ValidatorType> f.type ?? "regex"
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
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.
*/
export interface LayoutConfigJson {
export interface ThemeConfigJson {
/**
* question: What is the id of this layout?
*

View file

@ -1,5 +1,5 @@
import { Translation } from "../../UI/i18n/Translation"
import { LayoutConfigJson } from "./Json/LayoutConfigJson"
import { ThemeConfigJson } from "./Json/ThemeConfigJson"
import LayerConfig from "./LayerConfig"
import { LayerConfigJson } from "./Json/LayerConfigJson"
import Constants from "../Constants"
@ -13,7 +13,7 @@ import { Translatable } from "./Json/Translatable"
/**
* Minimal information about a theme
**/
export class MinimalLayoutInformation {
export class MinimalThemeInformation {
id: string
icon: string
title: Translatable
@ -27,7 +27,7 @@ export class MinimalLayoutInformation {
/**
* Minimal information about a theme
**/
export class LayoutInformation {
export class ThemeInformation {
id: string
icon: string
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 readonly id: string
public readonly credits?: string
@ -57,7 +57,6 @@ export default class LayoutConfig implements LayoutInformation {
public readonly startZoom: number
public readonly startLat: number
public readonly startLon: number
public widenFactor: number
public defaultBackgroundId?: string
public layers: LayerConfig[]
public tileLayerSources: (RasterLayerProperties & { defaultState?: true | boolean })[]
@ -92,11 +91,11 @@ export default class LayoutConfig implements LayoutInformation {
public readonly definitionRaw?: string
private readonly layersDict: Map<string, LayerConfig>
private readonly source: LayoutConfigJson
private readonly source: ThemeConfigJson
public readonly enableCache: boolean
constructor(
json: LayoutConfigJson,
json: ThemeConfigJson,
official = true,
options?: {
definedAtUrl?: string
@ -167,7 +166,7 @@ export default class LayoutConfig implements LayoutInformation {
? undefined
: new Translation(json.descriptionTail, "themes:" + context + ".descriptionTail")
this.icon = json.icon
this.socialImage = json.socialImage ?? LayoutConfig.defaultSocialImage
this.socialImage = json.socialImage ?? ThemeConfig.defaultSocialImage
if (this.socialImage === "") {
if (official) {
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.startLat = json.startLat
this.startLon = json.startLon
this.widenFactor = 1.5
this.defaultBackgroundId = json.defaultBackgroundId
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 { Changes } from "../Logic/Osm/Changes"
import { Store, UIEventSource } from "../Logic/UIEventSource"
@ -18,7 +18,7 @@ import UserRelatedState from "../Logic/State/UserRelatedState"
import LayerConfig from "./ThemeConfig/LayerConfig"
import GeoLocationHandler from "../Logic/Actors/GeoLocationHandler"
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 FeaturePropertiesStore from "../Logic/FeatureSource/Actors/FeaturePropertiesStore"
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.
*/
export default class ThemeViewState implements SpecialVisualizationState {
readonly layout: LayoutConfig
readonly theme: ThemeConfig
readonly map: UIEventSource<MlMap>
readonly changes: Changes
readonly featureSwitches: FeatureSwitchState
@ -104,7 +104,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
readonly fullNodeDatabase?: FullNodeDatabaseSource
readonly historicalUserLocations: WritableFeatureSource<Feature<Point>>
readonly indexedFeatures: IndexedFeatureSource & LayoutSource
readonly indexedFeatures: IndexedFeatureSource & ThemeSource
readonly currentView: FeatureSource<Feature<Polygon>>
readonly featuresInView: FeatureSource
readonly favourites: FavouritesFeatureSource
@ -160,9 +160,9 @@ export default class ThemeViewState implements SpecialVisualizationState {
*/
public readonly featureSummary: SummaryTileSourceRewriter
constructor(layout: LayoutConfig, mvtAvailableLayers: Set<string>) {
constructor(layout: ThemeConfig, mvtAvailableLayers: Set<string>) {
Utils.initDomPurify()
this.layout = layout
this.theme = layout
this.featureSwitches = new FeatureSwitchState(layout)
this.guistate = new MenuState(
this.featureSwitches.featureSwitchWelcomeMessage.data,
@ -218,7 +218,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
{
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(
"overlay-" + rasterInfo.id,
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
*/
if (this.layout.layers.some((l) => l._needsFullNodeDatabase)) {
if (this.theme.layers.some((l) => l._needsFullNodeDatabase)) {
this.fullNodeDatabase = new FullNodeDatabaseSource()
}
const layoutSource = new LayoutSource(
const layoutSource = new ThemeSource(
layout.layers,
this.featureSwitches,
this.mapProperties,
@ -340,7 +340,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
})
this.lastClickObject = new LastClickFeatureSource(
this.layout,
this.theme,
this.mapProperties.lastClickLocation,
this.userRelatedState.addNewFeatureMode,
)
@ -414,7 +414,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
const storage = new SaveFeatureSourceToLocalStorage(
this.osmConnection.Backend(),
fs.layer.layerDef.id,
LayoutSource.fromCacheZoomLevel,
ThemeSource.fromCacheZoomLevel,
fs,
this.featureProperties,
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) => {
if (selected === undefined) {
@ -517,8 +517,8 @@ export default class ThemeViewState implements SpecialVisualizationState {
}
})
if (this.layout.customCss !== undefined && window.location.pathname.indexOf("theme") >= 0) {
Utils.LoadCustomCss(this.layout.customCss)
if (this.theme.customCss !== undefined && window.location.pathname.indexOf("theme") >= 0) {
Utils.LoadCustomCss(this.theme.customCss)
}
Hash.hash.addCallbackAndRunD((hash) => {
@ -738,11 +738,11 @@ export default class ThemeViewState implements SpecialVisualizationState {
/**
* 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 layers = this.layout.layers.filter(
const layers = this.theme.layers.filter(
(l) =>
Constants.priviliged_layers.indexOf(<any>l.id) < 0 &&
l.source.geojsonSource === undefined &&
@ -796,8 +796,8 @@ export default class ThemeViewState implements SpecialVisualizationState {
this.closestFeatures.registerSource(specialLayers.favourite, "favourite")
if (this.layout?.lockLocation) {
const bbox = new BBox(<any>this.layout.lockLocation)
if (this.theme?.lockLocation) {
const bbox = new BBox(<any>this.theme.lockLocation)
this.mapProperties.maxbounds.setData(bbox)
ShowDataLayer.showRange(
this.map,
@ -805,7 +805,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
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) {
const params = MetaTagging.createExtraFuncParams(this)
this.featureProperties.trackFeatureSource(specialLayers.current_view)
@ -814,7 +814,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
features,
params,
currentViewLayer,
this.layout,
this.theme,
this.osmObjectDownloader,
this.featureProperties,
)
@ -909,9 +909,9 @@ export default class ThemeViewState implements SpecialVisualizationState {
*/
private initActors() {
if (!this.layout.official) {
if (!this.theme.official) {
// Add custom themes to the "visited custom themes"
const th = this.layout
const th = this.theme
this.userRelatedState.addUnofficialTheme({
id: th.id,
icon: th.icon,
@ -945,7 +945,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
this.selectedElement.addCallbackD(selected => {
const [osm_type, osm_id] = selected.properties.id.split("/")
const [lon, lat] = GeoOperations.centerpointCoordinates(selected)
const layer = this.layout.getMatchingLayer(selected.properties)
const layer = this.theme.getMatchingLayer(selected.properties)
const nameOptions = [
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>) {
@ -1002,15 +1002,15 @@ export default class ThemeViewState implements SpecialVisualizationState {
return UserRelatedState.usersettingsConfig
}
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")) {
return GeocodingUtils.searchLayer
}
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 = "") {
@ -1059,7 +1059,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
body: JSON.stringify({
stacktrace,
message: "" + message,
layout: this.layout.id,
theme: this.theme.id,
version: Constants.vNumber,
language: this.userRelatedState.language.data,
username: this.osmConnection.userDetails.data?.name,

View file

@ -12,7 +12,7 @@
import Constants from "../Models/Constants"
import { ImmutableStore, Store, Stores, UIEventSource } from "../Logic/UIEventSource"
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 LoginButton from "./Base/LoginButton.svelte"
import Mastodon from "../assets/svg/Mastodon.svelte"
@ -46,16 +46,16 @@
let searchIsFocused = new UIEventSource(true)
const officialThemes: MinimalLayoutInformation[] = ThemeSearch.officialThemes.themes.filter(th => th.hideFromOverview === false)
const hiddenThemes: MinimalLayoutInformation[] = ThemeSearch.officialThemes.themes.filter(th => th.hideFromOverview === true)
let visitedHiddenThemes: Store<MinimalLayoutInformation[]> = UserRelatedState.initDiscoveredHiddenThemes(state.osmConnection)
const officialThemes: MinimalThemeInformation[] = ThemeSearch.officialThemes.themes.filter(th => th.hideFromOverview === false)
const hiddenThemes: MinimalThemeInformation[] = ThemeSearch.officialThemes.themes.filter(th => th.hideFromOverview === true)
let visitedHiddenThemes: Store<MinimalThemeInformation[]> = UserRelatedState.initDiscoveredHiddenThemes(state.osmConnection)
.map((knownIds) => hiddenThemes.filter((theme) =>
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))))
function filtered(themes: Store<MinimalLayoutInformation[]>): Store<MinimalLayoutInformation[]> {
function filtered(themes: Store<MinimalThemeInformation[]>): Store<MinimalThemeInformation[]> {
return searchStable.map(search => {
if (!search) {
return themes.data
@ -74,9 +74,9 @@
}
let officialSearched : Store<MinimalLayoutInformation[]>= filtered(new ImmutableStore(officialThemes))
let hiddenSearched: Store<MinimalLayoutInformation[]> = filtered(visitedHiddenThemes)
let customSearched: Store<MinimalLayoutInformation[]> = filtered(customThemes)
let officialSearched : Store<MinimalThemeInformation[]>= filtered(new ImmutableStore(officialThemes))
let hiddenSearched: Store<MinimalThemeInformation[]> = filtered(visitedHiddenThemes)
let customSearched: Store<MinimalThemeInformation[]> = filtered(customThemes)
let searchIsFocussed = new UIEventSource(false)

View file

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

View file

@ -18,7 +18,7 @@
export let state: SpecialVisualizationState
const t = Translations.t.general.attribution
const layoutToUse = state.layout
const layoutToUse = state.theme
let maintainer: Translation = undefined
if (layoutToUse.credits !== undefined && layoutToUse.credits !== "") {
@ -122,7 +122,7 @@
{/if}
{#if maintainer !== undefined}
<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} />
</div>
{/if}

View file

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

View file

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

View file

@ -56,7 +56,7 @@
let usersettingslayer = new LayerConfig(<LayerConfigJson>usersettings, "usersettings", true)
let layout = state.layout
let theme = state.theme
let featureSwitches = state.featureSwitches
let showHome = featureSwitches.featureSwitchBackToThemeOverview
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 -->
<LoginToggle {state}>
<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} />
<LoginButton clss="primary" osmConnection={state.osmConnection} />
</div>
@ -161,12 +161,12 @@
<Page {onlyLink} shown={pg.about_theme}>
<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} />
</svelte:fragment>
<svelte:fragment slot="header">
<Marker size="h-8 w-8 mr-3" icons={layout.icon} />
<Tr t={layout.title} />
<Marker size="h-8 w-8 mr-3" icons={theme.icon} />
<Tr t={theme.title} />
</svelte:fragment>
<ThemeIntroPanel {state} />
</Page>
@ -193,25 +193,25 @@
</Page>
{/if}
{#if layout.official}
{#if theme.official}
<a
class="flex"
href={"https://github.com/pietervdvn/MapComplete/blob/develop/Docs/Themes/" +
layout.id +
theme.id +
".md"}
target="_blank"
>
<DocumentMagnifyingGlass class="h-6 w-6" />
<Tr
t={Translations.t.general.attribution.openThemeDocumentation.Subs({
name: layout.title,
name: theme.title,
})}
/>
</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" />
<Tr t={Translations.t.general.attribution.openOsmcha.Subs({ theme: layout.title })} />
<Tr t={Translations.t.general.attribution.openOsmcha.Subs({ theme: theme.title })} />
</a>
{/if}
</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?...`
* Note that the 'layout='-param will be included automatically
*/
let needsThemeRedirect = url.port !== "" || url.hostname.match(/^[0-9]/) || !state.layout.official
let layoutId = state.layout.id
let needsThemeRedirect = url.port !== "" || url.hostname.match(/^[0-9]/) || !state.theme.official
let layoutId = state.theme.id
let baseLink = `${url.protocol}//${url.host}/${needsThemeRedirect ? "theme.html" : layoutId}?`
let showWelcomeMessage = true
@ -44,7 +44,7 @@
enableBackground: boolean,
enableGeolocation: boolean
) {
const layout = state.layout
const layout = state.theme
let excluded = Utils.NoNull([
showWelcomeMessage ? undefined : "fs-welcome-message",
enableLogin ? undefined : "fs-enable-login",
@ -99,7 +99,7 @@
${
enableGeolocation ? 'allow="geolocation"' : ""
} 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>`
Array.from(state.layerState.filteredLayers.values()).forEach((flayer) =>
@ -163,7 +163,7 @@
<Tr t={tr.stateIsIncluded} />
<a
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} />
</a>

View file

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

View file

@ -1,12 +1,12 @@
<script lang="ts">
import { ImmutableStore, Store } from "../../Logic/UIEventSource"
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 Translations from "../i18n/Translations"
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://")
export let state: { layoutToUse?: { id: string }; osmConnection: OsmConnection }

View file

@ -14,7 +14,7 @@
* The theme introduction panel
*/
export let state: ThemeViewState
let layout = state.layout
let theme = state.theme
let geolocation = state.geolocation.geolocationState
let geopermission: Store<GeolocationPermissionState> = geolocation.permission
@ -42,16 +42,16 @@
<div>
<!-- Intro, description, ... -->
<Tr t={layout.description} />
<Tr t={theme.description} />
<If condition={state.featureSwitches.featureSwitchEnableLogin}>
<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} />
{/if}
</If>
<Tr t={layout.descriptionTail} />
<Tr t={theme.descriptionTail} />
<!-- Buttons: open map, go to location, search -->
<NextButton

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -156,7 +156,7 @@ export default class DownloadHelper {
private getCleanGeoJsonPerLayer(includeMetaData: boolean): Map<string, Feature[]> {
const state = this._state
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
for (const neededLayer of neededLayers) {
@ -186,7 +186,7 @@ export default class DownloadHelper {
createImage(key: string, width: string, height: string): HTMLImageElement {
const img = document.createElement("img")
const sources = {
layouticon: this._state.layout.icon,
layouticon: this._state.theme.icon,
}
img.src = sources[key]
if (!img.src) {

View file

@ -38,10 +38,10 @@
return downloadHelper.createImage(key, width, height)
},
textSubstitutions: <Record<string, string | Translation>>{
"layout.title": state.layout.title,
layoutid: state.layout.id,
title: state.layout.title,
layoutImg: state.layout.icon,
"layout.title": state.theme.title,
layoutid: state.theme.id,
title: state.theme.title,
layoutImg: state.theme.icon,
version: Constants.vNumber,
date: new Date().toISOString().substring(0, 16),
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 { Changes } from "../../Logic/Osm/Changes"
import { OsmConnection } from "../../Logic/Osm/OsmConnection"
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"
import ThemeConfig from "../../Models/ThemeConfig/ThemeConfig"
import SvelteUIElement from "../Base/SvelteUIElement"
import Delete_icon from "../../assets/svg/Delete_icon.svelte"
@ -14,7 +14,7 @@ export default class DeleteImage extends Toggle {
constructor(
key: string,
tags: Store<any>,
state: { layout: LayoutConfig; changes?: Changes; osmConnection?: OsmConnection }
state: { theme: ThemeConfig; changes?: Changes; osmConnection?: OsmConnection }
) {
const oldValue = tags.data[key]
const isDeletedBadge = Translations.t.image.isDeleted
@ -25,7 +25,7 @@ export default class DeleteImage extends Toggle {
await state?.changes?.applyAction(
new ChangeTagAction(tags.data.id, new Tag(key, oldValue), tags.data, {
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(
new ChangeTagAction(tags.data.id, new Tag(key, ""), tags.data, {
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 { OsmConnection } from "../../Logic/Osm/OsmConnection"
import { Changes } from "../../Logic/Osm/Changes"
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"
import ThemeConfig from "../../Models/ThemeConfig/ThemeConfig"
import SvelteUIElement from "../Base/SvelteUIElement"
import AttributedImage from "./AttributedImage.svelte"
@ -18,7 +18,7 @@ export class ImageCarousel extends Toggle {
state: {
osmConnection?: OsmConnection
changes?: Changes
layout: LayoutConfig
theme: ThemeConfig
previewedImage?: UIEventSource<ProvidedImage>
}
) {

View file

@ -45,7 +45,7 @@
const url = targetValue
if (isLinked) {
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"
})
await state.changes.applyAction(action)
@ -54,7 +54,7 @@
const v = currentTags[k]
if (v === url) {
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"
})
state.changes.applyAction(action)

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -65,7 +65,7 @@
featureId,
deleteConfig.softDeletionTags,
{
theme: state?.layout?.id ?? "unknown",
theme: state?.theme?.id ?? "unknown",
specialMotivation: deleteReason,
},
canBeDeleted.data
@ -73,7 +73,7 @@
} else {
// 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, {
theme: state?.layout?.id ?? "unkown",
theme: state?.theme?.id ?? "unkown",
changeType: "special-delete",
})
}

View file

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

View file

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

View file

@ -174,7 +174,7 @@ export default abstract class ImportFlow<ArgT extends ImportFlowArguments> {
return { error: t.hasBeenImported }
}
if (!state.layout.official && !isTesting) {
if (!state.theme.official && !isTesting) {
// Unofficial theme - imports not allowed
return {
error: t.officialThemesOnly,
@ -183,7 +183,7 @@ export default abstract class ImportFlow<ArgT extends ImportFlowArguments> {
}
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)
return { error: new Translation({ "*": e }) }
}

View file

@ -15,7 +15,7 @@
// The following variables are used for the map
const targetLayers: LayerConfig[] = args.targetLayer
.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 =
args.snap_onto_layers?.split(",")?.map((l) => l.trim()) ?? []
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, {
theme: this.state.layout.id,
theme: this.state.theme.id,
changeType: "import",
snapOnto: <OsmWay>snapOnto,
specialMotivation: specialMotivation,

View file

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

View file

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

View file

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

View file

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

View file

@ -19,7 +19,7 @@
const t = Translations.t.favouritePoi
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>

View file

@ -20,7 +20,7 @@
const t = Translations.t.favouritePoi
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>

View file

@ -72,7 +72,7 @@
ShowDataLayer.showMultipleLayers(
mlmap,
new StaticFeatureSource(featuresToShow),
state.layout.layers,
state.theme.layers,
{ zoomToFeatures: true }
)
</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 matchingPreset = matchingPresets.flatMap(pr => pr.preciseInput?.snapToLayers)
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)
reasons.push({
text,
@ -133,7 +133,7 @@ export class MoveWizardState {
snappedTo,
{
reason: reason.changesetCommentValue,
theme: state.layout.id,
theme: state.theme.id,
}),
)
featureToMove.properties._lat = loc.lat
@ -152,7 +152,7 @@ export class MoveWizardState {
featureToMove.properties,
{
changeType: "relocated",
theme: state.layout.id,
theme: state.theme.id,
},
),
)

View file

@ -64,7 +64,7 @@ class MultiApplyExecutor {
const keysToChange = this.params.keysToApply
const overwrite = this.params.overwrite
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) {
const tagsToApply: Tag[] = []
const otherFeatureTags = allElements.getStore(id).data

View file

@ -47,7 +47,7 @@
return
}
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)
console.log("Created a note, got id", id)
const feature = <Feature<Point, OsmTags>>{

View file

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

View file

@ -17,7 +17,7 @@
let [lon, lat] = GeoOperations.centerpointCoordinates(feature)
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 url = id.mapD(
(id) =>

View file

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

View file

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

View file

@ -144,7 +144,7 @@ export default class TagApplyButton implements AutoAction, SpecialVisualization
new And(tagsToApply.data),
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",
}
)

View file

@ -283,7 +283,7 @@
}
dispatch("saved", { config, applied: selectedTags })
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",
})
freeformInput.set(undefined)
@ -327,7 +327,7 @@
function clearAnswer() {
const tagsToSet = settableKeys.data.map(k => new Tag(k, ""))
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",
})
freeformInput.set(undefined)

View file

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

View file

@ -20,7 +20,7 @@
let tags: UIEventSource<Record<string, string>>
let descriptionTr: TagRenderingConfig = undefined
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)
descriptionTr = layer?.tagRenderings?.find(tr => tr.labels.indexOf("description") >= 0)
}

View file

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

View file

@ -14,7 +14,7 @@
export let state: SpecialVisualizationState
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
const t =Translations.t.general.search

View file

@ -1,6 +1,6 @@
import { Store, UIEventSource } from "../Logic/UIEventSource"
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 { OsmConnection } from "../Logic/Osm/OsmConnection"
import { Changes } from "../Logic/Osm/Changes"
@ -19,7 +19,7 @@ import FavouritesFeatureSource from "../Logic/FeatureSource/Sources/FavouritesFe
import { ProvidedImage } from "../Logic/ImageProviders/ImageProvider"
import GeoLocationHandler from "../Logic/Actors/GeoLocationHandler"
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 ShowDataLayer from "./Map/ShowDataLayer"
import { CombinedFetcher } from "../Logic/Web/NearbyImagesSearch"
@ -33,13 +33,13 @@ import FeaturePropertiesStore from "../Logic/FeatureSource/Actors/FeaturePropert
*/
export interface SpecialVisualizationState {
readonly guistate: MenuState
readonly layout: LayoutConfig
readonly theme: ThemeConfig
readonly featureSwitches: FeatureSwitchState
readonly layerState: LayerState
readonly featureProperties: FeaturePropertiesStore
readonly indexedFeatures: IndexedFeatureSource & LayoutSource
readonly indexedFeatures: IndexedFeatureSource & ThemeSource
/**
* Some features will create a new element that should be displayed.
* 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][] = []
for (const layerAndTagRenderingId of layerAndtagRenderingIds.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)
tagRenderings.push([layer, tagRendering])
}
@ -444,7 +444,7 @@ export default class SpecialVisualizations {
Locale.showLinkToWeblate.map((showTranslations) => {
const languages = showTranslations
? LanguageUtils.usedLanguagesSorted
: state.layout.language
: state.theme.language
return new SvelteUIElement(LanguagePicker, {
assignTo: state.userRelatedState.language,
availableLanguages: languages,
@ -971,7 +971,7 @@ export default class SpecialVisualizations {
return undefined
}
const allUnits: Unit[] = [].concat(
...(state?.layout?.layers?.map((lyr) => lyr.units) ?? [])
...(state?.theme?.layers?.map((lyr) => lyr.units) ?? [])
)
const unit = allUnits.filter((unit) =>
unit.isApplicableToKey(key)
@ -1125,7 +1125,7 @@ export default class SpecialVisualizations {
) =>
new VariableUiElement(
tagsSource.map((tags) => {
if (state.layout === undefined) {
if (state.theme === undefined) {
return "<feature title>"
}
const title = layer?.title?.GetRenderValue(tags)
@ -1276,7 +1276,7 @@ export default class SpecialVisualizations {
constr: (state) => {
return new Combine(
state.layout.layers
state.theme.layers
.filter(
(l) =>
l.name !== null &&
@ -1995,7 +1995,7 @@ export default class SpecialVisualizations {
layer: LayerConfig
): BaseUIElement {
const translation = tagSource.map((tags) => {
const layer = state.layout.getMatchingLayer(tags)
const layer = state.theme.getMatchingLayer(tags)
return layer?.getMostMatchingPreset(tags)?.description
})
return new VariableUiElement(translation)

View file

@ -11,7 +11,7 @@ import BaseUIElement from "./BaseUIElement"
import Title from "./Base/Title"
import { FixedUiElement } from "./Base/FixedUiElement"
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 SvelteUIElement from "./Base/SvelteUIElement"
import Filterview from "./BigComponents/Filterview.svelte"
@ -24,7 +24,7 @@ import { Feature } from "geojson"
class StatsticsForOverviewFile extends Combine {
constructor(homeUrl: string, paths: string[]) {
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 filterPanel = new Combine([
new Title("Filters"),

View file

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

View file

@ -2,7 +2,7 @@ import { Utils } from "../../Utils"
import Constants from "../../Models/Constants"
import { LayerConfigJson } from "../../Models/ThemeConfig/Json/LayerConfigJson"
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.
@ -71,12 +71,12 @@ export default class StudioServer {
}
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(
layerId: string,
category: "layers" | "themes",
uid?: number
): Promise<LayerConfigJson | LayoutConfigJson> {
): Promise<LayerConfigJson | ThemeConfigJson> {
try {
return <any>await Utils.downloadJson(this.urlFor(layerId, category, uid))
} catch (e) {

View file

@ -53,7 +53,7 @@
export let state: ThemeViewState
let layout = state.layout
let theme = state.theme
let maplibremap: UIEventSource<MlMap> = state.map
let state_selectedElement = state.selectedElement
let selectedElement: UIEventSource<Feature> = new UIEventSource<Feature>(undefined)
@ -65,7 +65,7 @@
let gpsButtonAriaLabel = state.geolocation.geolocationState.gpsStateExplanation
let debug = state.featureSwitches.featureSwitchIsDebugging
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 currentZoom = state.mapProperties.zoom
let showCrosshair = state.userRelatedState.showCrosshair
@ -213,7 +213,7 @@
<div class="flex w-full items-end justify-between px-4">
<div class="flex flex-col">
<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
class="low-interaction pointer-events-auto w-fit"
class:disabled={$currentZoom < Constants.minZoomLevelToAddNewPoint}
@ -224,7 +224,7 @@
>
{#if $currentZoom < Constants.minZoomLevelToAddNewPoint}
<Tr t={Translations.t.general.add.zoomInFurther} />
{:else if state.layout.hasPresets()}
{:else if state.theme.hasPresets()}
<Tr t={Translations.t.general.add.title} />
{:else}
<Tr t={Translations.t.notes.addAComment} />
@ -351,9 +351,9 @@
<div
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">
<Tr t={layout.title} />
<Tr t={theme.title} />
</b>
</div>
</MapControlButton>

View file

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

View file

@ -1,10 +1,10 @@
import { QueryParameters } from "./Logic/Web/QueryParameters"
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 l = window.location
if (layout !== "") {
if (theme !== "") {
if (window.location.host.startsWith("127.0.0.1")) {
window.location.replace(
l.protocol +
@ -13,12 +13,12 @@ if (layout !== "") {
"/theme.html" +
l.search +
"&layout=" +
layout +
theme +
l.hash
)
} else {
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 !== "") {

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