2021-07-23 15:56:22 +02:00
import { Translation } from "../../UI/i18n/Translation" ;
2021-03-20 23:45:52 +01:00
import SourceConfig from "./SourceConfig" ;
2021-08-07 23:11:34 +02:00
import TagRenderingConfig from "./TagRenderingConfig" ;
2021-07-23 15:56:22 +02:00
import { TagsFilter } from "../../Logic/Tags/TagsFilter" ;
2021-08-07 23:11:34 +02:00
import PresetConfig from "./PresetConfig" ;
import { LayerConfigJson } from "./Json/LayerConfigJson" ;
import Translations from "../../UI/i18n/Translations" ;
import { TagUtils } from "../../Logic/Tags/TagUtils" ;
2021-07-22 11:29:09 +02:00
import FilterConfig from "./FilterConfig" ;
2021-08-07 23:11:34 +02:00
import { Unit } from "../Unit" ;
import DeleteConfig from "./DeleteConfig" ;
2021-10-14 03:46:09 +02:00
import MoveConfig from "./MoveConfig" ;
2021-10-19 02:31:32 +02:00
import PointRenderingConfig from "./PointRenderingConfig" ;
import WithContextLoader from "./WithContextLoader" ;
2021-10-20 02:01:27 +02:00
import LineRenderingConfig from "./LineRenderingConfig" ;
import PointRenderingConfigJson from "./Json/PointRenderingConfigJson" ;
import LineRenderingConfigJson from "./Json/LineRenderingConfigJson" ;
2021-10-22 18:53:07 +02:00
import { TagRenderingConfigJson } from "./Json/TagRenderingConfigJson" ;
2021-04-10 23:53:13 +02:00
2021-10-22 18:53:07 +02:00
export default class LayerConfig extends WithContextLoader {
2021-07-23 15:56:22 +02:00
id : string ;
name : Translation ;
description : Translation ;
source : SourceConfig ;
calculatedTags : [ string , string ] [ ] ;
doNotDownload : boolean ;
passAllFeatures : boolean ;
isShown : TagRenderingConfig ;
minzoom : number ;
2021-07-27 19:39:57 +02:00
minzoomVisible : number ;
2021-08-07 21:19:01 +02:00
maxzoom : number ;
2021-07-23 15:56:22 +02:00
title? : TagRenderingConfig ;
titleIcons : TagRenderingConfig [ ] ;
2021-10-22 18:53:07 +02:00
2021-10-19 02:31:32 +02:00
public readonly mapRendering : PointRenderingConfig [ ]
2021-10-20 02:01:27 +02:00
public readonly lineRendering : LineRenderingConfig [ ]
2021-10-19 02:31:32 +02:00
2021-07-23 15:56:22 +02:00
public readonly units : Unit [ ] ;
public readonly deletion : DeleteConfig | null ;
2021-10-14 03:46:09 +02:00
public readonly allowMove : MoveConfig | null
2021-07-15 20:47:28 +02:00
public readonly allowSplit : boolean
2021-10-25 20:38:57 +02:00
/ * *
* In seconds
* /
public readonly maxAgeOfCache : number
2021-07-23 15:56:22 +02:00
2021-08-07 21:19:01 +02:00
presets : PresetConfig [ ] ;
2020-11-17 02:22:48 +01:00
2021-07-23 15:56:22 +02:00
tagRenderings : TagRenderingConfig [ ] ;
filters : FilterConfig [ ] ;
2021-07-22 11:29:09 +02:00
2021-07-23 15:56:22 +02:00
constructor (
json : LayerConfigJson ,
context? : string ,
official : boolean = true
) {
context = context + "." + json . id ;
2021-10-19 02:31:32 +02:00
super ( json , context )
2021-07-23 15:56:22 +02:00
this . id = json . id ;
let legacy = undefined ;
if ( json [ "overpassTags" ] !== undefined ) {
// @ts-ignore
2021-08-07 23:11:34 +02:00
legacy = TagUtils . Tag ( json [ "overpassTags" ] , context + ".overpasstags" ) ;
2021-03-20 23:45:52 +01:00
}
2021-10-25 20:38:57 +02:00
2021-07-23 15:56:22 +02:00
if ( json . source !== undefined ) {
2021-10-25 20:38:57 +02:00
this . maxAgeOfCache = json . source . maxCacheAge ? ? 24 * 60 * 60 * 30
2021-07-23 15:56:22 +02:00
if ( legacy !== undefined ) {
throw (
context +
"Both the legacy 'layer.overpasstags' and the new 'layer.source'-field are defined"
) ;
}
let osmTags : TagsFilter = legacy ;
if ( json . source [ "osmTags" ] ) {
2021-08-07 23:11:34 +02:00
osmTags = TagUtils . Tag (
2021-07-23 15:56:22 +02:00
json . source [ "osmTags" ] ,
context + "source.osmTags"
) ;
}
if ( json . source [ "geoJsonSource" ] !== undefined ) {
throw context + "Use 'geoJson' instead of 'geoJsonSource'" ;
}
2021-09-18 02:32:40 +02:00
if ( json . source [ "geojson" ] !== undefined ) {
throw context + "Use 'geoJson' instead of 'geojson' (the J is a capital letter)" ;
}
2021-10-22 18:53:07 +02:00
this . source = new SourceConfig (
2021-07-23 15:56:22 +02:00
{
osmTags : osmTags ,
geojsonSource : json.source [ "geoJson" ] ,
geojsonSourceLevel : json.source [ "geoJsonZoomLevel" ] ,
overpassScript : json.source [ "overpassScript" ] ,
isOsmCache : json.source [ "isOsmCache" ] ,
2021-10-27 03:52:19 +02:00
mercatorCrs : json.source [ "mercatorCrs" ]
2021-07-23 15:56:22 +02:00
} ,
2021-10-19 02:31:32 +02:00
json . id
2021-07-23 15:56:22 +02:00
) ;
2021-10-22 18:53:07 +02:00
} else if ( legacy === undefined ) {
throw "No valid source defined (" + context + ")"
2021-07-23 15:56:22 +02:00
} else {
2021-10-22 18:53:07 +02:00
this . source = new SourceConfig ( {
2021-07-23 15:56:22 +02:00
osmTags : legacy ,
} ) ;
2021-03-24 01:25:57 +01:00
}
2021-10-19 02:31:32 +02:00
this . allowSplit = json . allowSplit ? ? false ;
this . name = Translations . T ( json . name , context + ".name" ) ;
this . units = ( json . units ? ? [ ] ) . map ( ( ( unitJson , i ) = > Unit . fromJson ( unitJson , ` ${ context } .unit[ ${ i } ] ` ) ) )
if ( json . description !== undefined ) {
if ( Object . keys ( json . description ) . length === 0 ) {
json . description = undefined ;
}
}
this . description = Translations . T (
json . description ,
context + ".description"
) ;
2021-07-23 15:56:22 +02:00
this . calculatedTags = undefined ;
if ( json . calculatedTags !== undefined ) {
if ( ! official ) {
console . warn (
` Unofficial theme ${ this . id } with custom javascript! This is a security risk `
) ;
2021-01-08 03:57:18 +01:00
}
2021-07-23 15:56:22 +02:00
this . calculatedTags = [ ] ;
for ( const kv of json . calculatedTags ) {
const index = kv . indexOf ( "=" ) ;
const key = kv . substring ( 0 , index ) ;
const code = kv . substring ( index + 1 ) ;
2021-01-08 03:57:18 +01:00
2021-10-14 03:46:09 +02:00
try {
new Function ( "feat" , "return " + code + ";" ) ;
} catch ( e ) {
throw ` Invalid function definition: code ${ code } is invalid: ${ e } (at ${ context } ) `
2021-09-18 02:32:40 +02:00
}
2021-07-23 15:56:22 +02:00
this . calculatedTags . push ( [ key , code ] ) ;
2020-10-27 01:01:34 +01:00
}
2021-07-23 15:56:22 +02:00
}
2021-01-08 03:57:18 +01:00
2021-07-23 15:56:22 +02:00
this . doNotDownload = json . doNotDownload ? ? false ;
this . passAllFeatures = json . passAllFeatures ? ? false ;
this . minzoom = json . minzoom ? ? 0 ;
2021-07-27 19:39:57 +02:00
this . minzoomVisible = json . minzoomVisible ? ? this . minzoom ;
2021-10-14 03:46:09 +02:00
if ( json . presets !== undefined && json . presets ? . map === undefined ) {
throw "Presets should be a list of items (at " + context + ")"
2021-09-22 20:44:53 +02:00
}
2021-07-14 00:17:15 +02:00
this . presets = ( json . presets ? ? [ ] ) . map ( ( pr , i ) = > {
2021-08-07 23:11:34 +02:00
2021-10-16 00:43:53 +02:00
let preciseInput : any = {
preferredBackground : [ "photo" ] ,
snapToLayers : undefined ,
maxSnapDistance : undefined
2021-10-15 19:58:02 +02:00
} ;
2021-08-07 23:11:34 +02:00
if ( pr . preciseInput !== undefined ) {
2021-08-07 21:19:01 +02:00
if ( pr . preciseInput === true ) {
pr . preciseInput = {
preferredBackground : undefined
}
}
let snapToLayers : string [ ] ;
if ( typeof pr . preciseInput . snapToLayer === "string" ) {
snapToLayers = [ pr . preciseInput . snapToLayer ]
} else {
snapToLayers = pr . preciseInput . snapToLayer
}
2021-08-07 23:11:34 +02:00
let preferredBackground : string [ ]
2021-08-07 21:19:01 +02:00
if ( typeof pr . preciseInput . preferredBackground === "string" ) {
preferredBackground = [ pr . preciseInput . preferredBackground ]
} else {
preferredBackground = pr . preciseInput . preferredBackground
}
preciseInput = {
preferredBackground : preferredBackground ,
snapToLayers : snapToLayers ,
maxSnapDistance : pr.preciseInput.maxSnapDistance ? ? 10
2021-07-14 00:17:15 +02:00
}
}
2021-08-07 23:11:34 +02:00
const config : PresetConfig = {
2021-03-13 19:08:31 +01:00
title : Translations.T ( pr . title , ` ${ context } .presets[ ${ i } ].title ` ) ,
2021-08-07 23:11:34 +02:00
tags : pr.tags.map ( ( t ) = > TagUtils . SimpleTag ( t ) ) ,
2021-07-14 00:17:15 +02:00
description : Translations.T ( pr . description , ` ${ context } .presets[ ${ i } ].description ` ) ,
2021-08-07 21:19:01 +02:00
preciseInput : preciseInput ,
2021-07-24 02:32:33 +02:00
}
2021-08-07 21:19:01 +02:00
return config ;
2021-07-24 02:32:33 +02:00
} ) ;
2021-07-23 15:56:22 +02:00
2021-10-22 18:53:07 +02:00
if ( json . mapRendering === undefined ) {
throw "MapRendering is undefined in " + context
2021-10-21 01:26:20 +02:00
}
2021-09-09 20:26:12 +02:00
2021-10-20 02:01:27 +02:00
this . mapRendering = json . mapRendering
. filter ( r = > r [ "icon" ] !== undefined || r [ "label" ] !== undefined )
2021-10-22 18:53:07 +02:00
. map ( ( r , i ) = > new PointRenderingConfig ( < PointRenderingConfigJson > r , context + ".mapRendering[" + i + "]" ) )
2021-10-20 02:01:27 +02:00
this . lineRendering = json . mapRendering
. filter ( r = > r [ "icon" ] === undefined && r [ "label" ] === undefined )
2021-10-22 18:53:07 +02:00
. map ( ( r , i ) = > new LineRenderingConfig ( < LineRenderingConfigJson > r , context + ".mapRendering[" + i + "]" ) )
2021-10-20 02:01:27 +02:00
2021-10-14 03:46:09 +02:00
2021-10-22 18:53:07 +02:00
this . tagRenderings = this . ExtractLayerTagRenderings ( json )
2021-10-23 00:31:41 +02:00
const missingIds = json . tagRenderings ? . filter ( tr = > typeof tr !== "string" && tr [ "builtin" ] === undefined && tr [ "id" ] === undefined && tr [ "rewrite" ] === undefined ) ? ? [ ] ;
2021-10-14 03:46:09 +02:00
if ( missingIds . length > 0 && official ) {
console . error ( "Some tagRenderings of" , this . id , "are missing an id:" , missingIds )
throw "Missing ids in tagrenderings"
}
2021-07-23 15:56:22 +02:00
this . filters = ( json . filter ? ? [ ] ) . map ( ( option , i ) = > {
return new FilterConfig ( option , ` ${ context } .filter-[ ${ i } ] ` )
} ) ;
2021-10-14 03:46:09 +02:00
if ( json [ "filters" ] !== undefined ) {
throw "Error in " + context + ": use 'filter' instead of 'filters'"
}
2021-07-23 15:56:22 +02:00
const titleIcons = [ ] ;
const defaultIcons = [
"phonelink" ,
"emaillink" ,
"wikipedialink" ,
"osmlink" ,
"sharelink" ,
] ;
for ( const icon of json . titleIcons ? ? defaultIcons ) {
if ( icon === "defaults" ) {
titleIcons . push ( . . . defaultIcons ) ;
} else {
titleIcons . push ( icon ) ;
}
}
2020-11-16 01:59:30 +01:00
2021-10-22 18:53:07 +02:00
this . titleIcons = this . ParseTagRenderings ( titleIcons , true ) ;
2021-03-15 16:23:04 +01:00
2021-10-19 02:31:32 +02:00
this . title = this . tr ( "title" , undefined ) ;
this . isShown = this . tr ( "isShown" , "yes" ) ;
2021-10-22 18:53:07 +02:00
2021-07-23 15:56:22 +02:00
this . deletion = null ;
if ( json . deletion === true ) {
json . deletion = { } ;
}
if ( json . deletion !== undefined && json . deletion !== false ) {
this . deletion = new DeleteConfig ( json . deletion , ` ${ context } .deletion ` ) ;
}
2021-03-15 16:23:04 +01:00
2021-10-14 03:46:09 +02:00
this . allowMove = null
if ( json . allowMove === false ) {
this . allowMove = null ;
} else if ( json . allowMove === true ) {
this . allowMove = new MoveConfig ( { } , context + ".allowMove" )
} else if ( json . allowMove !== undefined && json . allowMove !== false ) {
this . allowMove = new MoveConfig ( json . allowMove , context + ".allowMove" )
}
2021-07-23 15:56:22 +02:00
if ( json [ "showIf" ] !== undefined ) {
throw (
"Invalid key on layerconfig " +
this . id +
": showIf. Did you mean 'isShown' instead?"
) ;
}
2021-01-08 03:57:18 +01:00
}
2021-10-22 18:53:07 +02:00
public ExtractLayerTagRenderings ( json : LayerConfigJson ) : TagRenderingConfig [ ] {
if ( json . tagRenderings === undefined ) {
return [ ]
}
const normalTagRenderings : ( string | { builtin : string , override : any } | TagRenderingConfigJson ) [ ] = [ ]
2021-10-23 00:31:41 +02:00
const renderingsToRewrite : ( { rewrite : {
sourceString : string ,
into : string [ ]
} , renderings : ( string | { builtin : string , override : any } | TagRenderingConfigJson ) [ ] } ) [ ] = [ ]
2021-10-22 18:53:07 +02:00
for ( let i = 0 ; i < json . tagRenderings . length ; i ++ ) {
const tr = json . tagRenderings [ i ] ;
2021-10-23 00:31:41 +02:00
const rewriteDefined = tr [ "rewrite" ] !== undefined
2021-10-22 18:53:07 +02:00
const renderingsDefined = tr [ "renderings" ]
2021-10-23 00:31:41 +02:00
if ( ! rewriteDefined && ! renderingsDefined ) {
2021-10-22 18:53:07 +02:00
// @ts-ignore
normalTagRenderings . push ( tr )
continue
}
2021-10-23 00:31:41 +02:00
if ( rewriteDefined && renderingsDefined ) {
2021-10-22 18:53:07 +02:00
// @ts-ignore
2021-10-23 00:31:41 +02:00
renderingsToRewrite . push ( tr )
2021-10-22 18:53:07 +02:00
continue
}
2021-10-23 00:31:41 +02:00
throw ` Error in ${ this . _context } .tagrenderings[ ${ i } ]: got a value which defines either \` rewrite \` or \` renderings \` , but not both. Either define both or move the \` renderings \` out of this scope `
2021-10-22 18:53:07 +02:00
}
const allRenderings = this . ParseTagRenderings ( normalTagRenderings , false ) ;
2021-10-23 00:31:41 +02:00
if ( renderingsToRewrite . length === 0 ) {
2021-10-22 18:53:07 +02:00
return allRenderings
}
2021-10-23 00:31:41 +02:00
function prepConfig ( keyToRewrite : string , target :string , tr : TagRenderingConfigJson ) {
2021-10-22 18:53:07 +02:00
function replaceRecursive ( transl : string | any ) {
if ( typeof transl === "string" ) {
2021-10-23 00:31:41 +02:00
return transl . replace ( keyToRewrite , target )
2021-10-22 18:53:07 +02:00
}
if ( transl . map !== undefined ) {
return transl . map ( o = > replaceRecursive ( o ) )
}
transl = { . . . transl }
for ( const key in transl ) {
transl [ key ] = replaceRecursive ( transl [ key ] )
}
return transl
}
const orig = tr ;
tr = replaceRecursive ( tr )
tr . id = target + "-" + orig . id
tr . group = target
return tr
}
2021-10-23 00:31:41 +02:00
const rewriteGroups : Map < string , TagRenderingConfig [ ] > = new Map < string , TagRenderingConfig [ ] > ( )
for ( const rewriteGroup of renderingsToRewrite ) {
const tagRenderings = rewriteGroup . renderings
const textToReplace = rewriteGroup . rewrite . sourceString
const targets = rewriteGroup . rewrite . into
for ( const target of targets ) {
const parsedRenderings = this . ParseTagRenderings ( tagRenderings , false , tr = > prepConfig ( textToReplace , target , tr ) )
if ( ! rewriteGroups . has ( target ) ) {
rewriteGroups . set ( target , [ ] )
}
rewriteGroups . get ( target ) . push ( . . . parsedRenderings )
}
2021-10-22 18:53:07 +02:00
}
2021-10-23 00:31:41 +02:00
rewriteGroups . forEach ( ( group , groupName ) = > {
group . push ( new TagRenderingConfig ( {
id : "questions" ,
group :groupName
} ) )
} )
rewriteGroups . forEach ( group = > {
allRenderings . push ( . . . group )
} )
2021-10-22 18:53:07 +02:00
return allRenderings ;
}
2021-07-23 15:56:22 +02:00
public CustomCodeSnippets ( ) : string [ ] {
if ( this . calculatedTags === undefined ) {
return [ ] ;
}
return this . calculatedTags . map ( ( code ) = > code [ 1 ] ) ;
2021-07-22 11:29:09 +02:00
}
2021-01-08 03:57:18 +01:00
2021-07-23 15:56:22 +02:00
public ExtractImages ( ) : Set < string > {
const parts : Set < string > [ ] = [ ] ;
parts . push ( . . . this . tagRenderings ? . map ( ( tr ) = > tr . ExtractImages ( false ) ) ) ;
parts . push ( . . . this . titleIcons ? . map ( ( tr ) = > tr . ExtractImages ( true ) ) ) ;
for ( const preset of this . presets ) {
parts . push ( new Set < string > ( preset . description ? . ExtractImages ( false ) ) ) ;
2020-11-16 01:59:30 +01:00
}
2021-10-19 02:31:32 +02:00
for ( const pointRenderingConfig of this . mapRendering ) {
parts . push ( pointRenderingConfig . ExtractImages ( ) )
}
2021-07-23 15:56:22 +02:00
const allIcons = new Set < string > ( ) ;
for ( const part of parts ) {
part ? . forEach ( allIcons . add , allIcons ) ;
}
2021-04-09 02:57:06 +02:00
2021-07-23 15:56:22 +02:00
return allIcons ;
}
2021-10-22 18:53:07 +02:00
public isLeftRightSensitive ( ) : boolean {
2021-10-22 14:01:40 +02:00
return this . lineRendering . some ( lr = > lr . leftRightSensitive )
}
2021-08-07 23:11:34 +02:00
}