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 {
public static vNumber = "0.17.1";
public static vNumber = "0.17.2";
public static ImgurApiKey = '7070e7167f0a25a'
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 {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";
import Translations from "../../../UI/i18n/Translations";
class ExpandTagRendering extends Conversion<string | TagRenderingConfigJson | { builtin: string | string[], override: any }, TagRenderingConfigJson[]> {
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() {
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 {
const targetIsTranslation = Translations.isProbablyATranslation(target)
function replaceRecursive(transl: string | any) {
if(transl === keyToRewrite){
function replaceRecursive(obj: string | any, target) {
if (obj === keyToRewrite) {
return target
}
if (typeof transl === "string") {
if (typeof obj === "string") {
// 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
return transl.map(o => replaceRecursive(o))
return obj.map(o => replaceRecursive(o, target))
}
if(typeof transl === "object"){
transl = {...transl}
for (const key in transl) {
transl[key] = replaceRecursive(transl[key])
if (typeof obj === "object") {
obj = {...obj}
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[] } {
if(json === null || json === undefined){
if (json === null || json === undefined) {
return {result: []}
}
if (json["rewrite"] === undefined) {
// not a rewrite
return {result: [(<T>json)]}
}
const rewrite = <RewritableConfigJson<T>>json;
const keysToRewrite = rewrite.rewrite
const ts : T[] = []
const keysToRewrite = rewrite.rewrite
const ts: T[] = []
for (let i = 0; i < keysToRewrite.sourceString.length; i++){
const guard = keysToRewrite.sourceString[i];
for (let j = i + 1; j < keysToRewrite.sourceString.length; j++) {
const toRewrite = keysToRewrite.sourceString[j]
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.`
{// sanity check: rewrite: ["xyz", "longer_xyz"] is not allowed as "longer_xyz" will never be triggered
for (let i = 0; i < keysToRewrite.sourceString.length; i++) {
const guard = keysToRewrite.sourceString[i];
for (let j = i + 1; j < keysToRewrite.sourceString.length; j++) {
const toRewrite = keysToRewrite.sourceString[j]
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)
for (let i1 = 0; i1 < keysToRewrite.sourceString.length; i1++){
const key = keysToRewrite.sourceString[i1];
const target = keysToRewrite.into[i1][i]
for (let j = 0; j < keysToRewrite.sourceString.length; j++) {
const key = keysToRewrite.sourceString[j];
const target = keysToRewrite.into[i][j]
t = ExpandRewrite.RewriteParts(key, target, 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> {

View file

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

View file

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

View file

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

View file

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

View file

@ -1,93 +1,106 @@
import {describe} from 'mocha'
import {expect} from 'chai'
import {LayoutConfigJson} from "../../../../Models/ThemeConfig/Json/LayoutConfigJson";
import {LayerConfigJson} from "../../../../Models/ThemeConfig/Json/LayerConfigJson";
import {PrepareTheme} from "../../../../Models/ThemeConfig/Conversion/PrepareTheme";
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 {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 expand mappings 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"}]
}
expect(result).deep.eq(expected)
})
it("should not allow overlapping keys", () => {
const spec = <RewritableConfigJson<string>>{
rewrite: {
sourceString: ["xyz", "longer_xyz"],
into: [["a", "b"], ["A, B"]],
},
renderings: "The value of xyz is longer_xyz"
}
)
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)
})
})