forked from MapComplete/MapComplete
		
	Fix generateLayerOverview, drop priviliged 'icons.json' from code
This commit is contained in:
		
							parent
							
								
									df3ca4cce3
								
							
						
					
					
						commit
						36aed99843
					
				
					 10 changed files with 267 additions and 167 deletions
				
			
		|  | @ -1,5 +1,4 @@ | ||||||
| import questions from "../assets/tagRenderings/questions.json" | import questions from "../assets/tagRenderings/questions.json" | ||||||
| import icons from "../assets/tagRenderings/icons.json" |  | ||||||
| import { Utils } from "../Utils" | import { Utils } from "../Utils" | ||||||
| import TagRenderingConfig from "../Models/ThemeConfig/TagRenderingConfig" | import TagRenderingConfig from "../Models/ThemeConfig/TagRenderingConfig" | ||||||
| import { TagRenderingConfigJson } from "../Models/ThemeConfig/Json/TagRenderingConfigJson" | import { TagRenderingConfigJson } from "../Models/ThemeConfig/Json/TagRenderingConfigJson" | ||||||
|  | @ -14,11 +13,9 @@ export default class SharedTagRenderings { | ||||||
|         SharedTagRenderings.generatedSharedFields() |         SharedTagRenderings.generatedSharedFields() | ||||||
|     public static SharedTagRenderingJson: Map<string, TagRenderingConfigJson> = |     public static SharedTagRenderingJson: Map<string, TagRenderingConfigJson> = | ||||||
|         SharedTagRenderings.generatedSharedFieldsJsons() |         SharedTagRenderings.generatedSharedFieldsJsons() | ||||||
|     public static SharedIcons: Map<string, TagRenderingConfig> = |  | ||||||
|         SharedTagRenderings.generatedSharedFields(true) |  | ||||||
| 
 | 
 | ||||||
|     private static generatedSharedFields(iconsOnly = false): Map<string, TagRenderingConfig> { |     private static generatedSharedFields(): Map<string, TagRenderingConfig> { | ||||||
|         const configJsons = SharedTagRenderings.generatedSharedFieldsJsons(iconsOnly) |         const configJsons = SharedTagRenderings.generatedSharedFieldsJsons() | ||||||
|         const d = new Map<string, TagRenderingConfig>() |         const d = new Map<string, TagRenderingConfig>() | ||||||
|         for (const key of Array.from(configJsons.keys())) { |         for (const key of Array.from(configJsons.keys())) { | ||||||
|             try { |             try { | ||||||
|  | @ -31,7 +28,7 @@ export default class SharedTagRenderings { | ||||||
|                     console.error( |                     console.error( | ||||||
|                         "BUG: could not parse", |                         "BUG: could not parse", | ||||||
|                         key, |                         key, | ||||||
|                         " from questions.json or icons.json - this error happened during the build step of the SharedTagRenderings", |                         " from questions.json - this error happened during the build step of the SharedTagRenderings", | ||||||
|                         e |                         e | ||||||
|                     ) |                     ) | ||||||
|                 } |                 } | ||||||
|  | @ -40,25 +37,15 @@ export default class SharedTagRenderings { | ||||||
|         return d |         return d | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private static generatedSharedFieldsJsons( |     private static generatedSharedFieldsJsons(): Map<string, TagRenderingConfigJson> { | ||||||
|         iconsOnly = false |  | ||||||
|     ): Map<string, TagRenderingConfigJson> { |  | ||||||
|         const dict = new Map<string, TagRenderingConfigJson>() |         const dict = new Map<string, TagRenderingConfigJson>() | ||||||
| 
 | 
 | ||||||
|         if (!iconsOnly) { |  | ||||||
|         for (const key in questions) { |         for (const key in questions) { | ||||||
|             if (key === "id") { |             if (key === "id") { | ||||||
|                 continue |                 continue | ||||||
|             } |             } | ||||||
|             dict.set(key, <TagRenderingConfigJson>questions[key]) |             dict.set(key, <TagRenderingConfigJson>questions[key]) | ||||||
|         } |         } | ||||||
|         } |  | ||||||
|         for (const key in icons) { |  | ||||||
|             if (key === "id") { |  | ||||||
|                 continue |  | ||||||
|             } |  | ||||||
|             dict.set(key, <TagRenderingConfigJson>icons[key]) |  | ||||||
|         } |  | ||||||
| 
 | 
 | ||||||
|         dict.forEach((value, key) => { |         dict.forEach((value, key) => { | ||||||
|             if (key === "id") { |             if (key === "id") { | ||||||
|  |  | ||||||
|  | @ -194,8 +194,7 @@ export default class DetermineLayout { | ||||||
|             let { errors } = new ValidateThemeAndLayers( |             let { errors } = new ValidateThemeAndLayers( | ||||||
|                 new DoesImageExist(new Set<string>(), (_) => true), |                 new DoesImageExist(new Set<string>(), (_) => true), | ||||||
|                 "", |                 "", | ||||||
|                 false, |                 false | ||||||
|                 SharedTagRenderings.SharedTagRendering |  | ||||||
|             ).convert(json, "validation") |             ).convert(json, "validation") | ||||||
|             if (errors.length > 0) { |             if (errors.length > 0) { | ||||||
|                 throw "Detected errors: " + errors.join("\n") |                 throw "Detected errors: " + errors.join("\n") | ||||||
|  |  | ||||||
|  | @ -5,9 +5,12 @@ import metapaths from "../../../assets/layoutconfigmeta.json" | ||||||
| import tagrenderingmetapaths from "../../../assets/questionabletagrenderingconfigmeta.json" | import tagrenderingmetapaths from "../../../assets/questionabletagrenderingconfigmeta.json" | ||||||
| import Translations from "../../../UI/i18n/Translations" | import Translations from "../../../UI/i18n/Translations" | ||||||
| 
 | 
 | ||||||
| export class ExtractImages extends Conversion<LayoutConfigJson, string[]> { | export class ExtractImages extends Conversion< | ||||||
|  |     LayoutConfigJson, | ||||||
|  |     { path: string; context: string }[] | ||||||
|  | > { | ||||||
|     private _isOfficial: boolean |     private _isOfficial: boolean | ||||||
|     private _sharedTagRenderings: Map<string, any> |     private _sharedTagRenderings: Set<string> | ||||||
| 
 | 
 | ||||||
|     private static readonly layoutMetaPaths = metapaths.filter( |     private static readonly layoutMetaPaths = metapaths.filter( | ||||||
|         (mp) => |         (mp) => | ||||||
|  | @ -16,7 +19,7 @@ export class ExtractImages extends Conversion<LayoutConfigJson, string[]> { | ||||||
|     ) |     ) | ||||||
|     private static readonly tagRenderingMetaPaths = tagrenderingmetapaths |     private static readonly tagRenderingMetaPaths = tagrenderingmetapaths | ||||||
| 
 | 
 | ||||||
|     constructor(isOfficial: boolean, sharedTagRenderings: Map<string, any>) { |     constructor(isOfficial: boolean, sharedTagRenderings: Set<string>) { | ||||||
|         super("Extract all images from a layoutConfig using the meta paths.", [], "ExctractImages") |         super("Extract all images from a layoutConfig using the meta paths.", [], "ExctractImages") | ||||||
|         this._isOfficial = isOfficial |         this._isOfficial = isOfficial | ||||||
|         this._sharedTagRenderings = sharedTagRenderings |         this._sharedTagRenderings = sharedTagRenderings | ||||||
|  | @ -79,8 +82,8 @@ export class ExtractImages extends Conversion<LayoutConfigJson, string[]> { | ||||||
|     convert( |     convert( | ||||||
|         json: LayoutConfigJson, |         json: LayoutConfigJson, | ||||||
|         context: string |         context: string | ||||||
|     ): { result: string[]; errors: string[]; warnings: string[] } { |     ): { result: { path: string; context: string }[]; errors: string[]; warnings: string[] } { | ||||||
|         const allFoundImages: string[] = [] |         const allFoundImages: { path: string; context: string }[] = [] | ||||||
|         const errors = [] |         const errors = [] | ||||||
|         const warnings = [] |         const warnings = [] | ||||||
|         for (const metapath of ExtractImages.layoutMetaPaths) { |         for (const metapath of ExtractImages.layoutMetaPaths) { | ||||||
|  | @ -108,7 +111,7 @@ export class ExtractImages extends Conversion<LayoutConfigJson, string[]> { | ||||||
|                             continue |                             continue | ||||||
|                         } |                         } | ||||||
| 
 | 
 | ||||||
|                         allFoundImages.push(foundImage) |                         allFoundImages.push({ path: foundImage, context: context + "." + path }) | ||||||
|                     } else { |                     } else { | ||||||
|                         // This is a tagRendering.
 |                         // This is a tagRendering.
 | ||||||
|                         // Either every rendered value might be an icon
 |                         // Either every rendered value might be an icon
 | ||||||
|  | @ -137,7 +140,10 @@ export class ExtractImages extends Conversion<LayoutConfigJson, string[]> { | ||||||
|                                                 JSON.stringify(img.leaf) |                                                 JSON.stringify(img.leaf) | ||||||
|                                         ) |                                         ) | ||||||
|                                     } else { |                                     } else { | ||||||
|                                         allFoundImages.push(img.leaf) |                                         allFoundImages.push({ | ||||||
|  |                                             path: img.leaf, | ||||||
|  |                                             context: context + "." + path, | ||||||
|  |                                         }) | ||||||
|                                     } |                                     } | ||||||
|                                 } |                                 } | ||||||
|                                 if (!allRenderedValuesAreImages && isImage) { |                                 if (!allRenderedValuesAreImages && isImage) { | ||||||
|  | @ -146,7 +152,12 @@ export class ExtractImages extends Conversion<LayoutConfigJson, string[]> { | ||||||
|                                         ...Translations.T( |                                         ...Translations.T( | ||||||
|                                             img.leaf, |                                             img.leaf, | ||||||
|                                             "extract_images from " + img.path.join(".") |                                             "extract_images from " + img.path.join(".") | ||||||
|                                         ).ExtractImages(false) |                                         ) | ||||||
|  |                                             .ExtractImages(false) | ||||||
|  |                                             .map((path) => ({ | ||||||
|  |                                                 path, | ||||||
|  |                                                 context: context + "." + path, | ||||||
|  |                                             })) | ||||||
|                                     ) |                                     ) | ||||||
|                                 } |                                 } | ||||||
|                             } |                             } | ||||||
|  | @ -166,15 +177,19 @@ export class ExtractImages extends Conversion<LayoutConfigJson, string[]> { | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         const splitParts = [] |         const cleanedImages: { path: string; context: string }[] = [] | ||||||
|             .concat( | 
 | ||||||
|                 ...Utils.NoNull(allFoundImages) |         for (const foundImage of allFoundImages) { | ||||||
|                     .map((img) => img["path"] ?? img) |             // Split "circle:white;./assets/layers/.../something.svg" into ["circle", "./assets/layers/.../something.svg"]
 | ||||||
|                     .map((img) => img.split(";")) |             const allPaths = Utils.NoNull( | ||||||
|  |                 Utils.NoEmpty(foundImage.path?.split(";")?.map((part) => part.split(":")[0])) | ||||||
|             ) |             ) | ||||||
|             .map((img) => img.split(":")[0]) |             for (const path of allPaths) { | ||||||
|             .filter((img) => img !== "") |                 cleanedImages.push({ path, context: foundImage.context }) | ||||||
|         return { result: Utils.Dedup(splitParts), errors, warnings } |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return { result: cleanedImages, errors, warnings } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -7,29 +7,24 @@ import { | ||||||
|     FirstOf, |     FirstOf, | ||||||
|     Fuse, |     Fuse, | ||||||
|     On, |     On, | ||||||
|     SetDefault, |     SetDefault | ||||||
| } from "./Conversion" | } from "./Conversion"; | ||||||
| import { LayerConfigJson } from "../Json/LayerConfigJson" | import { LayerConfigJson } from "../Json/LayerConfigJson"; | ||||||
| import { TagRenderingConfigJson } from "../Json/TagRenderingConfigJson" | import { TagRenderingConfigJson } from "../Json/TagRenderingConfigJson"; | ||||||
| import { Utils } from "../../../Utils" | import { Utils } from "../../../Utils"; | ||||||
| import RewritableConfigJson from "../Json/RewritableConfigJson" | import RewritableConfigJson from "../Json/RewritableConfigJson"; | ||||||
| import SpecialVisualizations from "../../../UI/SpecialVisualizations" | import SpecialVisualizations from "../../../UI/SpecialVisualizations"; | ||||||
| import Translations from "../../../UI/i18n/Translations" | import Translations from "../../../UI/i18n/Translations"; | ||||||
| import { Translation } from "../../../UI/i18n/Translation" | import { Translation } from "../../../UI/i18n/Translation"; | ||||||
| import tagrenderingconfigmeta from "../../../assets/tagrenderingconfigmeta.json" | import tagrenderingconfigmeta from "../../../assets/tagrenderingconfigmeta.json"; | ||||||
| import { AddContextToTranslations } from "./AddContextToTranslations" | import { AddContextToTranslations } from "./AddContextToTranslations"; | ||||||
| import FilterConfigJson from "../Json/FilterConfigJson" | import FilterConfigJson from "../Json/FilterConfigJson"; | ||||||
| import predifined_filters from "../../../assets/layers/filters/filters.json" | import predifined_filters from "../../../assets/layers/filters/filters.json"; | ||||||
|  | import { TagConfigJson } from "../Json/TagConfigJson"; | ||||||
|  | import PointRenderingConfigJson from "../Json/PointRenderingConfigJson"; | ||||||
|  | import LineRenderingConfigJson from "../Json/LineRenderingConfigJson"; | ||||||
| 
 | 
 | ||||||
| class ExpandFilter extends DesugaringStep<LayerConfigJson> { | class ExpandFilter extends DesugaringStep<LayerConfigJson> { | ||||||
|     private static load_filters(): Map<string, FilterConfigJson> { |  | ||||||
|         let filters = new Map<string, FilterConfigJson>() |  | ||||||
|         for (const filter of <FilterConfigJson[]>predifined_filters.filter) { |  | ||||||
|             filters.set(filter.id, filter) |  | ||||||
|         } |  | ||||||
|         return filters |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private static readonly predefinedFilters = ExpandFilter.load_filters() |     private static readonly predefinedFilters = ExpandFilter.load_filters() | ||||||
| 
 | 
 | ||||||
|     constructor() { |     constructor() { | ||||||
|  | @ -40,6 +35,14 @@ class ExpandFilter extends DesugaringStep<LayerConfigJson> { | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     private static load_filters(): Map<string, FilterConfigJson> { | ||||||
|  |         let filters = new Map<string, FilterConfigJson>() | ||||||
|  |         for (const filter of <FilterConfigJson[]>predifined_filters.filter) { | ||||||
|  |             filters.set(filter.id, filter) | ||||||
|  |         } | ||||||
|  |         return filters | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     convert( |     convert( | ||||||
|         json: LayerConfigJson, |         json: LayerConfigJson, | ||||||
|         context: string |         context: string | ||||||
|  | @ -128,6 +131,37 @@ class ExpandTagRendering extends Conversion< | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private lookup(name: string): TagRenderingConfigJson[] { |     private lookup(name: string): TagRenderingConfigJson[] { | ||||||
|  |         const direct = this.directLookup(name) | ||||||
|  |         if (direct === undefined) { | ||||||
|  |             return undefined | ||||||
|  |         } | ||||||
|  |         const result: TagRenderingConfigJson[] = [] | ||||||
|  |         for (const tagRenderingConfigJson of direct) { | ||||||
|  |             if (tagRenderingConfigJson["builtin"] !== undefined) { | ||||||
|  |                 let nm: string | string[] = tagRenderingConfigJson["builtin"] | ||||||
|  |                 let indirect: TagRenderingConfigJson[] | ||||||
|  |                 if (typeof nm === "string") { | ||||||
|  |                     indirect = this.lookup(nm) | ||||||
|  |                 } else { | ||||||
|  |                     indirect = [].concat(...nm.map((n) => this.lookup(n))) | ||||||
|  |                 } | ||||||
|  |                 for (let foundTr of indirect) { | ||||||
|  |                     foundTr = Utils.Clone<any>(foundTr) | ||||||
|  |                     Utils.Merge(tagRenderingConfigJson["override"] ?? {}, foundTr) | ||||||
|  |                     foundTr.id = tagRenderingConfigJson.id ?? foundTr.id | ||||||
|  |                     result.push(foundTr) | ||||||
|  |                 } | ||||||
|  |             } else { | ||||||
|  |                 result.push(tagRenderingConfigJson) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return result | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Looks up a tagRendering based on the name. | ||||||
|  |      */ | ||||||
|  |     private directLookup(name: string): TagRenderingConfigJson[] { | ||||||
|         const state = this._state |         const state = this._state | ||||||
|         if (state.tagRenderings.has(name)) { |         if (state.tagRenderings.has(name)) { | ||||||
|             return [state.tagRenderings.get(name)] |             return [state.tagRenderings.get(name)] | ||||||
|  | @ -747,6 +781,79 @@ export class RewriteSpecial extends DesugaringStep<TagRenderingConfigJson> { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | class ExpandIconBadges extends DesugaringStep<PointRenderingConfigJson | LineRenderingConfigJson> { | ||||||
|  |     private _state: DesugaringContext | ||||||
|  |     private _layer: LayerConfigJson | ||||||
|  |     private _expand: ExpandTagRendering | ||||||
|  | 
 | ||||||
|  |     constructor(state: DesugaringContext, layer: LayerConfigJson) { | ||||||
|  |         super("Expands shorthand properties on iconBadges", ["iconBadges"], "ExpandIconBadges") | ||||||
|  |         this._state = state | ||||||
|  |         this._layer = layer | ||||||
|  |         this._expand = new ExpandTagRendering(state, layer) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     convert( | ||||||
|  |         json: PointRenderingConfigJson | LineRenderingConfigJson, | ||||||
|  |         context: string | ||||||
|  |     ): { | ||||||
|  |         result: PointRenderingConfigJson | LineRenderingConfigJson | ||||||
|  |         errors?: string[] | ||||||
|  |         warnings?: string[] | ||||||
|  |         information?: string[] | ||||||
|  |     } { | ||||||
|  |         if (!json["iconBadges"]) { | ||||||
|  |             return { result: json } | ||||||
|  |         } | ||||||
|  |         const badgesJson = (<PointRenderingConfigJson>json).iconBadges | ||||||
|  | 
 | ||||||
|  |         const iconBadges: { if: TagConfigJson; then: string | TagRenderingConfigJson }[] = [] | ||||||
|  | 
 | ||||||
|  |         const errs: string[] = [] | ||||||
|  |         const warns: string[] = [] | ||||||
|  |         for (let i = 0; i < badgesJson.length; i++) { | ||||||
|  |             const iconBadge: { if: TagConfigJson; then: string | TagRenderingConfigJson } = | ||||||
|  |                 badgesJson[i] | ||||||
|  |             const { errors, result, warnings } = this._expand.convert( | ||||||
|  |                 iconBadge.then, | ||||||
|  |                 context + ".iconBadges[" + i + "]" | ||||||
|  |             ) | ||||||
|  |             errs.push(...errors) | ||||||
|  |             warns.push(...warnings) | ||||||
|  |             if (result === undefined) { | ||||||
|  |                 iconBadges.push(iconBadge) | ||||||
|  |                 continue | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             iconBadges.push( | ||||||
|  |                 ...result.map((resolved) => ({ | ||||||
|  |                     if: iconBadge.if, | ||||||
|  |                     then: resolved, | ||||||
|  |                 })) | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return { | ||||||
|  |             result: { ...json, iconBadges }, | ||||||
|  |             errors: errs, | ||||||
|  |             warnings: warns, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class PreparePointRendering extends Fuse<PointRenderingConfigJson | LineRenderingConfigJson> { | ||||||
|  |     constructor(state: DesugaringContext, layer: LayerConfigJson) { | ||||||
|  |         super( | ||||||
|  |             "Prepares point renderings by expanding 'icon' and 'iconBadges'", | ||||||
|  |             new On( | ||||||
|  |                 "icon", | ||||||
|  |                 new FirstOf(new ExpandTagRendering(state, layer, { applyCondition: false })) | ||||||
|  |             ), | ||||||
|  |             new ExpandIconBadges(state, layer) | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| export class PrepareLayer extends Fuse<LayerConfigJson> { | export class PrepareLayer extends Fuse<LayerConfigJson> { | ||||||
|     constructor(state: DesugaringContext) { |     constructor(state: DesugaringContext) { | ||||||
|         super( |         super( | ||||||
|  | @ -755,19 +862,11 @@ export class PrepareLayer extends Fuse<LayerConfigJson> { | ||||||
|             new On("tagRenderings", new Concat(new ExpandRewrite()).andThenF(Utils.Flatten)), |             new On("tagRenderings", new Concat(new ExpandRewrite()).andThenF(Utils.Flatten)), | ||||||
|             new On("tagRenderings", (layer) => new Concat(new ExpandTagRendering(state, layer))), |             new On("tagRenderings", (layer) => new Concat(new ExpandTagRendering(state, layer))), | ||||||
|             new On("mapRendering", new Concat(new ExpandRewrite()).andThenF(Utils.Flatten)), |             new On("mapRendering", new Concat(new ExpandRewrite()).andThenF(Utils.Flatten)), | ||||||
|             new On( |             new On<(PointRenderingConfigJson | LineRenderingConfigJson)[], LayerConfigJson>( | ||||||
|                 "mapRendering", |                 "mapRendering", | ||||||
|                 (layer) => |                 (layer) => new Each(new PreparePointRendering(state, layer)) | ||||||
|                     new Each( |  | ||||||
|                         new On( |  | ||||||
|                             "icon", |  | ||||||
|                             new FirstOf( |  | ||||||
|                                 new ExpandTagRendering(state, layer, { applyCondition: false }) |  | ||||||
|                             ) |  | ||||||
|                         ) |  | ||||||
|                     ) |  | ||||||
|             ), |             ), | ||||||
|             new SetDefault("titleIcons", ["defaults"]), |             new SetDefault("titleIcons", ["icons.defaults"]), | ||||||
|             new On("titleIcons", (layer) => new Concat(new ExpandTagRendering(state, layer))), |             new On("titleIcons", (layer) => new Concat(new ExpandTagRendering(state, layer))), | ||||||
|             new ExpandFilter() |             new ExpandFilter() | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|  | @ -59,13 +59,16 @@ class ValidateLanguageCompleteness extends DesugaringStep<any> { | ||||||
| 
 | 
 | ||||||
| export class DoesImageExist extends DesugaringStep<string> { | export class DoesImageExist extends DesugaringStep<string> { | ||||||
|     private readonly _knownImagePaths: Set<string> |     private readonly _knownImagePaths: Set<string> | ||||||
|  |     private readonly _ignore?: Set<string> | ||||||
|     private readonly doesPathExist: (path: string) => boolean = undefined |     private readonly doesPathExist: (path: string) => boolean = undefined | ||||||
| 
 | 
 | ||||||
|     constructor( |     constructor( | ||||||
|         knownImagePaths: Set<string>, |         knownImagePaths: Set<string>, | ||||||
|         checkExistsSync: (path: string) => boolean = undefined |         checkExistsSync: (path: string) => boolean = undefined, | ||||||
|  |         ignore?: Set<string> | ||||||
|     ) { |     ) { | ||||||
|         super("Checks if an image exists", [], "DoesImageExist") |         super("Checks if an image exists", [], "DoesImageExist") | ||||||
|  |         this._ignore = ignore | ||||||
|         this._knownImagePaths = knownImagePaths |         this._knownImagePaths = knownImagePaths | ||||||
|         this.doesPathExist = checkExistsSync |         this.doesPathExist = checkExistsSync | ||||||
|     } |     } | ||||||
|  | @ -74,6 +77,10 @@ export class DoesImageExist extends DesugaringStep<string> { | ||||||
|         image: string, |         image: string, | ||||||
|         context: string |         context: string | ||||||
|     ): { result: string; errors?: string[]; warnings?: string[]; information?: string[] } { |     ): { result: string; errors?: string[]; warnings?: string[]; information?: string[] } { | ||||||
|  |         if (this._ignore?.has(image)) { | ||||||
|  |             return { result: image } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         const errors = [] |         const errors = [] | ||||||
|         const warnings = [] |         const warnings = [] | ||||||
|         const information = [] |         const information = [] | ||||||
|  | @ -123,20 +130,23 @@ class ValidateTheme extends DesugaringStep<LayoutConfigJson> { | ||||||
|      */ |      */ | ||||||
|     private readonly _path?: string |     private readonly _path?: string | ||||||
|     private readonly _isBuiltin: boolean |     private readonly _isBuiltin: boolean | ||||||
|     private _sharedTagRenderings: Map<string, any> |     //private readonly _sharedTagRenderings: Map<string, any>
 | ||||||
|     private readonly _validateImage: DesugaringStep<string> |     private readonly _validateImage: DesugaringStep<string> | ||||||
|  |     private readonly _extractImages: ExtractImages = undefined | ||||||
| 
 | 
 | ||||||
|     constructor( |     constructor( | ||||||
|         doesImageExist: DoesImageExist, |         doesImageExist: DoesImageExist, | ||||||
|         path: string, |         path: string, | ||||||
|         isBuiltin: boolean, |         isBuiltin: boolean, | ||||||
|         sharedTagRenderings: Map<string, any> |         sharedTagRenderings?: Set<string> | ||||||
|     ) { |     ) { | ||||||
|         super("Doesn't change anything, but emits warnings and errors", [], "ValidateTheme") |         super("Doesn't change anything, but emits warnings and errors", [], "ValidateTheme") | ||||||
|         this._validateImage = doesImageExist |         this._validateImage = doesImageExist | ||||||
|         this._path = path |         this._path = path | ||||||
|         this._isBuiltin = isBuiltin |         this._isBuiltin = isBuiltin | ||||||
|         this._sharedTagRenderings = sharedTagRenderings |         if (sharedTagRenderings) { | ||||||
|  |             this._extractImages = new ExtractImages(this._isBuiltin, sharedTagRenderings) | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     convert( |     convert( | ||||||
|  | @ -168,13 +178,10 @@ class ValidateTheme extends DesugaringStep<LayoutConfigJson> { | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         if (this._isBuiltin) { |         if (this._isBuiltin && this._extractImages !== undefined) { | ||||||
|             // Check images: are they local, are the licenses there, is the theme icon square, ...
 |             // Check images: are they local, are the licenses there, is the theme icon square, ...
 | ||||||
|             const images = new ExtractImages( |             const images = this._extractImages.convertStrict(json, "validation") | ||||||
|                 this._isBuiltin, |             const remoteImages = images.filter((img) => img.path.indexOf("http") == 0) | ||||||
|                 this._sharedTagRenderings |  | ||||||
|             ).convertStrict(json, "validation") |  | ||||||
|             const remoteImages = images.filter((img) => img.indexOf("http") == 0) |  | ||||||
|             for (const remoteImage of remoteImages) { |             for (const remoteImage of remoteImages) { | ||||||
|                 errors.push( |                 errors.push( | ||||||
|                     "Found a remote image: " + |                     "Found a remote image: " + | ||||||
|  | @ -186,8 +193,8 @@ class ValidateTheme extends DesugaringStep<LayoutConfigJson> { | ||||||
|             } |             } | ||||||
|             for (const image of images) { |             for (const image of images) { | ||||||
|                 this._validateImage.convertJoin( |                 this._validateImage.convertJoin( | ||||||
|                     image, |                     image.path, | ||||||
|                     context === undefined ? "" : ` in a layer defined in the theme ${context}`, |                     context === undefined ? "" : ` in the theme ${context} at ${image.context}`, | ||||||
|                     errors, |                     errors, | ||||||
|                     warnings, |                     warnings, | ||||||
|                     information |                     information | ||||||
|  | @ -267,7 +274,7 @@ export class ValidateThemeAndLayers extends Fuse<LayoutConfigJson> { | ||||||
|         doesImageExist: DoesImageExist, |         doesImageExist: DoesImageExist, | ||||||
|         path: string, |         path: string, | ||||||
|         isBuiltin: boolean, |         isBuiltin: boolean, | ||||||
|         sharedTagRenderings: Map<string, any> |         sharedTagRenderings?: Set<string> | ||||||
|     ) { |     ) { | ||||||
|         super( |         super( | ||||||
|             "Validates a theme and the contained layers", |             "Validates a theme and the contained layers", | ||||||
|  | @ -878,53 +885,6 @@ export class DetectDuplicateFilters extends DesugaringStep<{ | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |  | ||||||
|      * Add all filter options into 'perOsmTag' |  | ||||||
|      */ |  | ||||||
|     private addLayerFilters( |  | ||||||
|         layer: LayerConfigJson, |  | ||||||
|         perOsmTag: Map< |  | ||||||
|             string, |  | ||||||
|             { |  | ||||||
|                 layer: LayerConfigJson |  | ||||||
|                 layout: LayoutConfigJson | undefined |  | ||||||
|                 filter: FilterConfigJson |  | ||||||
|             }[] |  | ||||||
|         >, |  | ||||||
|         layout?: LayoutConfigJson | undefined |  | ||||||
|     ): void { |  | ||||||
|         if (layer.filter === undefined || layer.filter === null) { |  | ||||||
|             return |  | ||||||
|         } |  | ||||||
|         if (layer.filter["sameAs"] !== undefined) { |  | ||||||
|             return |  | ||||||
|         } |  | ||||||
|         for (const filter of <(string | FilterConfigJson)[]>layer.filter) { |  | ||||||
|             if (typeof filter === "string") { |  | ||||||
|                 continue |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             if (filter["#"]?.indexOf("ignore-possible-duplicate") >= 0) { |  | ||||||
|                 continue |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             for (const option of filter.options) { |  | ||||||
|                 if (option.osmTags === undefined) { |  | ||||||
|                     continue |  | ||||||
|                 } |  | ||||||
|                 const key = JSON.stringify(option.osmTags) |  | ||||||
|                 if (!perOsmTag.has(key)) { |  | ||||||
|                     perOsmTag.set(key, []) |  | ||||||
|                 } |  | ||||||
|                 perOsmTag.get(key).push({ |  | ||||||
|                     layer, |  | ||||||
|                     filter, |  | ||||||
|                     layout, |  | ||||||
|                 }) |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     convert( |     convert( | ||||||
|         json: { layers: LayerConfigJson[]; themes: LayoutConfigJson[] }, |         json: { layers: LayerConfigJson[]; themes: LayoutConfigJson[] }, | ||||||
|         context: string |         context: string | ||||||
|  | @ -991,4 +951,51 @@ export class DetectDuplicateFilters extends DesugaringStep<{ | ||||||
|             information, |             information, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Add all filter options into 'perOsmTag' | ||||||
|  |      */ | ||||||
|  |     private addLayerFilters( | ||||||
|  |         layer: LayerConfigJson, | ||||||
|  |         perOsmTag: Map< | ||||||
|  |             string, | ||||||
|  |             { | ||||||
|  |                 layer: LayerConfigJson | ||||||
|  |                 layout: LayoutConfigJson | undefined | ||||||
|  |                 filter: FilterConfigJson | ||||||
|  |             }[] | ||||||
|  |         >, | ||||||
|  |         layout?: LayoutConfigJson | undefined | ||||||
|  |     ): void { | ||||||
|  |         if (layer.filter === undefined || layer.filter === null) { | ||||||
|  |             return | ||||||
|  |         } | ||||||
|  |         if (layer.filter["sameAs"] !== undefined) { | ||||||
|  |             return | ||||||
|  |         } | ||||||
|  |         for (const filter of <(string | FilterConfigJson)[]>layer.filter) { | ||||||
|  |             if (typeof filter === "string") { | ||||||
|  |                 continue | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (filter["#"]?.indexOf("ignore-possible-duplicate") >= 0) { | ||||||
|  |                 continue | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             for (const option of filter.options) { | ||||||
|  |                 if (option.osmTags === undefined) { | ||||||
|  |                     continue | ||||||
|  |                 } | ||||||
|  |                 const key = JSON.stringify(option.osmTags) | ||||||
|  |                 if (!perOsmTag.has(key)) { | ||||||
|  |                     perOsmTag.set(key, []) | ||||||
|  |                 } | ||||||
|  |                 perOsmTag.get(key).push({ | ||||||
|  |                     layer, | ||||||
|  |                     filter, | ||||||
|  |                     layout, | ||||||
|  |                 }) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -82,10 +82,12 @@ export default class LayoutConfig { | ||||||
|         this.credits = json.credits |         this.credits = json.credits | ||||||
|         this.language = json.mustHaveLanguage ?? Object.keys(json.title) |         this.language = json.mustHaveLanguage ?? Object.keys(json.title) | ||||||
|         this.usedImages = Array.from( |         this.usedImages = Array.from( | ||||||
|             new ExtractImages(official, undefined).convertStrict( |             new ExtractImages(official, undefined) | ||||||
|  |                 .convertStrict( | ||||||
|                     json, |                     json, | ||||||
|                     "while extracting the images of " + json.id + " " + context ?? "" |                     "while extracting the images of " + json.id + " " + context ?? "" | ||||||
|                 ) |                 ) | ||||||
|  |                 .map((i) => i.path) | ||||||
|         ).sort() |         ).sort() | ||||||
|         { |         { | ||||||
|             if (typeof json.title === "string") { |             if (typeof json.title === "string") { | ||||||
|  |  | ||||||
|  | @ -1,7 +1,6 @@ | ||||||
| import PointRenderingConfigJson from "./Json/PointRenderingConfigJson" | import PointRenderingConfigJson from "./Json/PointRenderingConfigJson" | ||||||
| import TagRenderingConfig from "./TagRenderingConfig" | import TagRenderingConfig from "./TagRenderingConfig" | ||||||
| import { TagsFilter } from "../../Logic/Tags/TagsFilter" | import { TagsFilter } from "../../Logic/Tags/TagsFilter" | ||||||
| import SharedTagRenderings from "../../Customizations/SharedTagRenderings" |  | ||||||
| import { TagUtils } from "../../Logic/Tags/TagUtils" | import { TagUtils } from "../../Logic/Tags/TagUtils" | ||||||
| import { Utils } from "../../Utils" | import { Utils } from "../../Utils" | ||||||
| import Svg from "../../Svg" | import Svg from "../../Svg" | ||||||
|  | @ -72,18 +71,9 @@ export default class PointRenderingConfig extends WithContextLoader { | ||||||
|         } |         } | ||||||
|         this.cssClasses = this.tr("cssClasses", undefined) |         this.cssClasses = this.tr("cssClasses", undefined) | ||||||
|         this.iconBadges = (json.iconBadges ?? []).map((overlay, i) => { |         this.iconBadges = (json.iconBadges ?? []).map((overlay, i) => { | ||||||
|             let tr: TagRenderingConfig |  | ||||||
|             if ( |  | ||||||
|                 typeof overlay.then === "string" && |  | ||||||
|                 SharedTagRenderings.SharedIcons.get(overlay.then) !== undefined |  | ||||||
|             ) { |  | ||||||
|                 tr = SharedTagRenderings.SharedIcons.get(overlay.then) |  | ||||||
|             } else { |  | ||||||
|                 tr = new TagRenderingConfig(overlay.then, `iconBadges.${i}`) |  | ||||||
|             } |  | ||||||
|             return { |             return { | ||||||
|                 if: TagUtils.Tag(overlay.if), |                 if: TagUtils.Tag(overlay.if), | ||||||
|                 then: tr, |                 then: new TagRenderingConfig(overlay.then, `iconBadges.${i}`), | ||||||
|             } |             } | ||||||
|         }) |         }) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -37,7 +37,7 @@ | ||||||
|     "generate:service-worker": "tsc service-worker.ts && git_hash=$(git rev-parse HEAD) && sed -i'.bkp' \"s/GITHUB-COMMIT/$git_hash/\" service-worker.js && rm service-worker.js.bkp", |     "generate:service-worker": "tsc service-worker.ts && git_hash=$(git rev-parse HEAD) && sed -i'.bkp' \"s/GITHUB-COMMIT/$git_hash/\" service-worker.js && rm service-worker.js.bkp", | ||||||
|     "optimize-images": "cd assets/generated/ &&  find -name '*.png' -exec optipng '{}' \\; && echo 'PNGs are optimized'", |     "optimize-images": "cd assets/generated/ &&  find -name '*.png' -exec optipng '{}' \\; && echo 'PNGs are optimized'", | ||||||
|     "generate:stats": "vite-node scripts/GenerateSeries.ts", |     "generate:stats": "vite-node scripts/GenerateSeries.ts", | ||||||
|     "reset:layeroverview": "echo {\\\"layers\\\":[], \\\"themes\\\":[]} > ./assets/generated/known_layers_and_themes.json && echo {\\\"layers\\\": []} > ./assets/generated/known_layers.json && rm -f ./assets/generated/layers/*.json && rm -f ./assets/generated/themes/*.json && npm run generate:layeroverview && vite-node scripts/generateLayerOverview.ts --force", |     "reset:layeroverview": "echo {\\\"layers\\\":[], \\\"themes\\\":[]} > ./assets/generated/known_layers_and_themes.json && echo {\\\"layers\\\": []} > ./assets/generated/known_layers.json && rm -f ./assets/generated/layers/*.json && rm -f ./assets/generated/themes/*.json && npm run generate:layeroverview && vite-node scripts/generateLayerOverview.ts -- --force", | ||||||
|     "generate": "mkdir -p ./assets/generated; npm run generate:licenses; npm run generate:images; npm run generate:charging-stations; npm run generate:translations; npm run reset:layeroverview; npm run generate:service-worker", |     "generate": "mkdir -p ./assets/generated; npm run generate:licenses; npm run generate:images; npm run generate:charging-stations; npm run generate:translations; npm run reset:layeroverview; npm run generate:service-worker", | ||||||
|     "generate:charging-stations": "cd ./assets/layers/charging_station && vite-node csvToJson.ts && cd -", |     "generate:charging-stations": "cd ./assets/layers/charging_station && vite-node csvToJson.ts && cd -", | ||||||
|     "prepare-deploy": "npm run generate:service-worker && ./scripts/build.sh", |     "prepare-deploy": "npm run generate:service-worker && ./scripts/build.sh", | ||||||
|  |  | ||||||
|  | @ -15,7 +15,6 @@ import { | ||||||
| import { Translation } from "../UI/i18n/Translation" | import { Translation } from "../UI/i18n/Translation" | ||||||
| import { TagRenderingConfigJson } from "../Models/ThemeConfig/Json/TagRenderingConfigJson" | import { TagRenderingConfigJson } from "../Models/ThemeConfig/Json/TagRenderingConfigJson" | ||||||
| import questions from "../assets/tagRenderings/questions.json" | import questions from "../assets/tagRenderings/questions.json" | ||||||
| import icons from "../assets/tagRenderings/icons.json" |  | ||||||
| import PointRenderingConfigJson from "../Models/ThemeConfig/Json/PointRenderingConfigJson" | import PointRenderingConfigJson from "../Models/ThemeConfig/Json/PointRenderingConfigJson" | ||||||
| import { PrepareLayer } from "../Models/ThemeConfig/Conversion/PrepareLayer" | import { PrepareLayer } from "../Models/ThemeConfig/Conversion/PrepareLayer" | ||||||
| import { PrepareTheme } from "../Models/ThemeConfig/Conversion/PrepareTheme" | import { PrepareTheme } from "../Models/ThemeConfig/Conversion/PrepareTheme" | ||||||
|  | @ -167,21 +166,6 @@ class LayerOverviewUtils { | ||||||
|             ) |             ) | ||||||
|             dict.set(key, config) |             dict.set(key, config) | ||||||
|         } |         } | ||||||
|         for (const key in icons) { |  | ||||||
|             if (key === "id") { |  | ||||||
|                 continue |  | ||||||
|             } |  | ||||||
|             if (typeof icons[key] !== "object") { |  | ||||||
|                 continue |  | ||||||
|             } |  | ||||||
|             icons[key].id = key |  | ||||||
|             const config = <TagRenderingConfigJson>icons[key] |  | ||||||
|             validator.convertStrict( |  | ||||||
|                 config, |  | ||||||
|                 "generate-layer-overview:tagRenderings/icons.json:" + key |  | ||||||
|             ) |  | ||||||
|             dict.set(key, config) |  | ||||||
|         } |  | ||||||
| 
 | 
 | ||||||
|         dict.forEach((value, key) => { |         dict.forEach((value, key) => { | ||||||
|             if (key === "id") { |             if (key === "id") { | ||||||
|  | @ -251,7 +235,7 @@ class LayerOverviewUtils { | ||||||
|         const sharedLayers = this.buildLayerIndex(doesImageExist, forceReload) |         const sharedLayers = this.buildLayerIndex(doesImageExist, forceReload) | ||||||
|         const recompiledThemes: string[] = [] |         const recompiledThemes: string[] = [] | ||||||
|         const sharedThemes = this.buildThemeIndex( |         const sharedThemes = this.buildThemeIndex( | ||||||
|             doesImageExist, |             licensePaths, | ||||||
|             sharedLayers, |             sharedLayers, | ||||||
|             recompiledThemes, |             recompiledThemes, | ||||||
|             forceReload |             forceReload | ||||||
|  | @ -381,7 +365,7 @@ class LayerOverviewUtils { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private buildThemeIndex( |     private buildThemeIndex( | ||||||
|         doesImageExist: DoesImageExist, |         licensePaths: Set<string>, | ||||||
|         sharedLayers: Map<string, LayerConfigJson>, |         sharedLayers: Map<string, LayerConfigJson>, | ||||||
|         recompiledThemes: string[], |         recompiledThemes: string[], | ||||||
|         forceReload: boolean |         forceReload: boolean | ||||||
|  | @ -396,9 +380,26 @@ class LayerOverviewUtils { | ||||||
| 
 | 
 | ||||||
|         const convertState: DesugaringContext = { |         const convertState: DesugaringContext = { | ||||||
|             sharedLayers, |             sharedLayers, | ||||||
|             tagRenderings: this.getSharedTagRenderings(doesImageExist), |             tagRenderings: this.getSharedTagRenderings( | ||||||
|  |                 new DoesImageExist(licensePaths, existsSync) | ||||||
|  |             ), | ||||||
|             publicLayers, |             publicLayers, | ||||||
|         } |         } | ||||||
|  |         const knownTagRenderings = new Set<string>() | ||||||
|  |         convertState.tagRenderings.forEach((_, key) => knownTagRenderings.add(key)) | ||||||
|  |         sharedLayers.forEach((layer) => { | ||||||
|  |             for (const tagRendering of layer.tagRenderings ?? []) { | ||||||
|  |                 if (tagRendering["id"]) { | ||||||
|  |                     knownTagRenderings.add(layer.id + "." + tagRendering["id"]) | ||||||
|  |                 } | ||||||
|  |                 if (tagRendering["labels"]) { | ||||||
|  |                     for (const label of tagRendering["labels"]) { | ||||||
|  |                         knownTagRenderings.add(layer.id + "." + label) | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         }) | ||||||
|  | 
 | ||||||
|         const skippedThemes: string[] = [] |         const skippedThemes: string[] = [] | ||||||
|         for (const themeInfo of themeFiles) { |         for (const themeInfo of themeFiles) { | ||||||
|             const themePath = themeInfo.path |             const themePath = themeInfo.path | ||||||
|  | @ -433,10 +434,10 @@ class LayerOverviewUtils { | ||||||
|                 themeFile = new PrepareTheme(convertState).convertStrict(themeFile, themePath) |                 themeFile = new PrepareTheme(convertState).convertStrict(themeFile, themePath) | ||||||
| 
 | 
 | ||||||
|                 new ValidateThemeAndLayers( |                 new ValidateThemeAndLayers( | ||||||
|                     doesImageExist, |                     new DoesImageExist(licensePaths, existsSync, knownTagRenderings), | ||||||
|                     themePath, |                     themePath, | ||||||
|                     true, |                     true, | ||||||
|                     convertState.tagRenderings |                     knownTagRenderings | ||||||
|                 ).convertStrict(themeFile, themePath) |                 ).convertStrict(themeFile, themePath) | ||||||
| 
 | 
 | ||||||
|                 if (themeFile.icon.endsWith(".svg")) { |                 if (themeFile.icon.endsWith(".svg")) { | ||||||
|  |  | ||||||
|  | @ -143,7 +143,7 @@ describe("PrepareTheme", () => { | ||||||
| describe("ExtractImages", () => { | describe("ExtractImages", () => { | ||||||
|     it("should find all images in a themefile", () => { |     it("should find all images in a themefile", () => { | ||||||
|         const images = new Set( |         const images = new Set( | ||||||
|             new ExtractImages(true, new Map<string, any>()).convertStrict(<any>cyclofix, "test") |             new ExtractImages(true, new Set<string>()).convertStrict(<any>cyclofix, "test") | ||||||
|         ) |         ) | ||||||
|         const expectedValues = [ |         const expectedValues = [ | ||||||
|             "./assets/layers/bike_repair_station/repair_station.svg", |             "./assets/layers/bike_repair_station/repair_station.svg", | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue