2020-11-11 16:23:49 +01:00
import { Translation } from "../../UI/i18n/Translation" ;
2021-08-07 23:11:34 +02:00
import { LayoutConfigJson } from "./Json/LayoutConfigJson" ;
import LayerConfig from "./LayerConfig" ;
2021-09-07 00:23:00 +02:00
import { LayerConfigJson } from "./Json/LayerConfigJson" ;
2021-09-29 16:55:05 +02:00
import Constants from "../Constants" ;
2021-10-14 21:43:14 +02:00
import TilesourceConfig from "./TilesourceConfig" ;
2022-02-09 22:37:21 +01:00
import { ExtractImages } from "./Conversion/FixImages" ;
2022-02-14 04:48:33 +01:00
import ExtraLinkConfig from "./ExtraLinkConfig" ;
2020-11-11 16:23:49 +01:00
export default class LayoutConfig {
2022-03-08 04:09:03 +01:00
public static readonly defaultSocialImage = "assets/SocialImage.png"
2020-11-11 16:23:49 +01:00
public readonly id : string ;
public readonly maintainer : string ;
2021-04-09 17:56:13 +02:00
public readonly credits? : string ;
2020-11-11 16:23:49 +01:00
public readonly version : string ;
public readonly language : string [ ] ;
public readonly title : Translation ;
2021-12-21 18:35:31 +01:00
public readonly shortDescription : Translation ;
2020-11-11 16:23:49 +01:00
public readonly description : Translation ;
public readonly descriptionTail? : Translation ;
public readonly icon : string ;
public readonly socialImage? : string ;
public readonly startZoom : number ;
public readonly startLat : number ;
public readonly startLon : number ;
public readonly widenFactor : number ;
public readonly defaultBackgroundId? : string ;
2021-05-17 00:18:21 +02:00
public layers : LayerConfig [ ] ;
2021-10-14 21:43:14 +02:00
public tileLayerSources : TilesourceConfig [ ]
2021-01-06 02:52:38 +01:00
public readonly clustering ? : {
2021-01-05 00:21:00 +01:00
maxZoom : number ,
2021-10-13 00:43:19 +02:00
minNeededElements : number ,
2021-01-04 20:09:07 +01:00
} ;
2020-11-11 16:23:49 +01:00
public readonly hideFromOverview : boolean ;
2021-05-27 18:56:02 +02:00
public lockLocation : boolean | [ [ number , number ] , [ number , number ] ] ;
2020-11-11 16:23:49 +01:00
public readonly enableUserBadge : boolean ;
public readonly enableShareScreen : boolean ;
public readonly enableMoreQuests : boolean ;
public readonly enableAddNewPoints : boolean ;
public readonly enableLayers : boolean ;
public readonly enableSearch : boolean ;
public readonly enableGeolocation : boolean ;
2021-01-04 18:55:10 +01:00
public readonly enableBackgroundLayerSelection : boolean ;
2021-05-17 00:18:21 +02:00
public readonly enableShowAllQuestions : boolean ;
2021-07-16 01:42:09 +02:00
public readonly enableExportButton : boolean ;
2021-07-28 02:51:07 +02:00
public readonly enablePdfDownload : boolean ;
2020-11-11 16:23:49 +01:00
public readonly customCss? : string ;
2021-10-25 20:38:57 +02:00
2021-09-29 16:55:05 +02:00
public readonly overpassUrl : string [ ] ;
2021-08-23 15:48:42 +02:00
public readonly overpassTimeout : number ;
2021-10-15 05:20:02 +02:00
public readonly overpassMaxZoom : number
public readonly osmApiTileSize : number
2021-09-18 02:32:40 +02:00
public readonly official : boolean ;
2022-02-14 04:48:33 +01:00
public readonly usedImages : string [ ]
public readonly extraLink? : ExtraLinkConfig
2022-07-01 00:16:05 +02:00
public readonly definedAtUrl? : string ;
2022-04-18 02:39:30 +02:00
public readonly definitionRaw? : string ;
2022-07-01 00:16:05 +02:00
constructor ( json : LayoutConfigJson , official = true , options ? : {
2022-04-18 02:39:30 +02:00
definedAtUrl? : string ,
definitionRaw? : string
} ) {
2021-09-18 02:32:40 +02:00
this . official = official ;
2020-11-11 16:23:49 +01:00
this . id = json . id ;
2022-04-18 02:39:30 +02:00
this . definedAtUrl = options ? . definedAtUrl
this . definitionRaw = options ? . definitionRaw
2022-01-26 21:40:38 +01:00
if ( official ) {
if ( json . id . toLowerCase ( ) !== json . id ) {
throw "The id of a theme should be lowercase: " + json . id
2022-01-08 13:04:11 +01:00
}
2022-01-26 21:40:38 +01:00
if ( json . id . match ( /[a-z0-9-_]/ ) == null ) {
throw "The id of a theme should match [a-z0-9-_]*: " + json . id
2022-01-08 13:04:11 +01:00
}
2021-12-30 22:36:34 +01:00
}
2022-07-01 00:16:05 +02:00
const context = this . id
2020-11-11 16:23:49 +01:00
this . maintainer = json . maintainer ;
2021-04-09 17:56:13 +02:00
this . credits = json . credits ;
2020-11-11 16:23:49 +01:00
this . version = json . version ;
2022-02-01 04:14:54 +01:00
this . language = json . mustHaveLanguage ? ? Array . from ( Object . keys ( json . title ) ) ;
2022-02-18 23:10:27 +01:00
this . usedImages = Array . from ( new ExtractImages ( official , undefined ) . convertStrict ( json , "while extracting the images of " + json . id + " " + context ? ? "" ) ) . sort ( )
2021-12-21 18:35:31 +01:00
{
2022-01-27 01:23:04 +01:00
if ( typeof json . title === "string" ) {
2022-01-31 20:52:56 +01:00
throw ` The title of a theme should always be a translation, as it sets the corresponding languages ( ${ context } .title). The themenID is ${ this . id } ; the offending object is ${ JSON . stringify ( json . title ) } which is a ${ typeof json . title } ) `
2022-01-27 01:23:04 +01:00
}
2021-12-21 18:35:31 +01:00
if ( this . language . length == 0 ) {
throw ` No languages defined. Define at least one language. ( ${ context } .languages) `
}
if ( json . title === undefined ) {
throw "Title not defined in " + this . id ;
}
if ( json . description === undefined ) {
throw "Description not defined in " + this . id ;
}
if ( json . widenFactor <= 0 ) {
throw "Widenfactor too small, shoud be > 0"
}
if ( json . widenFactor > 20 ) {
throw "Widenfactor is very big, use a value between 1 and 5 (current value is " + json . widenFactor + ") at " + context
}
if ( json [ "hideInOverview" ] ) {
throw "The json for " + this . id + " contains a 'hideInOverview'. Did you mean hideFromOverview instead?"
}
if ( json . layers === undefined ) {
throw "Got undefined layers for " + json . id + " at " + context
}
2020-11-11 16:23:49 +01:00
}
2022-07-01 00:16:05 +02:00
this . title = new Translation ( json . title , "themes:" + context + ".title" ) ;
this . description = new Translation ( json . description , "themes:" + context + ".description" ) ;
this . shortDescription = json . shortDescription === undefined ? this . description . FirstSentence ( ) : new Translation ( json . shortDescription , "themes:" + context + ".shortdescription" ) ;
this . descriptionTail = json . descriptionTail === undefined ? undefined : new Translation ( json . descriptionTail , "themes:" + context + ".descriptionTail" ) ;
2020-11-11 16:23:49 +01:00
this . icon = json . icon ;
2022-03-08 04:09:03 +01:00
this . socialImage = json . socialImage ? ? LayoutConfig . defaultSocialImage ;
if ( this . socialImage === "" ) {
2022-01-26 21:40:38 +01:00
if ( official ) {
2022-03-08 04:09:03 +01:00
throw "Theme " + json . id + " has empty string as social image"
2022-01-18 20:18:12 +01:00
}
}
2020-11-11 16:23:49 +01:00
this . startZoom = json . startZoom ;
this . startLat = json . startLat ;
this . startLon = json . startLon ;
2021-09-22 16:07:56 +02:00
this . widenFactor = json . widenFactor ? ? 1.5 ;
2021-11-07 16:34:51 +01:00
2020-11-11 16:23:49 +01:00
this . defaultBackgroundId = json . defaultBackgroundId ;
2021-11-07 16:34:51 +01:00
this . tileLayerSources = ( json . tileLayerSources ? ? [ ] ) . map ( ( config , i ) = > new TilesourceConfig ( config , ` ${ this . id } .tileLayerSources[ ${ i } ] ` ) )
2021-12-21 18:35:31 +01:00
// At this point, layers should be expanded and validated either by the generateScript or the LegacyJsonConvert
this . layers = json . layers . map ( lyrJson = > new LayerConfig ( < LayerConfigJson > lyrJson , json . id + ".layers." + lyrJson [ "id" ] , official ) ) ;
2021-11-07 16:34:51 +01:00
2022-07-01 00:16:05 +02:00
this . extraLink = new ExtraLinkConfig ( json . extraLink ? ? {
2022-02-14 04:48:33 +01:00
icon : "./assets/svg/pop-out.svg" ,
2022-07-01 00:16:05 +02:00
href : "https://{basepath}/{theme}.html?lat={lat}&lon={lon}&z={zoom}&language={language}" ,
2022-02-14 04:48:33 +01:00
newTab : true ,
2022-07-01 00:16:05 +02:00
requirements : [ "iframe" , "no-welcome-message" ]
} , context + ".extraLink" )
2021-11-07 16:34:51 +01:00
2021-06-15 01:24:04 +02:00
this . clustering = {
2021-01-05 00:21:00 +01:00
maxZoom : 16 ,
2022-01-27 01:23:04 +01:00
minNeededElements : 250 ,
2021-01-05 00:21:00 +01:00
} ;
2021-11-07 16:34:51 +01:00
if ( json . clustering === false ) {
2021-10-13 01:28:46 +02:00
this . clustering = {
maxZoom : 0 ,
minNeededElements : 100000 ,
} ;
2021-11-07 16:34:51 +01:00
} else if ( json . clustering ) {
2021-01-04 20:09:07 +01:00
this . clustering = {
2021-01-06 02:52:38 +01:00
maxZoom : json.clustering.maxZoom ? ? 18 ,
2022-01-27 01:23:04 +01:00
minNeededElements : json.clustering.minNeededElements ? ? 250 ,
2021-01-04 18:55:10 +01:00
}
}
2021-01-05 00:21:00 +01:00
2020-11-11 16:23:49 +01:00
this . hideFromOverview = json . hideFromOverview ? ? false ;
2021-11-07 16:34:51 +01:00
this . lockLocation = < [ [ number , number ] , [ number , number ] ] > json . lockLocation ? ? undefined ;
2020-11-11 16:23:49 +01:00
this . enableUserBadge = json . enableUserBadge ? ? true ;
this . enableShareScreen = json . enableShareScreen ? ? true ;
this . enableMoreQuests = json . enableMoreQuests ? ? true ;
this . enableLayers = json . enableLayers ? ? true ;
this . enableSearch = json . enableSearch ? ? true ;
this . enableGeolocation = json . enableGeolocation ? ? true ;
this . enableAddNewPoints = json . enableAddNewPoints ? ? true ;
this . enableBackgroundLayerSelection = json . enableBackgroundLayerSelection ? ? true ;
2021-05-17 00:18:21 +02:00
this . enableShowAllQuestions = json . enableShowAllQuestions ? ? false ;
2021-07-28 02:51:07 +02:00
this . enableExportButton = json . enableDownload ? ? false ;
this . enablePdfDownload = json . enablePdfDownload ? ? false ;
2020-11-11 16:23:49 +01:00
this . customCss = json . customCss ;
2021-09-29 16:55:05 +02:00
this . overpassUrl = Constants . defaultOverpassUrls
2021-11-07 16:34:51 +01:00
if ( json . overpassUrl !== undefined ) {
if ( typeof json . overpassUrl === "string" ) {
2021-09-29 16:55:05 +02:00
this . overpassUrl = [ json . overpassUrl ]
2021-11-07 16:34:51 +01:00
} else {
2021-09-29 16:55:05 +02:00
this . overpassUrl = json . overpassUrl
}
}
2021-08-23 15:48:42 +02:00
this . overpassTimeout = json . overpassTimeout ? ? 30
2022-01-15 02:44:11 +01:00
this . overpassMaxZoom = json . overpassMaxZoom ? ? 16
2021-10-15 05:20:02 +02:00
this . osmApiTileSize = json . osmApiTileSize ? ? this . overpassMaxZoom + 1
2021-06-22 00:29:07 +02:00
2021-06-22 03:16:45 +02:00
}
2021-03-24 01:25:57 +01:00
public CustomCodeSnippets ( ) : string [ ] {
2021-09-18 02:32:40 +02:00
if ( this . official ) {
2021-03-24 01:25:57 +01:00
return [ ] ;
}
const msg = "<br/><b>This layout uses <span class='alert'>custom javascript</span>, loaded for the wide internet. The code is printed below, please report suspicious code on the issue tracker of MapComplete:</b><br/>"
const custom = [ ] ;
for ( const layer of this . layers ) {
2021-04-17 11:37:22 +02:00
custom . push ( . . . layer . CustomCodeSnippets ( ) . map ( code = > code + "<br />" ) )
2021-03-24 01:25:57 +01:00
}
if ( custom . length === 0 ) {
return custom ;
}
custom . splice ( 0 , 0 , msg ) ;
return custom ;
}
2021-04-17 11:37:22 +02:00
2021-11-07 16:34:51 +01:00
public isLeftRightSensitive() {
2021-10-22 14:01:40 +02:00
return this . layers . some ( l = > l . isLeftRightSensitive ( ) )
}
2021-12-04 21:49:17 +01:00
public getMatchingLayer ( tags : any ) : LayerConfig | undefined {
if ( tags === undefined ) {
2021-11-08 20:49:51 +01:00
return undefined
}
for ( const layer of this . layers ) {
if ( layer . source . osmTags . matchesProperties ( tags ) ) {
return layer
}
}
return undefined
}
2022-07-01 00:16:05 +02:00
2020-11-11 16:23:49 +01:00
}