forked from MapComplete/MapComplete
		
	
		
			
				
	
	
		
			290 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			290 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import * as known_themes from "../assets/generated/known_layers_and_themes.json"
 | |
| import LayoutConfig from "../Models/ThemeConfig/LayoutConfig"
 | |
| import LayerConfig from "../Models/ThemeConfig/LayerConfig"
 | |
| import BaseUIElement from "../UI/BaseUIElement"
 | |
| import Combine from "../UI/Base/Combine"
 | |
| import Title from "../UI/Base/Title"
 | |
| import List from "../UI/Base/List"
 | |
| import DependencyCalculator from "../Models/ThemeConfig/DependencyCalculator"
 | |
| import Constants from "../Models/Constants"
 | |
| import { Utils } from "../Utils"
 | |
| import Link from "../UI/Base/Link"
 | |
| import { LayoutConfigJson } from "../Models/ThemeConfig/Json/LayoutConfigJson"
 | |
| import { LayerConfigJson } from "../Models/ThemeConfig/Json/LayerConfigJson"
 | |
| 
 | |
| export class AllKnownLayouts {
 | |
|     public static allKnownLayouts: Map<string, LayoutConfig> = AllKnownLayouts.AllLayouts()
 | |
|     public static layoutsList: LayoutConfig[] = AllKnownLayouts.GenerateOrderedList(
 | |
|         AllKnownLayouts.allKnownLayouts
 | |
|     )
 | |
|     // Must be below the list...
 | |
|     private static sharedLayers: Map<string, LayerConfig> = AllKnownLayouts.getSharedLayers()
 | |
| 
 | |
|     public static AllPublicLayers(options?: {
 | |
|         includeInlineLayers: true | boolean
 | |
|     }): LayerConfig[] {
 | |
|         const allLayers: LayerConfig[] = []
 | |
|         const seendIds = new Set<string>()
 | |
|         AllKnownLayouts.sharedLayers.forEach((layer, key) => {
 | |
|             seendIds.add(key)
 | |
|             allLayers.push(layer)
 | |
|         })
 | |
|         if (options?.includeInlineLayers ?? true) {
 | |
|             const publicLayouts = AllKnownLayouts.layoutsList.filter((l) => !l.hideFromOverview)
 | |
|             for (const layout of publicLayouts) {
 | |
|                 if (layout.hideFromOverview) {
 | |
|                     continue
 | |
|                 }
 | |
|                 for (const layer of layout.layers) {
 | |
|                     if (seendIds.has(layer.id)) {
 | |
|                         continue
 | |
|                     }
 | |
|                     seendIds.add(layer.id)
 | |
|                     allLayers.push(layer)
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         return allLayers
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Returns all themes which use the given layer, reverse sorted by minzoom. This sort maximizes the chances that the layer is prominently featured on the first theme
 | |
|      */
 | |
|     public static themesUsingLayer(id: string, publicOnly = true): LayoutConfig[] {
 | |
|         const themes = AllKnownLayouts.layoutsList
 | |
|             .filter((l) => !(publicOnly && l.hideFromOverview) && l.id !== "personal")
 | |
|             .map((theme) => ({
 | |
|                 theme,
 | |
|                 minzoom: theme.layers.find((layer) => layer.id === id)?.minzoom,
 | |
|             }))
 | |
|             .filter((obj) => obj.minzoom !== undefined)
 | |
|         themes.sort((th0, th1) => th1.minzoom - th0.minzoom)
 | |
|         return themes.map((th) => th.theme)
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Generates documentation for the layers.
 | |
|      * Inline layers are included (if the theme is public)
 | |
|      * @param callback
 | |
|      * @constructor
 | |
|      */
 | |
|     public static GenOverviewsForSingleLayer(
 | |
|         callback: (layer: LayerConfig, element: BaseUIElement, inlineSource: string) => void
 | |
|     ): void {
 | |
|         const allLayers: LayerConfig[] = Array.from(AllKnownLayouts.sharedLayers.values()).filter(
 | |
|             (layer) => Constants.priviliged_layers.indexOf(layer.id) < 0
 | |
|         )
 | |
|         const builtinLayerIds: Set<string> = new Set<string>()
 | |
|         allLayers.forEach((l) => builtinLayerIds.add(l.id))
 | |
|         const inlineLayers = new Map<string, string>()
 | |
| 
 | |
|         for (const layout of Array.from(AllKnownLayouts.allKnownLayouts.values())) {
 | |
|             if (layout.hideFromOverview) {
 | |
|                 continue
 | |
|             }
 | |
| 
 | |
|             for (const layer of layout.layers) {
 | |
|                 if (Constants.priviliged_layers.indexOf(layer.id) >= 0) {
 | |
|                     continue
 | |
|                 }
 | |
|                 if (builtinLayerIds.has(layer.id)) {
 | |
|                     continue
 | |
|                 }
 | |
|                 if (layer.source.geojsonSource !== undefined) {
 | |
|                     // Not an OSM-source
 | |
|                     continue
 | |
|                 }
 | |
|                 allLayers.push(layer)
 | |
|                 builtinLayerIds.add(layer.id)
 | |
|                 inlineLayers.set(layer.id, layout.id)
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         const themesPerLayer = new Map<string, string[]>()
 | |
| 
 | |
|         for (const layout of Array.from(AllKnownLayouts.allKnownLayouts.values())) {
 | |
|             if (layout.hideFromOverview) {
 | |
|                 continue
 | |
|             }
 | |
|             for (const layer of layout.layers) {
 | |
|                 if (!builtinLayerIds.has(layer.id)) {
 | |
|                     // This is an inline layer
 | |
|                     continue
 | |
|                 }
 | |
|                 if (!themesPerLayer.has(layer.id)) {
 | |
|                     themesPerLayer.set(layer.id, [])
 | |
|                 }
 | |
|                 themesPerLayer.get(layer.id).push(layout.id)
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         // Determine the cross-dependencies
 | |
|         const layerIsNeededBy: Map<string, string[]> = new Map<string, string[]>()
 | |
| 
 | |
|         for (const layer of allLayers) {
 | |
|             for (const dep of DependencyCalculator.getLayerDependencies(layer)) {
 | |
|                 const dependency = dep.neededLayer
 | |
|                 if (!layerIsNeededBy.has(dependency)) {
 | |
|                     layerIsNeededBy.set(dependency, [])
 | |
|                 }
 | |
|                 layerIsNeededBy.get(dependency).push(layer.id)
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         allLayers.forEach((layer) => {
 | |
|             const element = layer.GenerateDocumentation(
 | |
|                 themesPerLayer.get(layer.id),
 | |
|                 layerIsNeededBy,
 | |
|                 DependencyCalculator.getLayerDependencies(layer)
 | |
|             )
 | |
|             callback(layer, element, inlineLayers.get(layer.id))
 | |
|         })
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Generates the documentation for the layers overview page
 | |
|      * @constructor
 | |
|      */
 | |
|     public static GenLayerOverviewText(): BaseUIElement {
 | |
|         for (const id of Constants.priviliged_layers) {
 | |
|             if (!AllKnownLayouts.sharedLayers.has(id)) {
 | |
|                 throw "Priviliged layer definition not found: " + id
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         const allLayers: LayerConfig[] = Array.from(AllKnownLayouts.sharedLayers.values()).filter(
 | |
|             (layer) => Constants.priviliged_layers.indexOf(layer.id) < 0
 | |
|         )
 | |
| 
 | |
|         const builtinLayerIds: Set<string> = new Set<string>()
 | |
|         allLayers.forEach((l) => builtinLayerIds.add(l.id))
 | |
| 
 | |
|         const themesPerLayer = new Map<string, string[]>()
 | |
| 
 | |
|         for (const layout of Array.from(AllKnownLayouts.allKnownLayouts.values())) {
 | |
|             for (const layer of layout.layers) {
 | |
|                 if (!builtinLayerIds.has(layer.id)) {
 | |
|                     continue
 | |
|                 }
 | |
|                 if (!themesPerLayer.has(layer.id)) {
 | |
|                     themesPerLayer.set(layer.id, [])
 | |
|                 }
 | |
|                 themesPerLayer.get(layer.id).push(layout.id)
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         // Determine the cross-dependencies
 | |
|         const layerIsNeededBy: Map<string, string[]> = new Map<string, string[]>()
 | |
| 
 | |
|         for (const layer of allLayers) {
 | |
|             for (const dep of DependencyCalculator.getLayerDependencies(layer)) {
 | |
|                 const dependency = dep.neededLayer
 | |
|                 if (!layerIsNeededBy.has(dependency)) {
 | |
|                     layerIsNeededBy.set(dependency, [])
 | |
|                 }
 | |
|                 layerIsNeededBy.get(dependency).push(layer.id)
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         return new Combine([
 | |
|             new Title("Special and other useful layers", 1),
 | |
|             "MapComplete has a few data layers available in the theme which have special properties through builtin-hooks. Furthermore, there are some normal layers (which are built from normal Theme-config files) but are so general that they get a mention here.",
 | |
|             new Title("Priviliged layers", 1),
 | |
|             new List(Constants.priviliged_layers.map((id) => "[" + id + "](#" + id + ")")),
 | |
|             ...Constants.priviliged_layers
 | |
|                 .map((id) => AllKnownLayouts.sharedLayers.get(id))
 | |
|                 .map((l) =>
 | |
|                     l.GenerateDocumentation(
 | |
|                         themesPerLayer.get(l.id),
 | |
|                         layerIsNeededBy,
 | |
|                         DependencyCalculator.getLayerDependencies(l),
 | |
|                         Constants.added_by_default.indexOf(l.id) >= 0,
 | |
|                         Constants.no_include.indexOf(l.id) < 0
 | |
|                     )
 | |
|                 ),
 | |
|             new Title("Normal layers", 1),
 | |
|             "The following layers are included in MapComplete:",
 | |
|             new List(
 | |
|                 Array.from(AllKnownLayouts.sharedLayers.keys()).map(
 | |
|                     (id) => new Link(id, "./Layers/" + id + ".md")
 | |
|                 )
 | |
|             ),
 | |
|         ])
 | |
|     }
 | |
| 
 | |
|     public static GenerateDocumentationForTheme(theme: LayoutConfig): BaseUIElement {
 | |
|         return new Combine([
 | |
|             new Title(new Combine([theme.title, "(", theme.id + ")"]), 2),
 | |
|             theme.description,
 | |
|             "This theme contains the following layers:",
 | |
|             new List(
 | |
|                 theme.layers
 | |
|                     .filter((l) => !l.id.startsWith("note_import_"))
 | |
|                     .map((l) => new Link(l.id, "../Layers/" + l.id + ".md"))
 | |
|             ),
 | |
|             "Available languages:",
 | |
|             new List(theme.language.filter((ln) => ln !== "_context")),
 | |
|         ]).SetClass("flex flex-col")
 | |
|     }
 | |
| 
 | |
|     public static getSharedLayers(): Map<string, LayerConfig> {
 | |
|         const sharedLayers = new Map<string, LayerConfig>()
 | |
|         for (const layer of known_themes["layers"]) {
 | |
|             try {
 | |
|                 // @ts-ignore
 | |
|                 const parsed = new LayerConfig(layer, "shared_layers")
 | |
|                 sharedLayers.set(layer.id, parsed)
 | |
|             } catch (e) {
 | |
|                 if (!Utils.runningFromConsole) {
 | |
|                     console.error(
 | |
|                         "CRITICAL: Could not parse a layer configuration!",
 | |
|                         layer.id,
 | |
|                         " due to",
 | |
|                         e
 | |
|                     )
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         return sharedLayers
 | |
|     }
 | |
| 
 | |
|     public static getSharedLayersConfigs(): Map<string, LayerConfigJson> {
 | |
|         const sharedLayers = new Map<string, LayerConfigJson>()
 | |
|         for (const layer of known_themes["layers"]) {
 | |
|             // @ts-ignore
 | |
|             sharedLayers.set(layer.id, layer)
 | |
|         }
 | |
| 
 | |
|         return sharedLayers
 | |
|     }
 | |
| 
 | |
|     private static GenerateOrderedList(allKnownLayouts: Map<string, LayoutConfig>): LayoutConfig[] {
 | |
|         const list = []
 | |
|         allKnownLayouts.forEach((layout) => {
 | |
|             list.push(layout)
 | |
|         })
 | |
|         return list
 | |
|     }
 | |
| 
 | |
|     private static AllLayouts(): Map<string, LayoutConfig> {
 | |
|         const dict: Map<string, LayoutConfig> = new Map()
 | |
|         for (const layoutConfigJson of known_themes["themes"]) {
 | |
|             const layout = new LayoutConfig(<LayoutConfigJson>layoutConfigJson, true)
 | |
|             dict.set(layout.id, layout)
 | |
|             for (let i = 0; i < layout.layers.length; i++) {
 | |
|                 let layer = layout.layers[i]
 | |
|                 if (typeof layer === "string") {
 | |
|                     layer = AllKnownLayouts.sharedLayers.get(layer)
 | |
|                     layout.layers[i] = layer
 | |
|                     if (layer === undefined) {
 | |
|                         console.log("Defined layers are ", AllKnownLayouts.sharedLayers.keys())
 | |
|                         throw `Layer ${layer} was not found or defined - probably a type was made`
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         return dict
 | |
|     }
 | |
| }
 |