forked from MapComplete/MapComplete
		
	Made mapRenderings rewritable
This commit is contained in:
		
							parent
							
								
									9f81628f64
								
							
						
					
					
						commit
						75abd18d90
					
				
					 4 changed files with 185 additions and 31 deletions
				
			
		|  | @ -1,14 +1,16 @@ | |||
| import {Conversion, DesugaringContext, Fuse, OnEveryConcat, SetDefault} from "./Conversion"; | ||||
| import {Conversion, DesugaringContext, Fuse, OnEvery, OnEveryConcat, SetDefault} from "./Conversion"; | ||||
| import {LayerConfigJson} from "../Json/LayerConfigJson"; | ||||
| import {TagRenderingConfigJson} from "../Json/TagRenderingConfigJson"; | ||||
| import {Utils} from "../../../Utils"; | ||||
| import Translations from "../../../UI/i18n/Translations"; | ||||
| import {Translation} from "../../../UI/i18n/Translation"; | ||||
| import RewritableConfigJson from "../Json/RewritableConfigJson"; | ||||
| 
 | ||||
| class ExpandTagRendering extends Conversion<string | TagRenderingConfigJson | { builtin: string | string[], override: any }, TagRenderingConfigJson[]> { | ||||
|     private readonly _state: DesugaringContext; | ||||
| 
 | ||||
|     constructor(state: DesugaringContext) { | ||||
|         super("Converts a tagRenderingSpec into the full tagRendering", [],"ExpandTagRendering"); | ||||
|         super("Converts a tagRenderingSpec into the full tagRendering", [], "ExpandTagRendering"); | ||||
|         this._state = state; | ||||
|     } | ||||
| 
 | ||||
|  | @ -147,17 +149,17 @@ class ExpandGroupRewrite extends Conversion<{ | |||
| 
 | ||||
|     constructor(state: DesugaringContext) { | ||||
|         super( | ||||
|             "Converts a rewrite config for tagRenderings into the expanded form",[], | ||||
|             "Converts a rewrite config for tagRenderings into the expanded form", [], | ||||
|             "ExpandGroupRewrite" | ||||
|         ); | ||||
|         this._expandSubTagRenderings = new ExpandTagRendering(state) | ||||
|     } | ||||
| 
 | ||||
|     convert( json: | ||||
|         { | ||||
|             rewrite: | ||||
|                 { sourceString: string; into: string[] }[]; renderings: (string | { builtin: string; override: any } | TagRenderingConfigJson)[] | ||||
|         } | TagRenderingConfigJson, context: string): { result: TagRenderingConfigJson[]; errors: string[]; warnings?: string[] } { | ||||
|     convert(json: | ||||
|                 { | ||||
|                     rewrite: | ||||
|                         { sourceString: string; into: string[] }[]; renderings: (string | { builtin: string; override: any } | TagRenderingConfigJson)[] | ||||
|                 } | TagRenderingConfigJson, context: string): { result: TagRenderingConfigJson[]; errors: string[]; warnings?: string[] } { | ||||
| 
 | ||||
|         if (json["rewrite"] === undefined) { | ||||
|             return {result: [<TagRenderingConfigJson>json], errors: [], warnings: []} | ||||
|  | @ -167,32 +169,32 @@ class ExpandGroupRewrite extends Conversion<{ | |||
|                 { sourceString: string[]; into: (string | any)[][] }; | ||||
|             renderings: (string | { builtin: string; override: any } | TagRenderingConfigJson)[] | ||||
|         }>json; | ||||
|         | ||||
| 
 | ||||
| 
 | ||||
|         { | ||||
|             const errors = [] | ||||
| 
 | ||||
|             if(!Array.isArray(config.rewrite.sourceString)){ | ||||
|             if (!Array.isArray(config.rewrite.sourceString)) { | ||||
|                 let extra = ""; | ||||
|                 if(typeof config.rewrite.sourceString === "string"){ | ||||
|                     extra=`<br/>Try <span class='literal-code'>"sourceString": [ "${config.rewrite.sourceString}" ] </span> instead (note the [ and ])` | ||||
|                 if (typeof config.rewrite.sourceString === "string") { | ||||
|                     extra = `<br/>Try <span class='literal-code'>"sourceString": [ "${config.rewrite.sourceString}" ] </span> instead (note the [ and ])` | ||||
|                 } | ||||
|                 const msg = context+"<br/>Invalid format: a rewrite block is defined, but the 'sourceString' should be an array of strings, but it is a "+typeof  config.rewrite.sourceString + extra | ||||
|                 const msg = context + "<br/>Invalid format: a rewrite block is defined, but the 'sourceString' should be an array of strings, but it is a " + typeof config.rewrite.sourceString + extra | ||||
|                 errors.push(msg) | ||||
|             } | ||||
| 
 | ||||
| 
 | ||||
|             const expectedLength = config.rewrite.sourceString.length | ||||
|             for (let i = 0; i < config.rewrite.into.length; i++){ | ||||
|             for (let i = 0; i < config.rewrite.into.length; i++) { | ||||
|                 const targets = config.rewrite.into[i]; | ||||
|                 if(!Array.isArray(targets)){ | ||||
|                     errors.push(`${context}.rewrite.into[${i}] should be an array of values, but it is a `+typeof targets) | ||||
|                 } else                if(targets.length !== expectedLength){ | ||||
|                 if (!Array.isArray(targets)) { | ||||
|                     errors.push(`${context}.rewrite.into[${i}] should be an array of values, but it is a ` + typeof targets) | ||||
|                 } else if (targets.length !== expectedLength) { | ||||
|                     errors.push(`${context}.rewrite.into[${i}]:<br/>The rewrite specified ${config.rewrite.sourceString} as sourcestring, which consists of ${expectedLength} values. The target ${JSON.stringify(targets)} has ${targets.length} items`) | ||||
|                 if(typeof targets[0] !== "string"){ | ||||
|                     errors.push(context+".rewrite.into["+i+"]: expected a string as first rewrite value values, but got "+targets[0]) | ||||
|                     if (typeof targets[0] !== "string") { | ||||
|                         errors.push(context + ".rewrite.into[" + i + "]: expected a string as first rewrite value values, but got " + targets[0]) | ||||
| 
 | ||||
|                 } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|  | @ -205,7 +207,7 @@ class ExpandGroupRewrite extends Conversion<{ | |||
|             } | ||||
|         } | ||||
| 
 | ||||
|         const subRenderingsRes = <{ result: TagRenderingConfigJson[][], errors, warnings }> this._expandSubTagRenderings.convertAll(config.renderings, context); | ||||
|         const subRenderingsRes = <{ result: TagRenderingConfigJson[][], errors, warnings }>this._expandSubTagRenderings.convertAll(config.renderings, context); | ||||
|         const subRenderings: TagRenderingConfigJson[] = [].concat(...subRenderingsRes.result); | ||||
|         const errors = subRenderingsRes.errors; | ||||
|         const warnings = subRenderingsRes.warnings; | ||||
|  | @ -217,7 +219,7 @@ class ExpandGroupRewrite extends Conversion<{ | |||
|         const sourceStrings = config.rewrite.sourceString; | ||||
|         for (const targets of config.rewrite.into) { | ||||
|             const groupName = targets[0]; | ||||
|             if(typeof groupName !== "string"){ | ||||
|             if (typeof groupName !== "string") { | ||||
|                 throw "The first string of 'targets' should always be a string" | ||||
|             } | ||||
|             const trs: TagRenderingConfigJson[] = [] | ||||
|  | @ -227,7 +229,7 @@ class ExpandGroupRewrite extends Conversion<{ | |||
|                 for (let i = 0; i < sourceStrings.length; i++) { | ||||
|                     const source = sourceStrings[i] | ||||
|                     const target = targets[i] // This is a string OR a translation
 | ||||
|                     rewritten = this.prepConfig(source, target, rewritten) | ||||
|                     rewritten = ExpandRewrite.RewriteParts(source, target, rewritten) | ||||
|                 } | ||||
|                 rewritten.group = rewritten.group ?? groupName | ||||
|                 trs.push(rewritten) | ||||
|  | @ -253,7 +255,7 @@ class ExpandGroupRewrite extends Conversion<{ | |||
|         rewrittenPerGroup.forEach((group, _) => { | ||||
|             group.forEach(tr => { | ||||
|                 if (tr.id === undefined || tr.id === "") { | ||||
|                     errors.push("A tagrendering has an empty ID after expanding the tag; the tagrendering is: "+JSON.stringify(tr)) | ||||
|                     errors.push("A tagrendering has an empty ID after expanding the tag; the tagrendering is: " + JSON.stringify(tr)) | ||||
|                 } | ||||
|             }) | ||||
|         }) | ||||
|  | @ -264,9 +266,18 @@ class ExpandGroupRewrite extends Conversion<{ | |||
|         }; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| class ExpandRewrite<T> extends Conversion<T | RewritableConfigJson<T>, T[]> { | ||||
| 
 | ||||
|     constructor() { | ||||
|         super("Applies a rewrite", [], "ExpandRewrite"); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     /* Used for left|right group creation and replacement. | ||||
|     * Every 'keyToRewrite' will be replaced with 'target' recursively. This substitution will happen in place in the object 'tr' */ | ||||
|     private prepConfig(keyToRewrite: string, target: string | any, tr: TagRenderingConfigJson): TagRenderingConfigJson { | ||||
|     public static RewriteParts<T>(keyToRewrite: string, target: string | any, tr: T): T { | ||||
| 
 | ||||
|         const isTranslation = typeof target !== "string" | ||||
| 
 | ||||
|  | @ -293,8 +304,35 @@ class ExpandGroupRewrite extends Conversion<{ | |||
| 
 | ||||
|         return replaceRecursive(tr) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|     convert(json: T | RewritableConfigJson<T>, context: string): { result: T[]; errors?: string[]; warnings?: string[]; information?: string[] } { | ||||
| 
 | ||||
|         if (json["rewrite"] === undefined) { | ||||
|              | ||||
|             // not a rewrite
 | ||||
|             return {result: [(<T>json)]} | ||||
|         } | ||||
| 
 | ||||
|         const rewrite = <RewritableConfigJson<T>>json; | ||||
|         let toRewrite: T = rewrite.renderings | ||||
|         const keysToRewrite = rewrite.rewrite | ||||
|         const ts : T[] = [] | ||||
| 
 | ||||
|         for (let i = 0; i < keysToRewrite.into[0].length; i++){ | ||||
|             let t = Utils.Clone(rewrite.renderings) | ||||
|             for (let i1 = 0; i1 < keysToRewrite.sourceString.length; i1++){ | ||||
|                 const key = keysToRewrite.sourceString[i1]; | ||||
|                 const target = keysToRewrite.into[i1][i] | ||||
|                 t = ExpandRewrite.RewriteParts(key, target, t) | ||||
|             } | ||||
|             ts.push(t) | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         return {result: ts}; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| export class PrepareLayer extends Fuse<LayerConfigJson> { | ||||
|     constructor(state: DesugaringContext) { | ||||
|  | @ -302,6 +340,7 @@ export class PrepareLayer extends Fuse<LayerConfigJson> { | |||
|             "Fully prepares and expands a layer for the LayerConfig.", | ||||
|             new OnEveryConcat("tagRenderings", new ExpandGroupRewrite(state)), | ||||
|             new OnEveryConcat("tagRenderings", new ExpandTagRendering(state)), | ||||
|             new OnEveryConcat("mapRendering", new ExpandRewrite()), | ||||
|             new SetDefault("titleIcons", ["defaults"]), | ||||
|             new OnEveryConcat("titleIcons", new ExpandTagRendering(state)) | ||||
|         ); | ||||
|  |  | |||
|  | @ -179,7 +179,7 @@ export interface LayerConfigJson { | |||
|     /** | ||||
|      * Visualisation of the items on the map | ||||
|      */ | ||||
|     mapRendering: null | (PointRenderingConfigJson | LineRenderingConfigJson)[] | ||||
|     mapRendering: null | (PointRenderingConfigJson | LineRenderingConfigJson | RewritableConfigJson<LineRenderingConfigJson | PointRenderingConfigJson>)[] | ||||
| 
 | ||||
|     /** | ||||
|      * If set, this layer will pass all the features it receives onto the next layer. | ||||
|  |  | |||
|  | @ -1,5 +1,43 @@ | |||
| import {TagRenderingConfigJson} from "./TagRenderingConfigJson"; | ||||
| 
 | ||||
| /** | ||||
|  * Rewrites and multiplies the given renderings of type T. | ||||
|  *  | ||||
|  * For example: | ||||
|  * | ||||
|  * | ||||
|  * ``` | ||||
|  * { | ||||
|  *     rewrite: { | ||||
|  *         sourceString: ["key", "a|b|c"], | ||||
|  *         into: [ | ||||
|  *             ["X","Y", "Z"], | ||||
|  *             [0,1,2] | ||||
|  *         ], | ||||
|  *         renderings: { | ||||
|  *             "key":"a|b|c" | ||||
|  *         } | ||||
|  *     } | ||||
|  * } | ||||
|  * ``` | ||||
|  * will result in _three_ copies (as the values to rewrite into have three values, namely: | ||||
|  *  | ||||
|  * [ | ||||
|  *   { | ||||
|  *   // The first pair: key --> X, a|b|c --> 0
 | ||||
|  *       "X": 0 | ||||
|  *   }, | ||||
|  *   { | ||||
|  *       "Y": 1 | ||||
|  *   }, | ||||
|  *   { | ||||
|  *       "Z": 2 | ||||
|  *   } | ||||
|  *  | ||||
|  * ] | ||||
|  *  | ||||
|  *  | ||||
|  */ | ||||
| export default interface RewritableConfigJson<T> { | ||||
|     rewrite: { | ||||
|         sourceString: string[], | ||||
|  |  | |||
|  | @ -6,6 +6,9 @@ import {AddMiniMap} from "../Models/ThemeConfig/Conversion/PrepareTheme"; | |||
| import {DetectMappingsWithImages, DetectShadowedMappings} from "../Models/ThemeConfig/Conversion/Validation"; | ||||
| import * as Assert from "assert"; | ||||
| import {ExtractImages, FixImages} from "../Models/ThemeConfig/Conversion/FixImages"; | ||||
| import {PrepareLayer} from "../Models/ThemeConfig/Conversion/PrepareLayer"; | ||||
| import {LayerConfigJson} from "../Models/ThemeConfig/Json/LayerConfigJson"; | ||||
| import LineRenderingConfigJson from "../Models/ThemeConfig/Json/LineRenderingConfigJson"; | ||||
| 
 | ||||
| export default class LegacyThemeLoaderSpec extends T { | ||||
| 
 | ||||
|  | @ -502,7 +505,7 @@ export default class LegacyThemeLoaderSpec extends T { | |||
|                     }, "test"); | ||||
|                     const images = r.result | ||||
|                     T.isTrue(images.length > 0, "No images found"); | ||||
|                     T.isTrue(images.findIndex(img => img =="./assets/layers/bike_parking/staple.svg") >= 0, "staple.svg not mentioned"); | ||||
|                     T.isTrue(images.findIndex(img => img == "./assets/layers/bike_parking/staple.svg") >= 0, "staple.svg not mentioned"); | ||||
|                     T.isTrue(images.findIndex(img => img == "./assets/layers/bike_parking/bollard.svg") >= 0, "bollard.svg not mentioned"); | ||||
|                 }], | ||||
|                 ["Rotation and colours is not detected as image", () => { | ||||
|  | @ -511,7 +514,7 @@ export default class LegacyThemeLoaderSpec extends T { | |||
|                             { | ||||
|                                 mapRendering: [ | ||||
|                                     { | ||||
|                                         "location":["point","centroid"], | ||||
|                                         "location": ["point", "centroid"], | ||||
|                                         "icon": "pin:black", | ||||
|                                         rotation: 180, | ||||
|                                         iconSize: "40,40,center" | ||||
|  | @ -522,9 +525,83 @@ export default class LegacyThemeLoaderSpec extends T { | |||
|                     }, "test"); | ||||
|                     const images = r.result | ||||
|                     T.isTrue(images.length > 0, "No images found"); | ||||
|                     T.isTrue(images.length < 2, "To much images found: "+images.join(", ")); | ||||
|                     T.isTrue(images.length < 2, "To much images found: " + images.join(", ")); | ||||
|                     T.isTrue(images[0] === "pin", "pin not mentioned"); | ||||
|                 }] | ||||
|                 }], | ||||
|                 ["Test expansion in map renderings", () => { | ||||
|                     const exampleLayer: LayerConfigJson = { | ||||
|                         id: "testlayer", | ||||
|                         source: { | ||||
|                             osmTags: "key=value" | ||||
|                         }, | ||||
|                         mapRendering: [ | ||||
|                             { | ||||
|                                 "rewrite": { | ||||
|                                     sourceString: ["left|right", "lr_offset"], | ||||
|                                     into: [ | ||||
|                                         ["left", "right"], | ||||
|                                         [-6, +6] | ||||
|                                     ] | ||||
|                                 }, | ||||
|                                 renderings: <LineRenderingConfigJson>{ | ||||
|                                     "color": { | ||||
|                                         "render": "#888", | ||||
|                                         "mappings": [ | ||||
|                                             { | ||||
|                                                 "if": "parking:condition:left|right=free", | ||||
|                                                 "then": "#299921" | ||||
|                                             }, | ||||
|                                             { | ||||
|                                                 "if": "parking:condition:left|right=disc", | ||||
|                                                 "then": "#219991" | ||||
|                                             } | ||||
|                                         ] | ||||
|                                     }, | ||||
|                                     "offset": "lr_offset" | ||||
|                                 } | ||||
|                             } | ||||
|                         ] | ||||
|                     } | ||||
|                     const prep = new PrepareLayer({ | ||||
|                         tagRenderings: new Map<string, TagRenderingConfigJson>(), | ||||
|                         sharedLayers: new Map<string, LayerConfigJson>() | ||||
|                     }) | ||||
|                     const result = prep.convertStrict(exampleLayer, "test") | ||||
| 
 | ||||
|                     const expected = { | ||||
|                         "id": "testlayer", | ||||
|                         "source": {"osmTags": "key=value"}, | ||||
|                         "mapRendering": [{ | ||||
|                             "color": { | ||||
|                                 "render": "#888", | ||||
|                                 "mappings": [{ | ||||
|                                     "if": "parking:condition:left=free", | ||||
|                                     "then": "#299921" | ||||
|                                 },  | ||||
|                                     {"if": "parking:condition:left=disc", | ||||
|                                         "then": "#219991"}] | ||||
|                             },  | ||||
|                             "offset":   "-6" | ||||
|                         }, { | ||||
|                             "color": { | ||||
|                                 "render": "#888", | ||||
|                                 "mappings": [{ | ||||
|                                     "if": "parking:condition:right=free", | ||||
|                                     "then": "#299921" | ||||
|                                 },  | ||||
|                                     {"if": "parking:condition:right=disc", | ||||
|                                         "then": "#219991"}] | ||||
|                             },  | ||||
|                             "offset": "6" | ||||
|                         }], | ||||
|                         "titleIcons": [{"render": "defaults", "id": "defaults"}] | ||||
|                     } | ||||
| 
 | ||||
| 
 | ||||
|                    Assert.equal(JSON.stringify(result), JSON.stringify(expected)) | ||||
|                 } | ||||
| 
 | ||||
|                 ] | ||||
|             ] | ||||
|         ); | ||||
|     } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue