Full code cleanup

This commit is contained in:
Pieter Vander Vennet 2021-11-07 16:34:51 +01:00
parent 8e6ee8c87f
commit bd21212eba
246 changed files with 19418 additions and 11729 deletions

View file

@ -17,7 +17,6 @@ export default class Constants {
// Doesn't support nwr: "https://overpass.openstreetmap.fr/api/interpreter"
]
// The user journey states thresholds when a new feature gets unlocked
public static userJourney = {

View file

@ -44,13 +44,13 @@ export class Denomination {
get human(): Translation {
return this._human.Clone()
}
get humanSingular(): Translation {
return (this._humanSingular ?? this._human).Clone()
}
getToggledHuman(isSingular: UIEventSource<boolean>): BaseUIElement{
if(this._humanSingular === undefined){
getToggledHuman(isSingular: UIEventSource<boolean>): BaseUIElement {
if (this._humanSingular === undefined) {
return this.human
}
return new Toggle(
@ -59,7 +59,7 @@ export class Denomination {
isSingular
)
}
public canonicalValue(value: string, actAsDefault?: boolean) {
if (value === undefined) {
return undefined;
@ -68,12 +68,12 @@ export class Denomination {
if (stripped === null) {
return null;
}
if(stripped === "1" && this._canonicalSingular !== undefined){
return "1 "+this._canonicalSingular
if (stripped === "1" && this._canonicalSingular !== undefined) {
return "1 " + this._canonicalSingular
}
return stripped + " " + this.canonical;
}
/**
* Returns the core value (without unit) if:
* - the value ends with the canonical or an alternative value (or begins with if prefix is set)
@ -89,30 +89,31 @@ export class Denomination {
value = value.toLowerCase()
const self = this;
function startsWith(key){
if(self.prefix){
function startsWith(key) {
if (self.prefix) {
return value.startsWith(key)
}else{
} else {
return value.endsWith(key)
}
}
function substr(key){
if(self.prefix){
function substr(key) {
if (self.prefix) {
return value.substr(key.length).trim()
}else{
} else {
return value.substring(0, value.length - key.length).trim()
}
}
if(this.canonical !== "" && startsWith(this.canonical.toLowerCase())){
if (this.canonical !== "" && startsWith(this.canonical.toLowerCase())) {
return substr(this.canonical)
}
if(this._canonicalSingular !== undefined && this._canonicalSingular !== "" && startsWith(this._canonicalSingular)){
}
if (this._canonicalSingular !== undefined && this._canonicalSingular !== "" && startsWith(this._canonicalSingular)) {
return substr(this._canonicalSingular)
}
for (const alternativeValue of this.alternativeDenominations) {
if (startsWith(alternativeValue)) {
return substr(alternativeValue);

View file

@ -1,10 +1,9 @@
import {UIEventSource} from "../Logic/UIEventSource";
import LayerConfig from "./ThemeConfig/LayerConfig";
import {And} from "../Logic/Tags/And";
import FilterConfig from "./ThemeConfig/FilterConfig";
export default interface FilteredLayer {
readonly isDisplayed: UIEventSource<boolean>;
readonly appliedFilters: UIEventSource<{filter: FilterConfig, selected: number}[]>;
readonly appliedFilters: UIEventSource<{ filter: FilterConfig, selected: number }[]>;
readonly layerDef: LayerConfig;
}

View file

@ -18,7 +18,7 @@ export default class FilterConfig {
if (json.id === undefined) {
throw `A filter without id was found at ${context}`
}
if(json.id.match(/^[a-zA-Z0-9_-]*$/) === null){
if (json.id.match(/^[a-zA-Z0-9_-]*$/) === null) {
throw `A filter with invalid id was found at ${context}. Ids should only contain letters, numbers or - _`
}
@ -42,9 +42,9 @@ export default class FilterConfig {
return {question: question, osmTags: osmTags};
});
if(this.options.length > 1 && this.options[0].osmTags["and"]?.length !== 0){
throw "Error in "+context+"."+this.id+": the first option of a multi-filter should always be the 'reset' option and not have any filters"
if (this.options.length > 1 && this.options[0].osmTags["and"]?.length !== 0) {
throw "Error in " + context + "." + this.id + ": the first option of a multi-filter should always be the 'reset' option and not have any filters"
}
}
}

View file

@ -64,14 +64,14 @@ export interface LayerConfigJson {
* NOTE: the previous format was 'overpassTags: AndOrTagConfigJson | string', which is interpreted as a shorthand for source: {osmTags: "key=value"}
* While still supported, this is considered deprecated
*/
source: ({ osmTags: AndOrTagConfigJson | string, overpassScript?: string } |
source: ({ osmTags: AndOrTagConfigJson | string, overpassScript?: string } |
{ osmTags: AndOrTagConfigJson | string, geoJson: string, geoJsonZoomLevel?: number, isOsmCache?: boolean, mercatorCrs?: boolean }) & ({
/**
* The maximum amount of seconds that a tile is allowed to linger in the cache
*/
maxCacheAge?: number
})
/**
*
* A list of extra tags to calculate, specified as "keyToAssignTo=javascript-expression".
@ -93,8 +93,8 @@ export interface LayerConfigJson {
/**
* This tag rendering should either be 'yes' or 'no'. If 'no' is returned, then the feature will be hidden from view.
* This is useful to hide certain features from view.
*
* This is useful to hide certain features from view.
*
* Important: hiding features does not work dynamically, but is only calculated when the data is first renders.
* This implies that it is not possible to hide a feature after a tagging change
*
@ -207,15 +207,13 @@ export interface LayerConfigJson {
* This is mainly create questions for a 'left' and a 'right' side of the road.
* These will be grouped and questions will be asked together
*/
tagRenderings?: (string | {builtin: string, override: any} | TagRenderingConfigJson | {
tagRenderings?: (string | { builtin: string, override: any } | TagRenderingConfigJson | {
rewrite: {
sourceString: string,
into: string[]
}[],
renderings: (string | {builtin: string, override: any} | TagRenderingConfigJson)[]
renderings: (string | { builtin: string, override: any } | TagRenderingConfigJson)[]
}) [],
/**
@ -273,15 +271,15 @@ export interface LayerConfigJson {
/**
* Indicates if a point can be moved and configures the modalities.
*
*
* A feature can be moved by MapComplete if:
*
*
* - It is a point
* - The point is _not_ part of a way or a a relation.
*
*
* Off by default. Can be enabled by setting this flag or by configuring.
*/
allowMove?: boolean | MoveConfigJson
allowMove?: boolean | MoveConfigJson
/**
* IF set, a 'split this road' button is shown

View file

@ -1,4 +1,3 @@
import {TagRenderingConfigJson} from "./TagRenderingConfigJson";
import {LayerConfigJson} from "./LayerConfigJson";
import TilesourceConfigJson from "./TilesourceConfigJson";
@ -15,7 +14,7 @@ import TilesourceConfigJson from "./TilesourceConfigJson";
* General remark: a type (string | any) indicates either a fixed or a translatable string.
*/
export interface LayoutConfigJson {
/**
* The id of this layout.
*
@ -216,7 +215,7 @@ export interface LayoutConfigJson {
*/
maxZoom?: number,
/**
* The number of elements per tile needed to start clustering
* The number of elements per tile needed to start clustering
* If clustering is defined, defaults to 25
*/
minNeededElements?: number

View file

@ -2,9 +2,9 @@ import {TagRenderingConfigJson} from "./TagRenderingConfigJson";
/**
* The LineRenderingConfig gives all details onto how to render a single line of a feature.
*
*
* This can be used if:
*
*
* - The feature is a line
* - The feature is an area
*/
@ -28,9 +28,9 @@ export default interface LineRenderingConfigJson {
dashArray?: string | TagRenderingConfigJson
/**
* The number of pixels this line should be moved.
* The number of pixels this line should be moved.
* Use a positive numbe to move to the right, a negative to move to the left (left/right as defined by the drawing direction of the line).
*
*
* IMPORTANT: MapComplete will already normalize 'key:both:property' and 'key:both' into the corresponding 'key:left' and 'key:right' tagging (same for 'sidewalk=left/right/both' which is rewritten to 'sidewalk:left' and 'sidewalk:right')
* This simplifies programming. Refer to the CalculatedTags.md-documentation for more details
*/

View file

@ -3,9 +3,9 @@ import {AndOrTagConfigJson} from "./TagConfigJson";
/**
* The PointRenderingConfig gives all details onto how to render a single point of a feature.
*
*
* This can be used if:
*
*
* - The feature is a point
* - To render something at the centroid of an area, or at the start, end or projected centroid of a way
*/
@ -16,7 +16,7 @@ export default interface PointRenderingConfigJson {
* Using `location: ["point", "centroid"] will always render centerpoint
*/
location: ("point" | "centroid" | "start" | "end")[]
/**
* The icon for an element.
* Note that this also doubles as the icon for this layer (rendered with the overpass-tags) ánd the icon in the presets.

View file

@ -17,7 +17,7 @@ export interface TagRenderingConfigJson {
* The first tagRendering of a group will always be a sticky element.
*/
group?: string
/**
* Renders this value. Note that "{key}"-parts are substituted by the corresponding values of the element.
* If neither 'textFieldQuestion' nor 'mappings' are defined, this text is simply shown as default value.
@ -89,13 +89,13 @@ export interface TagRenderingConfigJson {
* Allows fixed-tag inputs, shown either as radiobuttons or as checkboxes
*/
mappings?: {
/**
* If this condition is met, then the text under `then` will be shown.
* If no value matches, and the user selects this mapping as an option, then these tags will be uploaded to OSM.
*
*
* For example: {'if': 'diet:vegetarion=yes', 'then':'A vegetarian option is offered here'}
*
*
* This can be an substituting-tag as well, e.g. {'if': 'addr:street:={_calculated_nearby_streetname}', 'then': '{_calculated_nearby_streetname}'}
*/
if: AndOrTagConfigJson | string,

View file

@ -12,12 +12,11 @@ export default interface UnitConfigJson {
/**
* The possible denominations
*/
applicableUnits:ApplicableUnitJson[]
applicableUnits: ApplicableUnitJson[]
}
export interface ApplicableUnitJson
{
export interface ApplicableUnitJson {
/**
* The canonical value which will be added to the text.
* e.g. "m" for meters
@ -28,8 +27,8 @@ export interface ApplicableUnitJson
* The canonical denomination in the case that the unit is precisely '1'
*/
canonicalDenominationSingular?: string,
/**
* A list of alternative values which can occur in the OSM database - used for parsing.
*/

View file

@ -264,7 +264,7 @@ export default class LayerConfig extends WithContextLoader {
}
}
public defaultIcon() : BaseUIElement | undefined{
public defaultIcon(): BaseUIElement | undefined {
const mapRendering = this.mapRendering.filter(r => r.location.has("point"))[0]
if (mapRendering === undefined) {
return undefined

View file

@ -52,8 +52,8 @@ export default class LayoutConfig {
public readonly overpassMaxZoom: number
public readonly osmApiTileSize: number
public readonly official: boolean;
public readonly trackAllNodes : boolean;
public readonly trackAllNodes: boolean;
constructor(json: LayoutConfigJson, official = true, context?: string) {
this.official = official;
this.id = json.id;
@ -63,7 +63,7 @@ export default class LayoutConfig {
this.version = json.version;
this.language = [];
this.trackAllNodes = false
if (typeof json.language === "string") {
this.language = [json.language];
} else {
@ -87,32 +87,32 @@ export default class LayoutConfig {
this.startZoom = json.startZoom;
this.startLat = json.startLat;
this.startLon = json.startLon;
if(json.widenFactor <= 0){
throw "Widenfactor too small, shoud be > 0"
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.widenFactor > 20) {
throw "Widenfactor is very big, use a value between 1 and 5 (current value is " + json.widenFactor + ") at " + context
}
this.widenFactor = json.widenFactor ?? 1.5;
this.defaultBackgroundId = json.defaultBackgroundId;
this.tileLayerSources = (json.tileLayerSources??[]).map((config, i) => new TilesourceConfig(config, `${this.id}.tileLayerSources[${i}]`))
const layerInfo = LayoutConfig.ExtractLayers(json, official, context);
this.tileLayerSources = (json.tileLayerSources ?? []).map((config, i) => new TilesourceConfig(config, `${this.id}.tileLayerSources[${i}]`))
const layerInfo = LayoutConfig.ExtractLayers(json, official, context);
this.layers = layerInfo.layers
this.trackAllNodes = layerInfo.extractAllNodes
this.clustering = {
maxZoom: 16,
minNeededElements: 25,
};
if(json.clustering === false){
if (json.clustering === false) {
this.clustering = {
maxZoom: 0,
minNeededElements: 100000,
};
}else if (json.clustering) {
} else if (json.clustering) {
this.clustering = {
maxZoom: json.clustering.maxZoom ?? 18,
minNeededElements: json.clustering.minNeededElements ?? 25,
@ -124,7 +124,7 @@ export default class LayoutConfig {
if (json.hideInOverview) {
throw "The json for " + this.id + " contains a 'hideInOverview'. Did you mean hideFromOverview instead?"
}
this.lockLocation = <[[number, number], [number, number]]> json.lockLocation ?? undefined;
this.lockLocation = <[[number, number], [number, number]]>json.lockLocation ?? undefined;
this.enableUserBadge = json.enableUserBadge ?? true;
this.enableShareScreen = json.enableShareScreen ?? true;
this.enableMoreQuests = json.enableMoreQuests ?? true;
@ -139,10 +139,10 @@ export default class LayoutConfig {
this.enableIframePopout = json.enableIframePopout ?? true
this.customCss = json.customCss;
this.overpassUrl = Constants.defaultOverpassUrls
if(json.overpassUrl !== undefined){
if(typeof json.overpassUrl === "string"){
if (json.overpassUrl !== undefined) {
if (typeof json.overpassUrl === "string") {
this.overpassUrl = [json.overpassUrl]
}else{
} else {
this.overpassUrl = json.overpassUrl
}
}
@ -152,11 +152,11 @@ export default class LayoutConfig {
}
private static ExtractLayers(json: LayoutConfigJson, official: boolean, context: string): {layers: LayerConfig[], extractAllNodes: boolean} {
private static ExtractLayers(json: LayoutConfigJson, official: boolean, context: string): { layers: LayerConfig[], extractAllNodes: boolean } {
const result: LayerConfig[] = []
let exportAllNodes = false
json.layers.forEach((layer, i) => {
if (typeof layer === "string") {
if (AllKnownLayers.sharedLayersJson.get(layer) !== undefined) {
if (json.overrideAll !== undefined) {
@ -183,7 +183,7 @@ export default class LayoutConfig {
result.push(newLayer)
return
}
// @ts-ignore
let names = layer.builtin;
if (typeof names === "string") {
@ -191,11 +191,11 @@ export default class LayoutConfig {
}
names.forEach(name => {
if(name === "type_node"){
if (name === "type_node") {
// This is a very special layer which triggers special behaviour
exportAllNodes = true;
}
const shared = AllKnownLayers.sharedLayersJson.get(name);
if (shared === undefined) {
throw `Unknown shared/builtin layer ${name} at ${context}.layers[${i}]. Available layers are ${Array.from(AllKnownLayers.sharedLayersJson.keys()).join(", ")}`;
@ -287,8 +287,8 @@ export default class LayoutConfig {
})
return new LayoutConfig(JSON.parse(originalJson), false, "Layout rewriting")
}
public isLeftRightSensitive(){
public isLeftRightSensitive() {
return this.layers.some(l => l.isLeftRightSensitive())
}

View file

@ -1,6 +1,4 @@
import PointRenderingConfigJson from "./Json/PointRenderingConfigJson";
import WithContextLoader from "./WithContextLoader";
import {UIEventSource} from "../../Logic/UIEventSource";
import TagRenderingConfig from "./TagRenderingConfig";
import {Utils} from "../../Utils";
import LineRenderingConfigJson from "./Json/LineRenderingConfigJson";

View file

@ -15,7 +15,7 @@ import {VariableUiElement} from "../../UI/Base/VariableUIElement";
export default class PointRenderingConfig extends WithContextLoader {
private static readonly allowed_location_codes = new Set<string>(["point", "centroid","start","end"])
private static readonly allowed_location_codes = new Set<string>(["point", "centroid", "start", "end"])
public readonly location: Set<"point" | "centroid" | "start" | "end">
public readonly icon: TagRenderingConfig;
@ -26,34 +26,34 @@ export default class PointRenderingConfig extends WithContextLoader {
constructor(json: PointRenderingConfigJson, context: string) {
super(json, context)
if(typeof json.location === "string"){
if (typeof json.location === "string") {
json.location = [json.location]
}
this.location = new Set(json.location)
this.location.forEach(l => {
const allowed = PointRenderingConfig.allowed_location_codes
if(!allowed.has(l)){
if (!allowed.has(l)) {
throw `A point rendering has an invalid location: '${l}' is not one of ${Array.from(allowed).join(", ")} (at ${context}.location)`
}
})
if(json.icon === undefined && json.label === undefined){
if (json.icon === undefined && json.label === undefined) {
throw `A point rendering should define at least an icon or a label`
}
if(this.location.size == 0){
throw "A pointRendering should have at least one 'location' to defined where it should be rendered. (At "+context+".location)"
if (this.location.size == 0) {
throw "A pointRendering should have at least one 'location' to defined where it should be rendered. (At " + context + ".location)"
}
this.icon = this.tr("icon", "");
this.iconBadges = (json.iconBadges ?? []).map((overlay, i) => {
let tr : TagRenderingConfig;
let tr: TagRenderingConfig;
if (typeof overlay.then === "string" &&
SharedTagRenderings.SharedIcons.get(overlay.then) !== undefined) {
tr = SharedTagRenderings.SharedIcons.get(overlay.then);
}else{
} else {
tr = new TagRenderingConfig(
overlay.then,
`iconBadges.${i}`
@ -77,6 +77,43 @@ export default class PointRenderingConfig extends WithContextLoader {
this.rotation = this.tr("rotation", "0");
}
/**
* Given a single HTML spec (either a single image path OR "image_path_to_known_svg:fill-colour", returns a fixedUIElement containing that
* The element will fill 100% and be positioned absolutely with top:0 and left: 0
*/
private static FromHtmlSpec(htmlSpec: string, style: string, isBadge = false): BaseUIElement {
if (htmlSpec === undefined) {
return undefined;
}
const match = htmlSpec.match(/([a-zA-Z0-9_]*):([^;]*)/);
if (match !== null && Svg.All[match[1] + ".svg"] !== undefined) {
const svg = (Svg.All[match[1] + ".svg"] as string)
const targetColor = match[2]
const img = new Img(svg.replace(/#000000/g, targetColor), true)
.SetStyle(style)
if (isBadge) {
img.SetClass("badge")
}
return img
} else {
return new FixedUiElement(`<img src="${htmlSpec}" style="${style}" />`);
}
}
private static FromHtmlMulti(multiSpec: string, rotation: string, isBadge: boolean, defaultElement: BaseUIElement = undefined) {
if (multiSpec === undefined) {
return defaultElement
}
const style = `width:100%;height:100%;transform: rotate( ${rotation} );display:block;position: absolute; top: 0; left: 0`;
const htmlDefs = multiSpec.trim()?.split(";") ?? []
const elements = Utils.NoEmpty(htmlDefs).map(def => PointRenderingConfig.FromHtmlSpec(def, style, isBadge))
if (elements.length === 0) {
return defaultElement
} else {
return new Combine(elements).SetClass("relative block w-full h-full")
}
}
public ExtractImages(): Set<string> {
const parts: Set<string>[] = [];
@ -92,44 +129,6 @@ export default class PointRenderingConfig extends WithContextLoader {
return allIcons;
}
/**
* Given a single HTML spec (either a single image path OR "image_path_to_known_svg:fill-colour", returns a fixedUIElement containing that
* The element will fill 100% and be positioned absolutely with top:0 and left: 0
*/
private static FromHtmlSpec(htmlSpec: string, style: string, isBadge = false): BaseUIElement {
if (htmlSpec === undefined) {
return undefined;
}
const match = htmlSpec.match(/([a-zA-Z0-9_]*):([^;]*)/);
if (match !== null && Svg.All[match[1] + ".svg"] !== undefined) {
const svg = (Svg.All[match[1] + ".svg"] as string)
const targetColor = match[2]
const img = new Img(svg.replace(/#000000/g, targetColor), true)
.SetStyle(style)
if(isBadge){
img.SetClass("badge")
}
return img
} else {
return new FixedUiElement(`<img src="${htmlSpec}" style="${style}" />`);
}
}
private static FromHtmlMulti(multiSpec: string, rotation: string , isBadge: boolean, defaultElement: BaseUIElement = undefined){
if(multiSpec === undefined){
return defaultElement
}
const style = `width:100%;height:100%;transform: rotate( ${rotation} );display:block;position: absolute; top: 0; left: 0`;
const htmlDefs = multiSpec.trim()?.split(";") ?? []
const elements = Utils.NoEmpty(htmlDefs).map(def => PointRenderingConfig.FromHtmlSpec(def, style, isBadge))
if (elements.length === 0) {
return defaultElement
} else {
return new Combine(elements).SetClass("relative block w-full h-full")
}
}
public GetSimpleIcon(tags: UIEventSource<any>): BaseUIElement {
const self = this;
if (this.icon === undefined) {
@ -137,58 +136,16 @@ export default class PointRenderingConfig extends WithContextLoader {
}
return new VariableUiElement(tags.map(tags => {
const rotation = Utils.SubstituteKeys(self.rotation?.GetRenderValue(tags)?.txt ?? "0deg", tags)
const htmlDefs = Utils.SubstituteKeys(self.icon.GetRenderValue(tags)?.txt, tags)
let defaultPin : BaseUIElement = undefined
if(self.label === undefined){
defaultPin = Svg.teardrop_with_hole_green_svg()
let defaultPin: BaseUIElement = undefined
if (self.label === undefined) {
defaultPin = Svg.teardrop_with_hole_green_svg()
}
return PointRenderingConfig.FromHtmlMulti(htmlDefs, rotation,false, defaultPin)
return PointRenderingConfig.FromHtmlMulti(htmlDefs, rotation, false, defaultPin)
})).SetClass("w-full h-full block")
}
private GetBadges(tags: UIEventSource<any>): BaseUIElement {
if (this.iconBadges.length === 0) {
return undefined
}
return new VariableUiElement(
tags.map(tags => {
const badgeElements = this.iconBadges.map(badge => {
if (!badge.if.matchesProperties(tags)) {
// Doesn't match...
return undefined
}
const htmlDefs = Utils.SubstituteKeys(badge.then.GetRenderValue(tags)?.txt, tags)
const badgeElement= PointRenderingConfig.FromHtmlMulti(htmlDefs, "0", true)?.SetClass("block relative")
if(badgeElement === undefined){
return undefined;
}
return new Combine([badgeElement]).SetStyle("width: 1.5rem").SetClass("block")
})
return new Combine(badgeElements).SetClass("inline-flex h-full")
})).SetClass("absolute bottom-0 right-1/3 h-1/2 w-0")
}
private GetLabel(tags: UIEventSource<any>): BaseUIElement {
if (this.label === undefined) {
return undefined;
}
const self = this;
return new VariableUiElement(tags.map(tags => {
const label = self.label
?.GetRenderValue(tags)
?.Subs(tags)
?.SetClass("block text-center")
return new Combine([label]).SetClass("flex flex-col items-center mt-1")
}))
}
public GenerateLeafletStyle(
tags: UIEventSource<any>,
clickable: boolean,
@ -246,9 +203,9 @@ export default class PointRenderingConfig extends WithContextLoader {
const iconAndBadges = new Combine([this.GetSimpleIcon(tags), this.GetBadges(tags)])
.SetClass("block relative")
if(!options?.noSize){
if (!options?.noSize) {
iconAndBadges.SetStyle(`width: ${iconW}px; height: ${iconH}px`)
}else{
} else {
iconAndBadges.SetClass("w-full h-full")
}
@ -264,4 +221,46 @@ export default class PointRenderingConfig extends WithContextLoader {
};
}
private GetBadges(tags: UIEventSource<any>): BaseUIElement {
if (this.iconBadges.length === 0) {
return undefined
}
return new VariableUiElement(
tags.map(tags => {
const badgeElements = this.iconBadges.map(badge => {
if (!badge.if.matchesProperties(tags)) {
// Doesn't match...
return undefined
}
const htmlDefs = Utils.SubstituteKeys(badge.then.GetRenderValue(tags)?.txt, tags)
const badgeElement = PointRenderingConfig.FromHtmlMulti(htmlDefs, "0", true)?.SetClass("block relative")
if (badgeElement === undefined) {
return undefined;
}
return new Combine([badgeElement]).SetStyle("width: 1.5rem").SetClass("block")
})
return new Combine(badgeElements).SetClass("inline-flex h-full")
})).SetClass("absolute bottom-0 right-1/3 h-1/2 w-0")
}
private GetLabel(tags: UIEventSource<any>): BaseUIElement {
if (this.label === undefined) {
return undefined;
}
const self = this;
return new VariableUiElement(tags.map(tags => {
const label = self.label
?.GetRenderValue(tags)
?.Subs(tags)
?.SetClass("block text-center")
return new Combine([label]).SetClass("flex flex-col items-center mt-1")
}))
}
}

View file

@ -8,7 +8,7 @@ export default class SourceConfig {
public readonly geojsonSource?: string;
public readonly geojsonZoomLevel?: number;
public readonly isOsmCacheLayer: boolean;
public readonly mercatorCrs: boolean;
public readonly mercatorCrs: boolean;
constructor(params: {
mercatorCrs?: boolean;
@ -36,11 +36,12 @@ export default class SourceConfig {
console.error(params)
throw `Source said it is a OSM-cached layer, but didn't define the actual source of the cache (in context ${context})`
}
if(params.geojsonSource !== undefined && params.geojsonSourceLevel !== undefined){
if(! ["x","y","x_min","x_max","y_min","Y_max"].some(toSearch => params.geojsonSource.indexOf(toSearch) > 0)){
if (params.geojsonSource !== undefined && params.geojsonSourceLevel !== undefined) {
if (!["x", "y", "x_min", "x_max", "y_min", "Y_max"].some(toSearch => params.geojsonSource.indexOf(toSearch) > 0)) {
throw `Source defines a geojson-zoomLevel, but does not specify {x} nor {y} (or equivalent), this is probably a bug (in context ${context})`
}}
this.osmTags = params.osmTags ?? new RegexTag("id",/.*/);
}
}
this.osmTags = params.osmTags ?? new RegexTag("id", /.*/);
this.overpassScript = params.overpassScript;
this.geojsonSource = params.geojsonSource;
this.geojsonZoomLevel = params.geojsonSourceLevel;

View file

@ -49,14 +49,14 @@ export default class TagRenderingConfig {
this.question = null;
this.condition = null;
}
if(typeof json === "number"){
this.render = Translations.WT( ""+json)
if (typeof json === "number") {
this.render = Translations.WT("" + json)
return;
}
if (json === undefined) {
throw "Initing a TagRenderingConfig with undefined in " + context;
}
@ -66,7 +66,7 @@ export default class TagRenderingConfig {
return;
}
this.id = json.id ?? "";
this.group = json.group ?? "";
this.render = Translations.T(json.render, context + ".render");
@ -74,7 +74,7 @@ export default class TagRenderingConfig {
this.condition = TagUtils.Tag(json.condition ?? {"and": []}, `${context}.condition`);
if (json.freeform) {
if(json.freeform.addExtraTags !== undefined && json.freeform.addExtraTags.map === undefined){
if (json.freeform.addExtraTags !== undefined && json.freeform.addExtraTags.map === undefined) {
throw `Freeform.addExtraTags should be a list of strings - not a single string (at ${context})`
}
this.freeform = {
@ -134,8 +134,8 @@ export default class TagRenderingConfig {
if (typeof mapping.if !== "string" && mapping.if["length"] !== undefined) {
throw `${ctx}: Invalid mapping: "if" is defined as an array. Use {"and": <your conditions>} or {"or": <your conditions>} instead`
}
if(mapping.addExtraTags !== undefined && this.multiAnswer){
if (mapping.addExtraTags !== undefined && this.multiAnswer) {
throw `${ctx}: Invalid mapping: got a multi-Answer with addExtraTags; this is not allowed`
}
@ -150,7 +150,7 @@ export default class TagRenderingConfig {
ifnot: (mapping.ifnot !== undefined ? TagUtils.Tag(mapping.ifnot, `${ctx}.ifnot`) : undefined),
then: Translations.T(mapping.then, `${ctx}.then`),
hideInAnswer: hideInAnswer,
addExtraTags: (mapping.addExtraTags??[]).map((str, j) => TagUtils.SimpleTag(str, `${ctx}.addExtraTags[${j}]`))
addExtraTags: (mapping.addExtraTags ?? []).map((str, j) => TagUtils.SimpleTag(str, `${ctx}.addExtraTags[${j}]`))
};
if (this.question) {
if (hideInAnswer !== true && mp.if !== undefined && !mp.if.isUsableAsAnswer()) {
@ -260,6 +260,7 @@ export default class TagRenderingConfig {
return false;
}
/**
* Gets all the render values. Will return multiple render values if 'multianswer' is enabled.
* The result will equal [GetRenderValue] if not 'multiAnswer'

View file

@ -19,7 +19,7 @@ export default class TilesourceConfig {
this.minzoom = config.minZoom ?? 0
this.maxzoom = config.maxZoom ?? 999
this.defaultState = config.defaultState ?? true;
if(this.id === undefined){
if (this.id === undefined) {
throw "An id is obligated"
}
if (this.minzoom > this.maxzoom) {
@ -34,7 +34,7 @@ export default class TilesourceConfig {
if (this.source.indexOf("{zoom}") >= 0) {
throw "Invalid source url: use {z} instead of {zoom} (at " + ctx + ".source)"
}
if(!this.defaultState && config.name === undefined){
if (!this.defaultState && config.name === undefined) {
throw "Disabling an overlay without a name is not possible"
}
}

View file

@ -4,8 +4,8 @@ import {TagRenderingConfigJson} from "./Json/TagRenderingConfigJson";
import {Utils} from "../../Utils";
export default class WithContextLoader {
private readonly _json: any;
protected readonly _context: string;
private readonly _json: any;
constructor(json: any, context: string) {
this._json = json;
@ -47,7 +47,7 @@ export default class WithContextLoader {
tagRenderings?: (string | { builtin: string, override: any } | TagRenderingConfigJson)[],
readOnly = false,
prepConfig: ((config: TagRenderingConfigJson) => TagRenderingConfigJson) = undefined
) : TagRenderingConfig[]{
): TagRenderingConfig[] {
if (tagRenderings === undefined) {
return [];
}

View file

@ -1,5 +1,5 @@
import {control} from "leaflet";
import zoom = control.zoom;
export interface TileRange {
xstart: number,
@ -15,7 +15,7 @@ export class Tiles {
public static MapRange<T>(tileRange: TileRange, f: (x: number, y: number) => T): T[] {
const result: T[] = []
const total = tileRange.total
if(total > 100000){
if (total > 100000) {
throw "Tilerange too big"
}
for (let x = tileRange.xstart; x <= tileRange.xend; x++) {
@ -27,24 +27,6 @@ export class Tiles {
return result;
}
private static tile2long(x, z) {
return (x / Math.pow(2, z) * 360 - 180);
}
private static tile2lat(y, z) {
const n = Math.PI - 2 * Math.PI * y / Math.pow(2, z);
return (180 / Math.PI * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n))));
}
private static lon2tile(lon, zoom) {
return (Math.floor((lon + 180) / 360 * Math.pow(2, zoom)));
}
private static lat2tile(lat, zoom) {
return (Math.floor((1 - Math.log(Math.tan(lat * Math.PI / 180) + 1 / Math.cos(lat * Math.PI / 180)) / Math.PI) / 2 * Math.pow(2, zoom)));
}
/**
* Calculates the tile bounds of the
* @param z
@ -56,7 +38,6 @@ export class Tiles {
return [[Tiles.tile2lat(y, z), Tiles.tile2long(x, z)], [Tiles.tile2lat(y + 1, z), Tiles.tile2long(x + 1, z)]]
}
static tile_bounds_lon_lat(z: number, x: number, y: number): [[number, number], [number, number]] {
return [[Tiles.tile2long(x, z), Tiles.tile2lat(y, z)], [Tiles.tile2long(x + 1, z), Tiles.tile2lat(y + 1, z)]]
}
@ -67,13 +48,14 @@ export class Tiles {
* @param x
* @param y
*/
static centerPointOf(z: number, x: number, y: number): [number, number]{
return [(Tiles.tile2long(x, z) + Tiles.tile2long(x+1, z)) / 2, (Tiles.tile2lat(y, z) + Tiles.tile2lat(y+1, z)) / 2]
static centerPointOf(z: number, x: number, y: number): [number, number] {
return [(Tiles.tile2long(x, z) + Tiles.tile2long(x + 1, z)) / 2, (Tiles.tile2lat(y, z) + Tiles.tile2lat(y + 1, z)) / 2]
}
static tile_index(z: number, x: number, y: number): number {
return ((x * (2 << z)) + y) * 100 + z
}
/**
* Given a tile index number, returns [z, x, y]
* @param index
@ -93,7 +75,7 @@ export class Tiles {
static embedded_tile(lat: number, lon: number, z: number): { x: number, y: number, z: number } {
return {x: Tiles.lon2tile(lon, z), y: Tiles.lat2tile(lat, z), z: z}
}
static TileRangeBetween(zoomlevel: number, lat0: number, lon0: number, lat1: number, lon1: number): TileRange {
const t0 = Tiles.embedded_tile(lat0, lon0, zoomlevel)
const t1 = Tiles.embedded_tile(lat1, lon1, zoomlevel)
@ -114,5 +96,22 @@ export class Tiles {
}
}
private static tile2long(x, z) {
return (x / Math.pow(2, z) * 360 - 180);
}
private static tile2lat(y, z) {
const n = Math.PI - 2 * Math.PI * y / Math.pow(2, z);
return (180 / Math.PI * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n))));
}
private static lon2tile(lon, zoom) {
return (Math.floor((lon + 180) / 360 * Math.pow(2, zoom)));
}
private static lat2tile(lat, zoom) {
return (Math.floor((1 - Math.log(Math.tan(lat * Math.PI / 180) + 1 / Math.cos(lat * Math.PI / 180)) / Math.PI) / 2 * Math.pow(2, zoom)));
}
}

View file

@ -37,7 +37,9 @@ export class Unit {
const possiblePostFixes = new Set<string>()
function addPostfixesOf(str) {
if(str === undefined){return}
if (str === undefined) {
return
}
str = str.toLowerCase()
for (let i = 0; i < str.length + 1; i++) {
const substr = str.substring(0, i)
@ -54,8 +56,8 @@ export class Unit {
this.possiblePostFixes.sort((a, b) => b.length - a.length)
}
static fromJson(json: UnitConfigJson, ctx: string){
static fromJson(json: UnitConfigJson, ctx: string) {
const appliesTo = json.appliesToKey
for (let i = 0; i < appliesTo.length; i++) {
let key = appliesTo[i];
@ -82,7 +84,7 @@ export class Unit {
const applicable = json.applicableUnits.map((u, i) => new Denomination(u, `${ctx}.units[${i}]`))
return new Unit(appliesTo, applicable, json.eraseInvalidValues ?? false)
}
isApplicableToKey(key: string | undefined): boolean {
if (key === undefined) {
return false;
@ -112,7 +114,7 @@ export class Unit {
return undefined;
}
const [stripped, denom] = this.findDenomination(value)
const human = stripped === "1" ? denom?.humanSingular : denom?.human
const human = stripped === "1" ? denom?.humanSingular : denom?.human
if (human === undefined) {
return new FixedUiElement(stripped ?? value);
}