This commit is contained in:
pietervdvn 2022-04-03 02:37:31 +02:00
parent 61e76cce47
commit 6c1a1d59f3
7 changed files with 221 additions and 155 deletions

View file

@ -2,7 +2,7 @@ import {Utils} from "../Utils";
export default class Constants { export default class Constants {
public static vNumber = "0.17.1"; public static vNumber = "0.17.2";
public static ImgurApiKey = '7070e7167f0a25a' public static ImgurApiKey = '7070e7167f0a25a'
public static readonly mapillary_client_token_v4 = "MLY|4441509239301885|b40ad2d3ea105435bd40c7e76993ae85" public static readonly mapillary_client_token_v4 = "MLY|4441509239301885|b40ad2d3ea105435bd40c7e76993ae85"

View file

@ -1,10 +1,9 @@
import {Conversion, DesugaringContext, Fuse, OnEvery, OnEveryConcat, SetDefault} from "./Conversion"; import {Conversion, DesugaringContext, Fuse, OnEveryConcat, SetDefault} 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 Translations from "../../../UI/i18n/Translations";
import {Translation} from "../../../UI/i18n/Translation";
import RewritableConfigJson from "../Json/RewritableConfigJson"; import RewritableConfigJson from "../Json/RewritableConfigJson";
import Translations from "../../../UI/i18n/Translations";
class ExpandTagRendering extends Conversion<string | TagRenderingConfigJson | { builtin: string | string[], override: any }, TagRenderingConfigJson[]> { class ExpandTagRendering extends Conversion<string | TagRenderingConfigJson | { builtin: string | string[], override: any }, TagRenderingConfigJson[]> {
private readonly _state: DesugaringContext; private readonly _state: DesugaringContext;
@ -268,76 +267,145 @@ class ExpandGroupRewrite extends Conversion<{
} }
class ExpandRewrite<T> extends Conversion<T | RewritableConfigJson<T>, T[]> { export class ExpandRewrite<T> extends Conversion<T | RewritableConfigJson<T>, T[]> {
constructor() { constructor() {
super("Applies a rewrite", [], "ExpandRewrite"); 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' */ * 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'
*
* // should substitute strings
* const spec = {
* "someKey": "somevalue {xyz}"
* }
* ExpandRewrite.RewriteParts("{xyz}", "rewritten", spec) // => {"someKey": "somevalue rewritten"}
*
*/
public static RewriteParts<T>(keyToRewrite: string, target: string | any, tr: T): T { public static RewriteParts<T>(keyToRewrite: string, target: string | any, tr: T): T {
const targetIsTranslation = Translations.isProbablyATranslation(target)
function replaceRecursive(transl: string | any) { function replaceRecursive(obj: string | any, target) {
if(transl === keyToRewrite){ if (obj === keyToRewrite) {
return target return target
} }
if (typeof transl === "string") { if (typeof obj === "string") {
// This is a simple string - we do a simple replace // This is a simple string - we do a simple replace
return transl.replace(keyToRewrite, target) return obj.replace(keyToRewrite, target)
} }
if (Array.isArray(transl)) { if (Array.isArray(obj)) {
// This is a list of items // This is a list of items
return transl.map(o => replaceRecursive(o)) return obj.map(o => replaceRecursive(o, target))
} }
if(typeof transl === "object"){ if (typeof obj === "object") {
transl = {...transl} obj = {...obj}
for (const key in transl) {
transl[key] = replaceRecursive(transl[key]) const isTr = targetIsTranslation && Translations.isProbablyATranslation(obj)
for (const key in obj) {
let subtarget = target
if(isTr && target[key] !== undefined){
// The target is a translation AND the current object is a translation
// This means we should recursively replace with the translated value
subtarget = target[key]
}
obj[key] = replaceRecursive(obj[key], subtarget)
} }
return transl return obj
} }
return transl return obj
} }
return replaceRecursive(tr) return replaceRecursive(tr, target)
} }
/**
* // should convert simple strings
* const spec = <RewritableConfigJson<string>>{
* rewrite: {
* sourceString: ["xyz","abc"],
* into: [
* ["X", "A"],
* ["Y", "B"],
* ["Z", "C"]],
* },
* renderings: "The value of xyz is abc"
* }
* new ExpandRewrite().convertStrict(spec, "test") // => ["The value of X is A", "The value of Y is B", "The value of Z is C"]
*
* // should rewrite with translations
* const spec = <RewritableConfigJson<any>>{
* rewrite: {
* sourceString: ["xyz","abc"],
* into: [
* ["X", {en: "value", nl: "waarde"}],
* ["Y", {en: "some other value", nl: "een andere waarde"}],
* },
* renderings: {en: "The value of xyz is abc", nl: "De waarde van xyz is abc"}
* }
* const expected = [
* {
* en: "The value of X is value",
* nl: "De waarde van X is waarde"
* },
* {
* en: "The value of Y is some other value",
* nl: "De waarde van Y is een andere waarde"
* }
* ]
* new ExpandRewrite().convertStrict(spec, "test") // => expected
*/
convert(json: T | RewritableConfigJson<T>, context: string): { result: T[]; errors?: string[]; warnings?: string[]; information?: string[] } { convert(json: T | RewritableConfigJson<T>, context: string): { result: T[]; errors?: string[]; warnings?: string[]; information?: string[] } {
if(json === null || json === undefined){ if (json === null || json === undefined) {
return {result: []} return {result: []}
} }
if (json["rewrite"] === undefined) { if (json["rewrite"] === undefined) {
// not a rewrite // not a rewrite
return {result: [(<T>json)]} return {result: [(<T>json)]}
} }
const rewrite = <RewritableConfigJson<T>>json; const rewrite = <RewritableConfigJson<T>>json;
const keysToRewrite = rewrite.rewrite const keysToRewrite = rewrite.rewrite
const ts : T[] = [] const ts: T[] = []
for (let i = 0; i < keysToRewrite.sourceString.length; i++){ {// sanity check: rewrite: ["xyz", "longer_xyz"] is not allowed as "longer_xyz" will never be triggered
const guard = keysToRewrite.sourceString[i]; for (let i = 0; i < keysToRewrite.sourceString.length; i++) {
for (let j = i + 1; j < keysToRewrite.sourceString.length; j++) { const guard = keysToRewrite.sourceString[i];
const toRewrite = keysToRewrite.sourceString[j] for (let j = i + 1; j < keysToRewrite.sourceString.length; j++) {
if(toRewrite.indexOf(guard) >= 0){ const toRewrite = keysToRewrite.sourceString[j]
throw `${context} Error in rewrite: sourcestring[${i}] is a substring of sourcestring[${j}]: ${guard} will be substituted away before ${toRewrite} is reached.` if (toRewrite.indexOf(guard) >= 0) {
throw `${context} Error in rewrite: sourcestring[${i}] is a substring of sourcestring[${j}]: ${guard} will be substituted away before ${toRewrite} is reached.`
}
} }
} }
} }
for (let i = 0; i < keysToRewrite.into[0].length; i++){ {// sanity check: {rewrite: ["a", "b"] should have the right amount of 'intos' in every case
for (let i = 0; i < rewrite.rewrite.into.length; i++) {
const into = keysToRewrite.into[i]
if(into.length !== rewrite.rewrite.sourceString.length){
throw `${context}.into.${i} Error in rewrite: there are ${rewrite.rewrite.sourceString.length} keys to rewrite, but entry ${i} has only ${into.length} values`
}
}
}
for (let i = 0; i < keysToRewrite.into.length; i++) {
let t = Utils.Clone(rewrite.renderings) let t = Utils.Clone(rewrite.renderings)
for (let i1 = 0; i1 < keysToRewrite.sourceString.length; i1++){ for (let j = 0; j < keysToRewrite.sourceString.length; j++) {
const key = keysToRewrite.sourceString[i1]; const key = keysToRewrite.sourceString[j];
const target = keysToRewrite.into[i1][i] const target = keysToRewrite.into[i][j]
t = ExpandRewrite.RewriteParts(key, target, t) t = ExpandRewrite.RewriteParts(key, target, t)
} }
ts.push(t) ts.push(t)
@ -349,22 +417,6 @@ class ExpandRewrite<T> extends Conversion<T | RewritableConfigJson<T>, T[]> {
} }
class ExpandRewriteWithFlatten<T> extends Conversion<T | RewritableConfigJson<T | T[]>, T[]> {
private _rewrite = new ExpandRewrite<T>()
constructor() {
super("Applies a rewrite, the result is flattened if it is an array", [], "ExpandRewriteWithFlatten");
}
convert(json: RewritableConfigJson<T[] | T> | T, context: string): { result: T[]; errors?: string[]; warnings?: string[]; information?: string[] } {
return undefined;
}
}
export class PrepareLayer extends Fuse<LayerConfigJson> { export class PrepareLayer extends Fuse<LayerConfigJson> {

View file

@ -1,5 +1,3 @@
import {TagRenderingConfigJson} from "./TagRenderingConfigJson";
/** /**
* Rewrites and multiplies the given renderings of type T. * Rewrites and multiplies the given renderings of type T.
* *
@ -11,8 +9,9 @@ import {TagRenderingConfigJson} from "./TagRenderingConfigJson";
* rewrite: { * rewrite: {
* sourceString: ["key", "a|b|c"], * sourceString: ["key", "a|b|c"],
* into: [ * into: [
* ["X","Y", "Z"], * ["X", 0]
* [0,1,2] * ["Y", 1],
* ["Z", 2]
* ], * ],
* renderings: { * renderings: {
* "key":"a|b|c" * "key":"a|b|c"
@ -36,7 +35,7 @@ import {TagRenderingConfigJson} from "./TagRenderingConfigJson";
* *
* ] * ]
* *
* * @see ExpandRewrite
*/ */
export default interface RewritableConfigJson<T> { export default interface RewritableConfigJson<T> {
rewrite: { rewrite: {

View file

@ -218,7 +218,7 @@
"city_bike", "city_bike",
{ {
"en": "city bikes", "en": "city bikes",
"nl": "Stadsfietsen" "nl": "stadsfietsen"
} }
], ],
[ [

View file

@ -1,7 +1,8 @@
{ {
"id": "waste_disposal", "id": "waste_disposal",
"name": { "name": {
"en": "Waste Disposal Bins" "en": "Waste Disposal Bins",
"nl": "Afvalcontainers voor huishoudelijk afval"
}, },
"description": { "description": {
"en": "Waste Disposal Bin, medium to large bin for disposal of (household) waste" "en": "Waste Disposal Bin, medium to large bin for disposal of (household) waste"
@ -109,7 +110,8 @@
"options": [ "options": [
{ {
"question": { "question": {
"en": "Only public access" "en": "Only public access",
"nl": "Enkel publiek toegankelijke afvalcontainers"
}, },
"osmTags": "access=yes" "osmTags": "access=yes"
} }

View file

@ -5212,6 +5212,16 @@
} }
}, },
"waste_disposal": { "waste_disposal": {
"filter": {
"0": {
"options": {
"0": {
"question": "Enkel publiek toegankelijke afvalcontainers"
}
}
}
},
"name": "Afvalcontainers voor huishoudelijk afval",
"tagRenderings": { "tagRenderings": {
"disposal-location": { "disposal-location": {
"mappings": { "mappings": {
@ -5227,17 +5237,7 @@
}, },
"question": "Waar bevindt deze container zich?" "question": "Waar bevindt deze container zich?"
} }
}, }
"filter": {
"0": {
"options": {
"0": {
"question": "Enkel publiek toegankelijke afvalcontainers"
}
}
}
},
"name": "Afvalcontainers voor huishoudelijk afval"
}, },
"watermill": { "watermill": {
"description": "Watermolens", "description": "Watermolens",
@ -5292,4 +5292,4 @@
"render": "Watermolens" "render": "Watermolens"
} }
} }
} }

View file

@ -1,93 +1,106 @@
import {describe} from 'mocha' import {describe} from 'mocha'
import {expect} from 'chai' import {expect} from 'chai'
import {LayoutConfigJson} from "../../../../Models/ThemeConfig/Json/LayoutConfigJson";
import {LayerConfigJson} from "../../../../Models/ThemeConfig/Json/LayerConfigJson"; import {LayerConfigJson} from "../../../../Models/ThemeConfig/Json/LayerConfigJson";
import {PrepareTheme} from "../../../../Models/ThemeConfig/Conversion/PrepareTheme";
import {TagRenderingConfigJson} from "../../../../Models/ThemeConfig/Json/TagRenderingConfigJson"; import {TagRenderingConfigJson} from "../../../../Models/ThemeConfig/Json/TagRenderingConfigJson";
import LayoutConfig from "../../../../Models/ThemeConfig/LayoutConfig";
import * as bookcaseLayer from "../../../../assets/generated/layers/public_bookcase.json"
import LayerConfig from "../../../../Models/ThemeConfig/LayerConfig";
import {ExtractImages} from "../../../../Models/ThemeConfig/Conversion/FixImages";
import * as cyclofix from "../../../../assets/generated/themes/cyclofix.json"
import LineRenderingConfigJson from "../../../../Models/ThemeConfig/Json/LineRenderingConfigJson"; import LineRenderingConfigJson from "../../../../Models/ThemeConfig/Json/LineRenderingConfigJson";
import {PrepareLayer} from "../../../../Models/ThemeConfig/Conversion/PrepareLayer"; import {ExpandRewrite, PrepareLayer} from "../../../../Models/ThemeConfig/Conversion/PrepareLayer";
import RewritableConfigJson from "../../../../Models/ThemeConfig/Json/RewritableConfigJson";
describe("ExpandRewrite", () => {
it("should do simple substitution", () => {
describe("PrepareLayer", () => { })
it("should not allow overlapping keys", () => {
it("should expand mappings in map renderings", () => { const spec = <RewritableConfigJson<string>>{
const exampleLayer: LayerConfigJson = { rewrite: {
id: "testlayer", sourceString: ["xyz", "longer_xyz"],
source: { into: [["a", "b"], ["A, B"]],
osmTags: "key=value" },
}, renderings: "The value of xyz is longer_xyz"
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"}]
}
expect(result).deep.eq(expected)
} }
) const rewrite = new ExpandRewrite()
expect(() => rewrite.convert(spec, "test")).to.throw
})
})
describe("PrepareLayer", () => {
it("should expand rewrites in map renderings", () => {
const exampleLayer: LayerConfigJson = {
id: "testlayer",
source: {
osmTags: "key=value"
},
mapRendering: [
{
"rewrite": {
sourceString: ["left|right", "lr_offset"],
into: [
["left", -6],
[ "right", +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"}]
}
expect(result).deep.eq(expected)
})
}) })