forked from MapComplete/MapComplete
		
	Formatting
This commit is contained in:
		
							parent
							
								
									6d822b42ca
								
							
						
					
					
						commit
						61aebc61eb
					
				
					 32 changed files with 664 additions and 511 deletions
				
			
		|  | @ -191,7 +191,7 @@ export default class OverpassFeatureSource implements FeatureSource { | ||||||
| 
 | 
 | ||||||
|         const self = this |         const self = this | ||||||
|         const overpassUrls = self.state.overpassUrl.data |         const overpassUrls = self.state.overpassUrl.data | ||||||
|         if(overpassUrls === undefined || overpassUrls.length === 0){ |         if (overpassUrls === undefined || overpassUrls.length === 0) { | ||||||
|             throw "Panic: overpassFeatureSource didn't receive any overpassUrls" |             throw "Panic: overpassFeatureSource didn't receive any overpassUrls" | ||||||
|         } |         } | ||||||
|         let bounds: BBox |         let bounds: BBox | ||||||
|  |  | ||||||
|  | @ -46,9 +46,9 @@ export default class TitleHandler { | ||||||
|             if (Utils.runningFromConsole) { |             if (Utils.runningFromConsole) { | ||||||
|                 return |                 return | ||||||
|             } |             } | ||||||
|             try{ |             try { | ||||||
|             document.title = title |                 document.title = title | ||||||
|             }catch (e) { |             } catch (e) { | ||||||
|                 console.error(e) |                 console.error(e) | ||||||
|             } |             } | ||||||
|         }) |         }) | ||||||
|  |  | ||||||
|  | @ -84,7 +84,7 @@ export class NewGeometryFromChangesFeatureSource implements FeatureSource { | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 try { |                 try { | ||||||
|                     const tags: OsmTags & {id: OsmId & string} = { |                     const tags: OsmTags & { id: OsmId & string } = { | ||||||
|                         id: <OsmId & string>(change.type + "/" + change.id), |                         id: <OsmId & string>(change.type + "/" + change.id), | ||||||
|                     } |                     } | ||||||
|                     for (const kv of change.tags) { |                     for (const kv of change.tags) { | ||||||
|  |  | ||||||
|  | @ -1,8 +1,8 @@ | ||||||
| import FeatureSource, {FeatureSourceForLayer, Tiled} from "../FeatureSource" | import FeatureSource, { FeatureSourceForLayer, Tiled } from "../FeatureSource" | ||||||
| import {ImmutableStore, Store} from "../../UIEventSource" | import { ImmutableStore, Store } from "../../UIEventSource" | ||||||
| import FilteredLayer from "../../../Models/FilteredLayer" | import FilteredLayer from "../../../Models/FilteredLayer" | ||||||
| import {BBox} from "../../BBox" | import { BBox } from "../../BBox" | ||||||
| import {Feature} from "geojson"; | import { Feature } from "geojson" | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * A simple, read only feature store. |  * A simple, read only feature store. | ||||||
|  |  | ||||||
|  | @ -19,7 +19,11 @@ export default class CreateNewNodeAction extends OsmCreateAction { | ||||||
|     private readonly _lon: number |     private readonly _lon: number | ||||||
|     private readonly _snapOnto: OsmWay |     private readonly _snapOnto: OsmWay | ||||||
|     private readonly _reusePointDistance: number |     private readonly _reusePointDistance: number | ||||||
|     private readonly meta: { changeType: "create" | "import"; theme: string; specialMotivation?: string } |     private readonly meta: { | ||||||
|  |         changeType: "create" | "import" | ||||||
|  |         theme: string | ||||||
|  |         specialMotivation?: string | ||||||
|  |     } | ||||||
|     private readonly _reusePreviouslyCreatedPoint: boolean |     private readonly _reusePreviouslyCreatedPoint: boolean | ||||||
| 
 | 
 | ||||||
|     constructor( |     constructor( | ||||||
|  |  | ||||||
|  | @ -3,10 +3,10 @@ import OsmChangeAction from "./OsmChangeAction" | ||||||
| import { Changes } from "../Changes" | import { Changes } from "../Changes" | ||||||
| import { ChangeDescription } from "./ChangeDescription" | import { ChangeDescription } from "./ChangeDescription" | ||||||
| import ChangeTagAction from "./ChangeTagAction" | import ChangeTagAction from "./ChangeTagAction" | ||||||
| import {TagsFilter} from "../../Tags/TagsFilter" | import { TagsFilter } from "../../Tags/TagsFilter" | ||||||
| import {And} from "../../Tags/And" | import { And } from "../../Tags/And" | ||||||
| import {Tag} from "../../Tags/Tag" | import { Tag } from "../../Tags/Tag" | ||||||
| import {OsmId} from "../../../Models/OsmFeature"; | import { OsmId } from "../../../Models/OsmFeature" | ||||||
| import { Utils } from "../../../Utils" | import { Utils } from "../../../Utils" | ||||||
| 
 | 
 | ||||||
| export default class DeleteAction extends OsmChangeAction { | export default class DeleteAction extends OsmChangeAction { | ||||||
|  | @ -19,7 +19,6 @@ export default class DeleteAction extends OsmChangeAction { | ||||||
|     private readonly _id: OsmId |     private readonly _id: OsmId | ||||||
|     private readonly _hardDelete: boolean |     private readonly _hardDelete: boolean | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     constructor( |     constructor( | ||||||
|         id: OsmId, |         id: OsmId, | ||||||
|         softDeletionTags: TagsFilter | undefined, |         softDeletionTags: TagsFilter | undefined, | ||||||
|  | @ -39,11 +38,12 @@ export default class DeleteAction extends OsmChangeAction { | ||||||
|             this._softDeletionTags = new And( |             this._softDeletionTags = new And( | ||||||
|                 Utils.NoNull([ |                 Utils.NoNull([ | ||||||
|                     softDeletionTags, |                     softDeletionTags, | ||||||
|                 new Tag( |                     new Tag( | ||||||
|                     "fixme", |                         "fixme", | ||||||
|                     `A mapcomplete user marked this feature to be deleted (${meta.specialMotivation})` |                         `A mapcomplete user marked this feature to be deleted (${meta.specialMotivation})` | ||||||
|                 ) |                     ), | ||||||
|             ])) |                 ]) | ||||||
|  |             ) | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     /** |     /** | ||||||
|  | @ -63,8 +63,11 @@ export default class DeleteAction extends OsmChangeAction { | ||||||
|      * const descr = await da.CreateChangeDescriptions(new Changes(), obj) |      * const descr = await da.CreateChangeDescriptions(new Changes(), obj) | ||||||
|      * descr[0] // => {doDelete: true, meta: {theme: "test", specialMotivation: "Testcase", changeType: "deletion"}, type: "node",id: 1 }
 |      * descr[0] // => {doDelete: true, meta: {theme: "test", specialMotivation: "Testcase", changeType: "deletion"}, type: "node",id: 1 }
 | ||||||
|      */ |      */ | ||||||
|     public async CreateChangeDescriptions(changes: Changes, object?: OsmObject): Promise<ChangeDescription[]> { |     public async CreateChangeDescriptions( | ||||||
|         const osmObject = object ?? await OsmObject.DownloadObjectAsync(this._id) |         changes: Changes, | ||||||
|  |         object?: OsmObject | ||||||
|  |     ): Promise<ChangeDescription[]> { | ||||||
|  |         const osmObject = object ?? (await OsmObject.DownloadObjectAsync(this._id)) | ||||||
| 
 | 
 | ||||||
|         if (this._hardDelete) { |         if (this._hardDelete) { | ||||||
|             return [ |             return [ | ||||||
|  |  | ||||||
|  | @ -13,7 +13,7 @@ import { Utils } from "../../../Utils" | ||||||
| import { OsmConnection } from "../OsmConnection" | import { OsmConnection } from "../OsmConnection" | ||||||
| import { Feature } from "@turf/turf" | import { Feature } from "@turf/turf" | ||||||
| import FeaturePipeline from "../../FeatureSource/FeaturePipeline" | import FeaturePipeline from "../../FeatureSource/FeaturePipeline" | ||||||
| import {Geometry, LineString, Point, Polygon} from "geojson"; | import { Geometry, LineString, Point, Polygon } from "geojson" | ||||||
| 
 | 
 | ||||||
| export default class ReplaceGeometryAction extends OsmChangeAction { | export default class ReplaceGeometryAction extends OsmChangeAction { | ||||||
|     /** |     /** | ||||||
|  | @ -85,7 +85,7 @@ export default class ReplaceGeometryAction extends OsmChangeAction { | ||||||
|     public async getPreview(): Promise<FeatureSource> { |     public async getPreview(): Promise<FeatureSource> { | ||||||
|         const { closestIds, allNodesById, detachedNodes, reprojectedNodes } = |         const { closestIds, allNodesById, detachedNodes, reprojectedNodes } = | ||||||
|             await this.GetClosestIds() |             await this.GetClosestIds() | ||||||
|         const preview: Feature<Geometry> [] = closestIds.map((newId, i) => { |         const preview: Feature<Geometry>[] = closestIds.map((newId, i) => { | ||||||
|             if (this.identicalTo[i] !== undefined) { |             if (this.identicalTo[i] !== undefined) { | ||||||
|                 return undefined |                 return undefined | ||||||
|             } |             } | ||||||
|  | @ -390,7 +390,7 @@ export default class ReplaceGeometryAction extends OsmChangeAction { | ||||||
|                 const node = allNodesById.get(id) |                 const node = allNodesById.get(id) | ||||||
| 
 | 
 | ||||||
|                 // Project the node onto the target way to calculate the new coordinates
 |                 // Project the node onto the target way to calculate the new coordinates
 | ||||||
|                 const way = <Feature<LineString>> { |                 const way = <Feature<LineString>>{ | ||||||
|                     type: "Feature", |                     type: "Feature", | ||||||
|                     properties: {}, |                     properties: {}, | ||||||
|                     geometry: { |                     geometry: { | ||||||
|  |  | ||||||
|  | @ -1,10 +1,10 @@ | ||||||
| import {Utils} from "../../Utils" | import { Utils } from "../../Utils" | ||||||
| import * as polygon_features from "../../assets/polygon-features.json" | import * as polygon_features from "../../assets/polygon-features.json" | ||||||
| import {Store, UIEventSource} from "../UIEventSource" | import { Store, UIEventSource } from "../UIEventSource" | ||||||
| import {BBox} from "../BBox" | import { BBox } from "../BBox" | ||||||
| import * as OsmToGeoJson from "osmtogeojson" | import * as OsmToGeoJson from "osmtogeojson" | ||||||
| import {NodeId, OsmFeature, OsmId, OsmTags, RelationId, WayId} from "../../Models/OsmFeature" | import { NodeId, OsmFeature, OsmId, OsmTags, RelationId, WayId } from "../../Models/OsmFeature" | ||||||
| import {Feature, LineString, Polygon} from "geojson"; | import { Feature, LineString, Polygon } from "geojson" | ||||||
| 
 | 
 | ||||||
| export abstract class OsmObject { | export abstract class OsmObject { | ||||||
|     private static defaultBackend = "https://www.openstreetmap.org/" |     private static defaultBackend = "https://www.openstreetmap.org/" | ||||||
|  | @ -17,7 +17,7 @@ export abstract class OsmObject { | ||||||
|     /** |     /** | ||||||
|      * The OSM tags as simple object |      * The OSM tags as simple object | ||||||
|      */ |      */ | ||||||
|     tags: OsmTags & {id: OsmId} |     tags: OsmTags & { id: OsmId } | ||||||
|     version: number |     version: number | ||||||
|     public changed: boolean = false |     public changed: boolean = false | ||||||
|     timestamp: Date |     timestamp: Date | ||||||
|  | @ -41,9 +41,9 @@ export abstract class OsmObject { | ||||||
|         this.backendURL = url |         this.backendURL = url | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public static DownloadObject(id: NodeId, forceRefresh?: boolean): Store<OsmNode> ; |     public static DownloadObject(id: NodeId, forceRefresh?: boolean): Store<OsmNode> | ||||||
|     public static DownloadObject(id: RelationId, forceRefresh?: boolean): Store<OsmRelation> ; |     public static DownloadObject(id: RelationId, forceRefresh?: boolean): Store<OsmRelation> | ||||||
|     public static DownloadObject(id: WayId, forceRefresh?: boolean): Store<OsmWay> ; |     public static DownloadObject(id: WayId, forceRefresh?: boolean): Store<OsmWay> | ||||||
|     public static DownloadObject(id: string, forceRefresh: boolean = false): Store<OsmObject> { |     public static DownloadObject(id: string, forceRefresh: boolean = false): Store<OsmObject> { | ||||||
|         let src: UIEventSource<OsmObject> |         let src: UIEventSource<OsmObject> | ||||||
|         if (OsmObject.objectCache.has(id)) { |         if (OsmObject.objectCache.has(id)) { | ||||||
|  | @ -73,12 +73,30 @@ export abstract class OsmObject { | ||||||
|         return rawData.elements[0].tags |         return rawData.elements[0].tags | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     static async DownloadObjectAsync(id: NodeId, maxCacheAgeInSecs?: number): Promise<OsmNode | undefined> |     static async DownloadObjectAsync( | ||||||
|     static async DownloadObjectAsync(id: WayId, maxCacheAgeInSecs?: number): Promise<OsmWay | undefined> |         id: NodeId, | ||||||
|     static async DownloadObjectAsync(id: RelationId, maxCacheAgeInSecs?: number): Promise<OsmRelation | undefined> |         maxCacheAgeInSecs?: number | ||||||
|     static async DownloadObjectAsync(id: OsmId, maxCacheAgeInSecs?: number): Promise<OsmObject | undefined> |     ): Promise<OsmNode | undefined> | ||||||
|     static async DownloadObjectAsync(id: string, maxCacheAgeInSecs?: number): Promise<OsmObject | undefined> |     static async DownloadObjectAsync( | ||||||
|     static async DownloadObjectAsync(id: string, maxCacheAgeInSecs?: number): Promise<OsmObject | undefined> { |         id: WayId, | ||||||
|  |         maxCacheAgeInSecs?: number | ||||||
|  |     ): Promise<OsmWay | undefined> | ||||||
|  |     static async DownloadObjectAsync( | ||||||
|  |         id: RelationId, | ||||||
|  |         maxCacheAgeInSecs?: number | ||||||
|  |     ): Promise<OsmRelation | undefined> | ||||||
|  |     static async DownloadObjectAsync( | ||||||
|  |         id: OsmId, | ||||||
|  |         maxCacheAgeInSecs?: number | ||||||
|  |     ): Promise<OsmObject | undefined> | ||||||
|  |     static async DownloadObjectAsync( | ||||||
|  |         id: string, | ||||||
|  |         maxCacheAgeInSecs?: number | ||||||
|  |     ): Promise<OsmObject | undefined> | ||||||
|  |     static async DownloadObjectAsync( | ||||||
|  |         id: string, | ||||||
|  |         maxCacheAgeInSecs?: number | ||||||
|  |     ): Promise<OsmObject | undefined> { | ||||||
|         const splitted = id.split("/") |         const splitted = id.split("/") | ||||||
|         const type = splitted[0] |         const type = splitted[0] | ||||||
|         const idN = Number(splitted[1]) |         const idN = Number(splitted[1]) | ||||||
|  | @ -210,7 +228,7 @@ export abstract class OsmObject { | ||||||
|                 case "relation": |                 case "relation": | ||||||
|                     osmObject = new OsmRelation(idN) |                     osmObject = new OsmRelation(idN) | ||||||
|                     const allGeojsons = OsmToGeoJson.default( |                     const allGeojsons = OsmToGeoJson.default( | ||||||
|                         {elements}, |                         { elements }, | ||||||
|                         // @ts-ignore
 |                         // @ts-ignore
 | ||||||
|                         { |                         { | ||||||
|                             flatProperties: true, |                             flatProperties: true, | ||||||
|  | @ -264,14 +282,16 @@ export abstract class OsmObject { | ||||||
|         return false |         return false | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private static constructPolygonFeatures(): Map<string, |     private static constructPolygonFeatures(): Map< | ||||||
|         { values: Set<string>; blacklist: boolean }> { |         string, | ||||||
|  |         { values: Set<string>; blacklist: boolean } | ||||||
|  |     > { | ||||||
|         const result = new Map<string, { values: Set<string>; blacklist: boolean }>() |         const result = new Map<string, { values: Set<string>; blacklist: boolean }>() | ||||||
|         for (const polygonFeature of polygon_features["default"] ?? polygon_features) { |         for (const polygonFeature of polygon_features["default"] ?? polygon_features) { | ||||||
|             const key = polygonFeature.key |             const key = polygonFeature.key | ||||||
| 
 | 
 | ||||||
|             if (polygonFeature.polygon === "all") { |             if (polygonFeature.polygon === "all") { | ||||||
|                 result.set(key, {values: null, blacklist: false}) |                 result.set(key, { values: null, blacklist: false }) | ||||||
|                 continue |                 continue | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,13 +1,13 @@ | ||||||
| import {Tag} from "./Tag" | import { Tag } from "./Tag" | ||||||
| import {TagsFilter} from "./TagsFilter" | import { TagsFilter } from "./TagsFilter" | ||||||
| import {And} from "./And" | import { And } from "./And" | ||||||
| import {Utils} from "../../Utils" | import { Utils } from "../../Utils" | ||||||
| import ComparingTag from "./ComparingTag" | import ComparingTag from "./ComparingTag" | ||||||
| import {RegexTag} from "./RegexTag" | import { RegexTag } from "./RegexTag" | ||||||
| import SubstitutingTag from "./SubstitutingTag" | import SubstitutingTag from "./SubstitutingTag" | ||||||
| import {Or} from "./Or" | import { Or } from "./Or" | ||||||
| import {TagConfigJson} from "../../Models/ThemeConfig/Json/TagConfigJson" | import { TagConfigJson } from "../../Models/ThemeConfig/Json/TagConfigJson" | ||||||
| import {isRegExp} from "util" | import { isRegExp } from "util" | ||||||
| import * as key_counts from "../../assets/key_totals.json" | import * as key_counts from "../../assets/key_totals.json" | ||||||
| 
 | 
 | ||||||
| type Tags = Record<string, string> | type Tags = Record<string, string> | ||||||
|  | @ -373,7 +373,7 @@ export class TagUtils { | ||||||
|             return null |             return null | ||||||
|         } |         } | ||||||
|         const [_, key, invert, modifier, value] = match |         const [_, key, invert, modifier, value] = match | ||||||
|         return {key, value, invert: invert == "!", modifier: modifier == "i~" ? "i" : ""} |         return { key, value, invert: invert == "!", modifier: modifier == "i~" ? "i" : "" } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  | @ -557,9 +557,9 @@ export class TagUtils { | ||||||
| 
 | 
 | ||||||
|         if (tag.indexOf("~~") >= 0) { |         if (tag.indexOf("~~") >= 0) { | ||||||
|             const split = Utils.SplitFirst(tag, "~~") |             const split = Utils.SplitFirst(tag, "~~") | ||||||
|             let keyRegex: RegExp; |             let keyRegex: RegExp | ||||||
|             if (split[0] === "*") { |             if (split[0] === "*") { | ||||||
|                 keyRegex = new RegExp(".+","i") |                 keyRegex = new RegExp(".+", "i") | ||||||
|             } else { |             } else { | ||||||
|                 keyRegex = new RegExp("^(" + split[0] + ")$") |                 keyRegex = new RegExp("^(" + split[0] + ")$") | ||||||
|             } |             } | ||||||
|  | @ -569,10 +569,7 @@ export class TagUtils { | ||||||
|             } else { |             } else { | ||||||
|                 valueRegex = new RegExp("^(" + split[1] + ")$", "s") |                 valueRegex = new RegExp("^(" + split[1] + ")$", "s") | ||||||
|             } |             } | ||||||
|             return new RegexTag( |             return new RegexTag(keyRegex, valueRegex) | ||||||
|                 keyRegex, |  | ||||||
|                 valueRegex |  | ||||||
|             ) |  | ||||||
|         } |         } | ||||||
|         const withRegex = TagUtils.parseRegexOperator(tag) |         const withRegex = TagUtils.parseRegexOperator(tag) | ||||||
|         if (withRegex != null) { |         if (withRegex != null) { | ||||||
|  | @ -627,7 +624,7 @@ export class TagUtils { | ||||||
|                 ) |                 ) | ||||||
|             } |             } | ||||||
|             if (split[1] === "") { |             if (split[1] === "") { | ||||||
|                 return new RegexTag(split[0], /.+/si) |                 return new RegexTag(split[0], /.+/is) | ||||||
|             } |             } | ||||||
|             return new RegexTag(split[0], split[1], true) |             return new RegexTag(split[0], split[1], true) | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  | @ -6,7 +6,7 @@ import Hash from "./Hash" | ||||||
| import { Utils } from "../../Utils" | import { Utils } from "../../Utils" | ||||||
| 
 | 
 | ||||||
| export class QueryParameters { | export class QueryParameters { | ||||||
|     static defaults : Record<string, string> = {} |     static defaults: Record<string, string> = {} | ||||||
|     static documentation: Map<string, string> = new Map<string, string>() |     static documentation: Map<string, string> = new Map<string, string>() | ||||||
|     private static order: string[] = ["layout", "test", "z", "lat", "lon"] |     private static order: string[] = ["layout", "test", "z", "lat", "lon"] | ||||||
|     protected static readonly _wasInitialized: Set<string> = new Set() |     protected static readonly _wasInitialized: Set<string> = new Set() | ||||||
|  | @ -105,9 +105,9 @@ export class QueryParameters { | ||||||
|         } |         } | ||||||
|         if (!Utils.runningFromConsole) { |         if (!Utils.runningFromConsole) { | ||||||
|             // Don't pollute the history every time a parameter changes
 |             // Don't pollute the history every time a parameter changes
 | ||||||
|             try{ |             try { | ||||||
|                 history.replaceState(null, "", "?" + parts.join("&") + Hash.Current()) |                 history.replaceState(null, "", "?" + parts.join("&") + Hash.Current()) | ||||||
|             }catch(e){ |             } catch (e) { | ||||||
|                 console.error(e) |                 console.error(e) | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  | @ -1,64 +1,90 @@ | ||||||
| import {Concat, Conversion, DesugaringContext, DesugaringStep, Each, FirstOf, Fuse, On, SetDefault,} from "./Conversion" | import { | ||||||
| import {LayerConfigJson} from "../Json/LayerConfigJson" |     Concat, | ||||||
| import {TagRenderingConfigJson} from "../Json/TagRenderingConfigJson" |     Conversion, | ||||||
| import {Utils} from "../../../Utils" |     DesugaringContext, | ||||||
|  |     DesugaringStep, | ||||||
|  |     Each, | ||||||
|  |     FirstOf, | ||||||
|  |     Fuse, | ||||||
|  |     On, | ||||||
|  |     SetDefault, | ||||||
|  | } from "./Conversion" | ||||||
|  | import { LayerConfigJson } from "../Json/LayerConfigJson" | ||||||
|  | import { TagRenderingConfigJson } from "../Json/TagRenderingConfigJson" | ||||||
|  | import { Utils } from "../../../Utils" | ||||||
| import RewritableConfigJson from "../Json/RewritableConfigJson" | import RewritableConfigJson from "../Json/RewritableConfigJson" | ||||||
| import SpecialVisualizations from "../../../UI/SpecialVisualizations" | import SpecialVisualizations from "../../../UI/SpecialVisualizations" | ||||||
| import Translations from "../../../UI/i18n/Translations" | import Translations from "../../../UI/i18n/Translations" | ||||||
| import {Translation} from "../../../UI/i18n/Translation" | import { Translation } from "../../../UI/i18n/Translation" | ||||||
| import * as tagrenderingconfigmeta from "../../../assets/tagrenderingconfigmeta.json" | import * as tagrenderingconfigmeta from "../../../assets/tagrenderingconfigmeta.json" | ||||||
| import {AddContextToTranslations} from "./AddContextToTranslations" | import { AddContextToTranslations } from "./AddContextToTranslations" | ||||||
| import FilterConfigJson from "../Json/FilterConfigJson"; | import FilterConfigJson from "../Json/FilterConfigJson" | ||||||
| import * as predifined_filters from "../../../assets/layers/filters/filters.json" | import * as predifined_filters from "../../../assets/layers/filters/filters.json" | ||||||
| 
 | 
 | ||||||
| class ExpandFilter extends DesugaringStep<LayerConfigJson>{ | class ExpandFilter extends DesugaringStep<LayerConfigJson> { | ||||||
| 
 |     private static load_filters(): Map<string, FilterConfigJson> { | ||||||
| 
 |         let filters = new Map<string, FilterConfigJson>() | ||||||
|     private static load_filters(): Map<string, FilterConfigJson>{ |         for (const filter of <FilterConfigJson[]>predifined_filters.filter) { | ||||||
|         let filters = new Map<string, FilterConfigJson>(); |  | ||||||
|         for (const filter of (<FilterConfigJson[]>predifined_filters.filter)) { |  | ||||||
|             filters.set(filter.id, filter) |             filters.set(filter.id, filter) | ||||||
|         } |         } | ||||||
|         return filters; |         return filters | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private static readonly predefinedFilters = ExpandFilter.load_filters(); |     private static readonly predefinedFilters = ExpandFilter.load_filters() | ||||||
| 
 | 
 | ||||||
|     constructor() { |     constructor() { | ||||||
|         super("Expands filters: replaces a shorthand by the value found in 'filters.json'", ["filter"], "ExpandFilter"); |         super( | ||||||
|  |             "Expands filters: replaces a shorthand by the value found in 'filters.json'", | ||||||
|  |             ["filter"], | ||||||
|  |             "ExpandFilter" | ||||||
|  |         ) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     convert(json: LayerConfigJson, context: string): { result: LayerConfigJson; errors?: string[]; warnings?: string[]; information?: string[] } { |     convert( | ||||||
|         if(json.filter === undefined || json.filter === null){ |         json: LayerConfigJson, | ||||||
|             return {result: json} // Nothing to change here
 |         context: string | ||||||
|  |     ): { result: LayerConfigJson; errors?: string[]; warnings?: string[]; information?: string[] } { | ||||||
|  |         if (json.filter === undefined || json.filter === null) { | ||||||
|  |             return { result: json } // Nothing to change here
 | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if( json.filter["sameAs"] !== undefined){ |         if (json.filter["sameAs"] !== undefined) { | ||||||
|             return {result: json} // Nothing to change here
 |             return { result: json } // Nothing to change here
 | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         const newFilters : FilterConfigJson[] = [] |         const newFilters: FilterConfigJson[] = [] | ||||||
|         const errors :string[]= [] |         const errors: string[] = [] | ||||||
|         for (const filter of (<(FilterConfigJson|string)[]> json.filter)) { |         for (const filter of <(FilterConfigJson | string)[]>json.filter) { | ||||||
|             if (typeof filter !== "string") { |             if (typeof filter !== "string") { | ||||||
|                 newFilters.push(filter) |                 newFilters.push(filter) | ||||||
|                 continue |                 continue | ||||||
|             } |             } | ||||||
|             // Search for the filter:
 |             // Search for the filter:
 | ||||||
|             const found = ExpandFilter.predefinedFilters.get(filter) |             const found = ExpandFilter.predefinedFilters.get(filter) | ||||||
|             if(found === undefined){ |             if (found === undefined) { | ||||||
|                 const suggestions = Utils.sortedByLevenshteinDistance(filter, Array.from(ExpandFilter.predefinedFilters.keys()), t => t) |                 const suggestions = Utils.sortedByLevenshteinDistance( | ||||||
|                 const err = context+".filter: while searching for predifined filter "+filter+": this filter is not found. Perhaps you meant one of: "+suggestions |                     filter, | ||||||
|  |                     Array.from(ExpandFilter.predefinedFilters.keys()), | ||||||
|  |                     (t) => t | ||||||
|  |                 ) | ||||||
|  |                 const err = | ||||||
|  |                     context + | ||||||
|  |                     ".filter: while searching for predifined filter " + | ||||||
|  |                     filter + | ||||||
|  |                     ": this filter is not found. Perhaps you meant one of: " + | ||||||
|  |                     suggestions | ||||||
|                 errors.push(err) |                 errors.push(err) | ||||||
|             } |             } | ||||||
|             newFilters.push(found) |             newFilters.push(found) | ||||||
|         } |         } | ||||||
|         return {result: { |         return { | ||||||
|             ...json, filter: newFilters |             result: { | ||||||
|             }, errors}; |                 ...json, | ||||||
|  |                 filter: newFilters, | ||||||
|  |             }, | ||||||
|  |             errors, | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| class ExpandTagRendering extends Conversion< | class ExpandTagRendering extends Conversion< | ||||||
|  |  | ||||||
|  | @ -1,21 +1,21 @@ | ||||||
| import {DesugaringStep, Each, Fuse, On} from "./Conversion" | import { DesugaringStep, Each, Fuse, On } from "./Conversion" | ||||||
| import {LayerConfigJson} from "../Json/LayerConfigJson" | import { LayerConfigJson } from "../Json/LayerConfigJson" | ||||||
| import LayerConfig from "../LayerConfig" | import LayerConfig from "../LayerConfig" | ||||||
| import {Utils} from "../../../Utils" | import { Utils } from "../../../Utils" | ||||||
| import Constants from "../../Constants" | import Constants from "../../Constants" | ||||||
| import {Translation} from "../../../UI/i18n/Translation" | import { Translation } from "../../../UI/i18n/Translation" | ||||||
| import {LayoutConfigJson} from "../Json/LayoutConfigJson" | import { LayoutConfigJson } from "../Json/LayoutConfigJson" | ||||||
| import LayoutConfig from "../LayoutConfig" | import LayoutConfig from "../LayoutConfig" | ||||||
| import {TagRenderingConfigJson} from "../Json/TagRenderingConfigJson" | import { TagRenderingConfigJson } from "../Json/TagRenderingConfigJson" | ||||||
| import {TagUtils} from "../../../Logic/Tags/TagUtils" | import { TagUtils } from "../../../Logic/Tags/TagUtils" | ||||||
| import {ExtractImages} from "./FixImages" | import { ExtractImages } from "./FixImages" | ||||||
| import ScriptUtils from "../../../scripts/ScriptUtils" | import ScriptUtils from "../../../scripts/ScriptUtils" | ||||||
| import {And} from "../../../Logic/Tags/And" | import { And } from "../../../Logic/Tags/And" | ||||||
| import Translations from "../../../UI/i18n/Translations" | import Translations from "../../../UI/i18n/Translations" | ||||||
| import Svg from "../../../Svg" | import Svg from "../../../Svg" | ||||||
| import {QuestionableTagRenderingConfigJson} from "../Json/QuestionableTagRenderingConfigJson" | import { QuestionableTagRenderingConfigJson } from "../Json/QuestionableTagRenderingConfigJson" | ||||||
| import FilterConfigJson from "../Json/FilterConfigJson"; | import FilterConfigJson from "../Json/FilterConfigJson" | ||||||
| import DeleteConfig from "../DeleteConfig"; | import DeleteConfig from "../DeleteConfig" | ||||||
| 
 | 
 | ||||||
| class ValidateLanguageCompleteness extends DesugaringStep<any> { | class ValidateLanguageCompleteness extends DesugaringStep<any> { | ||||||
|     private readonly _languages: string[] |     private readonly _languages: string[] | ||||||
|  | @ -42,12 +42,12 @@ class ValidateLanguageCompleteness extends DesugaringStep<any> { | ||||||
|                 .forEach((missing) => { |                 .forEach((missing) => { | ||||||
|                     errors.push( |                     errors.push( | ||||||
|                         context + |                         context + | ||||||
|                         "A theme should be translation-complete for " + |                             "A theme should be translation-complete for " + | ||||||
|                         neededLanguage + |                             neededLanguage + | ||||||
|                         ", but it lacks a translation for " + |                             ", but it lacks a translation for " + | ||||||
|                         missing.context + |                             missing.context + | ||||||
|                         ".\n\tThe known translation is " + |                             ".\n\tThe known translation is " + | ||||||
|                         missing.tr.textFor("en") |                             missing.tr.textFor("en") | ||||||
|                     ) |                     ) | ||||||
|                 }) |                 }) | ||||||
|         } |         } | ||||||
|  | @ -81,16 +81,16 @@ export class DoesImageExist extends DesugaringStep<string> { | ||||||
|         const information = [] |         const information = [] | ||||||
|         if (image.indexOf("{") >= 0) { |         if (image.indexOf("{") >= 0) { | ||||||
|             information.push("Ignoring image with { in the path: " + image) |             information.push("Ignoring image with { in the path: " + image) | ||||||
|             return {result: image} |             return { result: image } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (image === "assets/SocialImage.png") { |         if (image === "assets/SocialImage.png") { | ||||||
|             return {result: image} |             return { result: image } | ||||||
|         } |         } | ||||||
|         if (image.match(/[a-z]*/)) { |         if (image.match(/[a-z]*/)) { | ||||||
|             if (Svg.All[image + ".svg"] !== undefined) { |             if (Svg.All[image + ".svg"] !== undefined) { | ||||||
|                 // This is a builtin img, e.g. 'checkmark' or 'crosshair'
 |                 // This is a builtin img, e.g. 'checkmark' or 'crosshair'
 | ||||||
|                 return {result: image} |                 return { result: image } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | @ -157,15 +157,15 @@ class ValidateTheme extends DesugaringStep<LayoutConfigJson> { | ||||||
|                 if (json["units"] !== undefined) { |                 if (json["units"] !== undefined) { | ||||||
|                     errors.push( |                     errors.push( | ||||||
|                         "The theme " + |                         "The theme " + | ||||||
|                         json.id + |                             json.id + | ||||||
|                         " has units defined - these should be defined on the layer instead. (Hint: use overrideAll: { '+units': ... }) " |                             " has units defined - these should be defined on the layer instead. (Hint: use overrideAll: { '+units': ... }) " | ||||||
|                     ) |                     ) | ||||||
|                 } |                 } | ||||||
|                 if (json["roamingRenderings"] !== undefined) { |                 if (json["roamingRenderings"] !== undefined) { | ||||||
|                     errors.push( |                     errors.push( | ||||||
|                         "Theme " + |                         "Theme " + | ||||||
|                         json.id + |                             json.id + | ||||||
|                         " contains an old 'roamingRenderings'. Use an 'overrideAll' instead" |                             " contains an old 'roamingRenderings'. Use an 'overrideAll' instead" | ||||||
|                     ) |                     ) | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  | @ -180,10 +180,10 @@ class ValidateTheme extends DesugaringStep<LayoutConfigJson> { | ||||||
|             for (const remoteImage of remoteImages) { |             for (const remoteImage of remoteImages) { | ||||||
|                 errors.push( |                 errors.push( | ||||||
|                     "Found a remote image: " + |                     "Found a remote image: " + | ||||||
|                     remoteImage + |                         remoteImage + | ||||||
|                     " in theme " + |                         " in theme " + | ||||||
|                     json.id + |                         json.id + | ||||||
|                     ", please download it." |                         ", please download it." | ||||||
|                 ) |                 ) | ||||||
|             } |             } | ||||||
|             for (const image of images) { |             for (const image of images) { | ||||||
|  | @ -212,10 +212,10 @@ class ValidateTheme extends DesugaringStep<LayoutConfigJson> { | ||||||
|                         const h = parseInt(height) |                         const h = parseInt(height) | ||||||
|                         if (w < 370 || h < 370) { |                         if (w < 370 || h < 370) { | ||||||
|                             const e: string = [ |                             const e: string = [ | ||||||
|                                     `the icon for theme ${json.id} is too small. Please rescale the icon at ${json.icon}`, |                                 `the icon for theme ${json.id} is too small. Please rescale the icon at ${json.icon}`, | ||||||
|                                     `Even though an SVG is 'infinitely scaleable', the icon should be dimensioned bigger. One of the build steps of the theme does convert the image to a PNG (to serve as PWA-icon) and having a small dimension will cause blurry images.`, |                                 `Even though an SVG is 'infinitely scaleable', the icon should be dimensioned bigger. One of the build steps of the theme does convert the image to a PNG (to serve as PWA-icon) and having a small dimension will cause blurry images.`, | ||||||
|                                     ` Width = ${width} height = ${height}; we recommend a size of at least 500px * 500px and to use a square aspect ratio.`, |                                 ` Width = ${width} height = ${height}; we recommend a size of at least 500px * 500px and to use a square aspect ratio.`, | ||||||
|                                 ].join("\n") |                             ].join("\n") | ||||||
|                             ;(json.hideFromOverview ? warnings : errors).push(e) |                             ;(json.hideFromOverview ? warnings : errors).push(e) | ||||||
|                         } |                         } | ||||||
|                     }) |                     }) | ||||||
|  | @ -227,7 +227,6 @@ class ValidateTheme extends DesugaringStep<LayoutConfigJson> { | ||||||
| 
 | 
 | ||||||
|         try { |         try { | ||||||
|             if (this._isBuiltin) { |             if (this._isBuiltin) { | ||||||
| 
 |  | ||||||
|                 if (theme.id !== theme.id.toLowerCase()) { |                 if (theme.id !== theme.id.toLowerCase()) { | ||||||
|                     errors.push("Theme ids should be in lowercase, but it is " + theme.id) |                     errors.push("Theme ids should be in lowercase, but it is " + theme.id) | ||||||
|                 } |                 } | ||||||
|  | @ -239,12 +238,12 @@ class ValidateTheme extends DesugaringStep<LayoutConfigJson> { | ||||||
|                 if (theme.id !== filename) { |                 if (theme.id !== filename) { | ||||||
|                     errors.push( |                     errors.push( | ||||||
|                         "Theme ids should be the same as the name.json, but we got id: " + |                         "Theme ids should be the same as the name.json, but we got id: " + | ||||||
|                         theme.id + |                             theme.id + | ||||||
|                         " and filename " + |                             " and filename " + | ||||||
|                         filename + |                             filename + | ||||||
|                         " (" + |                             " (" + | ||||||
|                         this._path + |                             this._path + | ||||||
|                         ")" |                             ")" | ||||||
|                     ) |                     ) | ||||||
|                 } |                 } | ||||||
|                 this._validateImage.convertJoin( |                 this._validateImage.convertJoin( | ||||||
|  | @ -323,7 +322,7 @@ class OverrideShadowingCheck extends DesugaringStep<LayoutConfigJson> { | ||||||
|     ): { result: LayoutConfigJson; errors?: string[]; warnings?: string[] } { |     ): { result: LayoutConfigJson; errors?: string[]; warnings?: string[] } { | ||||||
|         const overrideAll = json.overrideAll |         const overrideAll = json.overrideAll | ||||||
|         if (overrideAll === undefined) { |         if (overrideAll === undefined) { | ||||||
|             return {result: json} |             return { result: json } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         const errors = [] |         const errors = [] | ||||||
|  | @ -350,7 +349,7 @@ class OverrideShadowingCheck extends DesugaringStep<LayoutConfigJson> { | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         return {result: json, errors} |         return { result: json, errors } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -460,7 +459,7 @@ export class DetectShadowedMappings extends DesugaringStep<QuestionableTagRender | ||||||
|         const errors = [] |         const errors = [] | ||||||
|         const warnings = [] |         const warnings = [] | ||||||
|         if (json.mappings === undefined || json.mappings.length === 0) { |         if (json.mappings === undefined || json.mappings.length === 0) { | ||||||
|             return {result: json} |             return { result: json } | ||||||
|         } |         } | ||||||
|         const defaultProperties = {} |         const defaultProperties = {} | ||||||
|         for (const calculatedTagName of this._calculatedTagNames) { |         for (const calculatedTagName of this._calculatedTagNames) { | ||||||
|  | @ -489,7 +488,7 @@ export class DetectShadowedMappings extends DesugaringStep<QuestionableTagRender | ||||||
|             } |             } | ||||||
|             const keyValues = parsedConditions[i].asChange(defaultProperties) |             const keyValues = parsedConditions[i].asChange(defaultProperties) | ||||||
|             const properties = {} |             const properties = {} | ||||||
|             keyValues.forEach(({k, v}) => { |             keyValues.forEach(({ k, v }) => { | ||||||
|                 properties[k] = v |                 properties[k] = v | ||||||
|             }) |             }) | ||||||
|             for (let j = 0; j < i; j++) { |             for (let j = 0; j < i; j++) { | ||||||
|  | @ -506,10 +505,10 @@ export class DetectShadowedMappings extends DesugaringStep<QuestionableTagRender | ||||||
|                     // The current mapping is shadowed!
 |                     // The current mapping is shadowed!
 | ||||||
|                     errors.push(`At ${context}: Mapping ${i} is shadowed by mapping ${j} and will thus never be shown:
 |                     errors.push(`At ${context}: Mapping ${i} is shadowed by mapping ${j} and will thus never be shown:
 | ||||||
|     The mapping ${parsedConditions[i].asHumanString( |     The mapping ${parsedConditions[i].asHumanString( | ||||||
|                         false, |         false, | ||||||
|                         false, |         false, | ||||||
|                         {} |         {} | ||||||
|                     )} is fully matched by a previous mapping (namely ${j}), which matches: |     )} is fully matched by a previous mapping (namely ${j}), which matches: | ||||||
|     ${parsedConditions[j].asHumanString(false, false, {})}. |     ${parsedConditions[j].asHumanString(false, false, {})}. | ||||||
| 
 | 
 | ||||||
|     To fix this problem, you can try to: |     To fix this problem, you can try to: | ||||||
|  | @ -578,7 +577,7 @@ export class DetectMappingsWithImages extends DesugaringStep<TagRenderingConfigJ | ||||||
|         const warnings: string[] = [] |         const warnings: string[] = [] | ||||||
|         const information: string[] = [] |         const information: string[] = [] | ||||||
|         if (json.mappings === undefined || json.mappings.length === 0) { |         if (json.mappings === undefined || json.mappings.length === 0) { | ||||||
|             return {result: json} |             return { result: json } | ||||||
|         } |         } | ||||||
|         const ignoreToken = "ignore-image-in-then" |         const ignoreToken = "ignore-image-in-then" | ||||||
|         for (let i = 0; i < json.mappings.length; i++) { |         for (let i = 0; i < json.mappings.length; i++) { | ||||||
|  | @ -664,13 +663,13 @@ export class ValidateLayer extends DesugaringStep<LayerConfigJson> { | ||||||
|             if (json.title === undefined) { |             if (json.title === undefined) { | ||||||
|                 errors.push( |                 errors.push( | ||||||
|                     context + |                     context + | ||||||
|                     ": this layer does not have a title defined but it does have tagRenderings. Not having a title will disable the popups, resulting in an unclickable element. Please add a title. If not having a popup is intended and the tagrenderings need to be kept (e.g. in a library layer), set `title: null` to disable this error." |                         ": this layer does not have a title defined but it does have tagRenderings. Not having a title will disable the popups, resulting in an unclickable element. Please add a title. If not having a popup is intended and the tagrenderings need to be kept (e.g. in a library layer), set `title: null` to disable this error." | ||||||
|                 ) |                 ) | ||||||
|             } |             } | ||||||
|             if (json.title === null) { |             if (json.title === null) { | ||||||
|                 information.push( |                 information.push( | ||||||
|                     context + |                     context + | ||||||
|                     ": title is `null`. This results in an element that cannot be clicked - even though tagRenderings is set." |                         ": title is `null`. This results in an element that cannot be clicked - even though tagRenderings is set." | ||||||
|                 ) |                 ) | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  | @ -697,16 +696,16 @@ export class ValidateLayer extends DesugaringStep<LayerConfigJson> { | ||||||
|             if (duplicates.length > 0) { |             if (duplicates.length > 0) { | ||||||
|                 errors.push( |                 errors.push( | ||||||
|                     "At " + |                     "At " + | ||||||
|                     context + |                         context + | ||||||
|                     ": some tagrenderings have a duplicate id: " + |                         ": some tagrenderings have a duplicate id: " + | ||||||
|                     duplicates.join(", ") |                         duplicates.join(", ") | ||||||
|                 ) |                 ) | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if(json.deletion !== undefined && json.deletion instanceof DeleteConfig){ |         if (json.deletion !== undefined && json.deletion instanceof DeleteConfig) { | ||||||
|             if(json.deletion.softDeletionTags === undefined){ |             if (json.deletion.softDeletionTags === undefined) { | ||||||
|                 warnings.push("No soft-deletion tags in deletion block for layer "+json.id) |                 warnings.push("No soft-deletion tags in deletion block for layer " + json.id) | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | @ -717,8 +716,8 @@ export class ValidateLayer extends DesugaringStep<LayerConfigJson> { | ||||||
|                 if (json["overpassTags"] !== undefined) { |                 if (json["overpassTags"] !== undefined) { | ||||||
|                     errors.push( |                     errors.push( | ||||||
|                         "Layer " + |                         "Layer " + | ||||||
|                         json.id + |                             json.id + | ||||||
|                         'still uses the old \'overpassTags\'-format. Please use "source": {"osmTags": <tags>}\' instead of "overpassTags": <tags> (note: this isn\'t your fault, the custom theme generator still spits out the old format)' |                             'still uses the old \'overpassTags\'-format. Please use "source": {"osmTags": <tags>}\' instead of "overpassTags": <tags> (note: this isn\'t your fault, the custom theme generator still spits out the old format)' | ||||||
|                     ) |                     ) | ||||||
|                 } |                 } | ||||||
|                 const forbiddenTopLevel = [ |                 const forbiddenTopLevel = [ | ||||||
|  | @ -736,18 +735,18 @@ export class ValidateLayer extends DesugaringStep<LayerConfigJson> { | ||||||
|                     if (json[forbiddenKey] !== undefined) |                     if (json[forbiddenKey] !== undefined) | ||||||
|                         errors.push( |                         errors.push( | ||||||
|                             context + |                             context + | ||||||
|                             ": layer " + |                                 ": layer " + | ||||||
|                             json.id + |                                 json.id + | ||||||
|                             " still has a forbidden key " + |                                 " still has a forbidden key " + | ||||||
|                             forbiddenKey |                                 forbiddenKey | ||||||
|                         ) |                         ) | ||||||
|                 } |                 } | ||||||
|                 if (json["hideUnderlayingFeaturesMinPercentage"] !== undefined) { |                 if (json["hideUnderlayingFeaturesMinPercentage"] !== undefined) { | ||||||
|                     errors.push( |                     errors.push( | ||||||
|                         context + |                         context + | ||||||
|                         ": layer " + |                             ": layer " + | ||||||
|                         json.id + |                             json.id + | ||||||
|                         " contains an old 'hideUnderlayingFeaturesMinPercentage'" |                             " contains an old 'hideUnderlayingFeaturesMinPercentage'" | ||||||
|                     ) |                     ) | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|  | @ -764,9 +763,9 @@ export class ValidateLayer extends DesugaringStep<LayerConfigJson> { | ||||||
|                 if (this._path != undefined && this._path.indexOf(expected) < 0) { |                 if (this._path != undefined && this._path.indexOf(expected) < 0) { | ||||||
|                     errors.push( |                     errors.push( | ||||||
|                         "Layer is in an incorrect place. The path is " + |                         "Layer is in an incorrect place. The path is " + | ||||||
|                         this._path + |                             this._path + | ||||||
|                         ", but expected " + |                             ", but expected " + | ||||||
|                         expected |                             expected | ||||||
|                     ) |                     ) | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  | @ -824,9 +823,9 @@ export class ValidateLayer extends DesugaringStep<LayerConfigJson> { | ||||||
|                 if (hasCondition?.length > 0) { |                 if (hasCondition?.length > 0) { | ||||||
|                     errors.push( |                     errors.push( | ||||||
|                         "At " + |                         "At " + | ||||||
|                         context + |                             context + | ||||||
|                         ":\n    One or more icons in the mapRenderings have a condition set. Don't do this, as this will result in an invisible but clickable element. Use extra filters in the source instead. The offending mapRenderings are:\n" + |                             ":\n    One or more icons in the mapRenderings have a condition set. Don't do this, as this will result in an invisible but clickable element. Use extra filters in the source instead. The offending mapRenderings are:\n" + | ||||||
|                         JSON.stringify(hasCondition, null, "  ") |                             JSON.stringify(hasCondition, null, "  ") | ||||||
|                     ) |                     ) | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  | @ -838,7 +837,7 @@ export class ValidateLayer extends DesugaringStep<LayerConfigJson> { | ||||||
|                     const preset = json.presets[i] |                     const preset = json.presets[i] | ||||||
|                     const tags: { k: string; v: string }[] = new And( |                     const tags: { k: string; v: string }[] = new And( | ||||||
|                         preset.tags.map((t) => TagUtils.Tag(t)) |                         preset.tags.map((t) => TagUtils.Tag(t)) | ||||||
|                     ).asChange({id: "node/-1"}) |                     ).asChange({ id: "node/-1" }) | ||||||
|                     const properties = {} |                     const properties = {} | ||||||
|                     for (const tag of tags) { |                     for (const tag of tags) { | ||||||
|                         properties[tag.k] = tag.v |                         properties[tag.k] = tag.v | ||||||
|  | @ -847,12 +846,12 @@ export class ValidateLayer extends DesugaringStep<LayerConfigJson> { | ||||||
|                     if (!doMatch) { |                     if (!doMatch) { | ||||||
|                         errors.push( |                         errors.push( | ||||||
|                             context + |                             context + | ||||||
|                             ".presets[" + |                                 ".presets[" + | ||||||
|                             i + |                                 i + | ||||||
|                             "]: This preset does not match the required tags of this layer. This implies that a newly added point will not show up.\n    A newly created point will have properties: " + |                                 "]: This preset does not match the required tags of this layer. This implies that a newly added point will not show up.\n    A newly created point will have properties: " + | ||||||
|                             JSON.stringify(properties) + |                                 JSON.stringify(properties) + | ||||||
|                             "\n    The required tags are: " + |                                 "\n    The required tags are: " + | ||||||
|                             baseTags.asHumanString(false, false, {}) |                                 baseTags.asHumanString(false, false, {}) | ||||||
|                         ) |                         ) | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|  | @ -870,32 +869,45 @@ export class ValidateLayer extends DesugaringStep<LayerConfigJson> { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export class DetectDuplicateFilters extends DesugaringStep<{ layers: LayerConfigJson[], themes: LayoutConfigJson[]}> { | export class DetectDuplicateFilters extends DesugaringStep<{ | ||||||
| 
 |     layers: LayerConfigJson[] | ||||||
|  |     themes: LayoutConfigJson[] | ||||||
|  | }> { | ||||||
|     constructor() { |     constructor() { | ||||||
|         super("Tries to detect layers where a shared filter can be used (or where similar filters occur)", [], "DetectDuplicateFilters"); |         super( | ||||||
|  |             "Tries to detect layers where a shared filter can be used (or where similar filters occur)", | ||||||
|  |             [], | ||||||
|  |             "DetectDuplicateFilters" | ||||||
|  |         ) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Add all filter options into 'perOsmTag' |      * Add all filter options into 'perOsmTag' | ||||||
|      */ |      */ | ||||||
|     private addLayerFilters(layer: LayerConfigJson, perOsmTag: Map<string, { |     private addLayerFilters( | ||||||
|         layer: LayerConfigJson, |         layer: LayerConfigJson, | ||||||
|         layout: LayoutConfigJson | undefined, |         perOsmTag: Map< | ||||||
|         filter: FilterConfigJson |             string, | ||||||
|     }[]>, layout?: LayoutConfigJson | undefined): void { |             { | ||||||
|  |                 layer: LayerConfigJson | ||||||
|  |                 layout: LayoutConfigJson | undefined | ||||||
|  |                 filter: FilterConfigJson | ||||||
|  |             }[] | ||||||
|  |         >, | ||||||
|  |         layout?: LayoutConfigJson | undefined | ||||||
|  |     ): void { | ||||||
|         if (layer.filter === undefined || layer.filter === null) { |         if (layer.filter === undefined || layer.filter === null) { | ||||||
|             return; |             return | ||||||
|         } |         } | ||||||
|         if (layer.filter["sameAs"] !== undefined) { |         if (layer.filter["sameAs"] !== undefined) { | ||||||
|             return; |             return | ||||||
|         } |         } | ||||||
|         for (const filter of (<(string | FilterConfigJson) []>layer.filter)) { |         for (const filter of <(string | FilterConfigJson)[]>layer.filter) { | ||||||
|             if (typeof filter === "string") { |             if (typeof filter === "string") { | ||||||
|                 continue |                 continue | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             if(filter["#"]?.indexOf("ignore-possible-duplicate")>=0){ |             if (filter["#"]?.indexOf("ignore-possible-duplicate") >= 0) { | ||||||
|                 continue |                 continue | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  | @ -908,57 +920,66 @@ export class DetectDuplicateFilters extends DesugaringStep<{ layers: LayerConfig | ||||||
|                     perOsmTag.set(key, []) |                     perOsmTag.set(key, []) | ||||||
|                 } |                 } | ||||||
|                 perOsmTag.get(key).push({ |                 perOsmTag.get(key).push({ | ||||||
|                     layer, filter, layout |                     layer, | ||||||
|  |                     filter, | ||||||
|  |                     layout, | ||||||
|                 }) |                 }) | ||||||
|             } |             } | ||||||
| 
 |  | ||||||
|         } |         } | ||||||
| 
 |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     convert(json: { layers: LayerConfigJson[]; themes: LayoutConfigJson[] }, context: string): { result: { layers: LayerConfigJson[]; themes: LayoutConfigJson[] }; errors?: string[]; warnings?: string[]; information?: string[] } { |     convert( | ||||||
| 
 |         json: { layers: LayerConfigJson[]; themes: LayoutConfigJson[] }, | ||||||
|  |         context: string | ||||||
|  |     ): { | ||||||
|  |         result: { layers: LayerConfigJson[]; themes: LayoutConfigJson[] } | ||||||
|  |         errors?: string[] | ||||||
|  |         warnings?: string[] | ||||||
|  |         information?: string[] | ||||||
|  |     } { | ||||||
|         const errors: string[] = [] |         const errors: string[] = [] | ||||||
|         const warnings: string[] = [] |         const warnings: string[] = [] | ||||||
|         const information: string[] = [] |         const information: string[] = [] | ||||||
| 
 | 
 | ||||||
|         const {layers, themes} = json |         const { layers, themes } = json | ||||||
|         const perOsmTag = new Map<string, { |         const perOsmTag = new Map< | ||||||
|             layer: LayerConfigJson, |             string, | ||||||
|             layout: LayoutConfigJson | undefined, |             { | ||||||
|             filter: FilterConfigJson |                 layer: LayerConfigJson | ||||||
|         }[]>() |                 layout: LayoutConfigJson | undefined | ||||||
|  |                 filter: FilterConfigJson | ||||||
|  |             }[] | ||||||
|  |         >() | ||||||
| 
 | 
 | ||||||
|         for (const layer of layers) { |         for (const layer of layers) { | ||||||
|             this.addLayerFilters(layer, perOsmTag) |             this.addLayerFilters(layer, perOsmTag) | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         for (const theme of themes) { |         for (const theme of themes) { | ||||||
|             if(theme.id === "personal"){ |             if (theme.id === "personal") { | ||||||
|                 continue |                 continue | ||||||
|             } |             } | ||||||
|             for (const layer of theme.layers) { |             for (const layer of theme.layers) { | ||||||
|                 if(typeof layer === "string"){ |                 if (typeof layer === "string") { | ||||||
|                     continue |                     continue | ||||||
|                 } |                 } | ||||||
|                 if(layer["builtin"] !== undefined){ |                 if (layer["builtin"] !== undefined) { | ||||||
|                     continue |                     continue | ||||||
|                 } |                 } | ||||||
|                 this.addLayerFilters(<LayerConfigJson> layer, perOsmTag, theme) |                 this.addLayerFilters(<LayerConfigJson>layer, perOsmTag, theme) | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|         // At this point, we have gathered all filters per tag - time to find duplicates
 |         // At this point, we have gathered all filters per tag - time to find duplicates
 | ||||||
|         perOsmTag.forEach((value, key) => { |         perOsmTag.forEach((value, key) => { | ||||||
|             if(value.length <= 1){ |             if (value.length <= 1) { | ||||||
|                 // Seen this key just once, it is unique
 |                 // Seen this key just once, it is unique
 | ||||||
|                 return; |                 return | ||||||
|             } |             } | ||||||
|             let msg = "Possible duplicate filter: "+ key |             let msg = "Possible duplicate filter: " + key | ||||||
|             for (const {filter, layer, layout} of value) { |             for (const { filter, layer, layout } of value) { | ||||||
|                 let id = "" |                 let id = "" | ||||||
|                 if(layout !== undefined){ |                 if (layout !== undefined) { | ||||||
|                     id = layout.id + ":" |                     id = layout.id + ":" | ||||||
|                 } |                 } | ||||||
|                 msg += `\n      - ${id}${layer.id}.${filter.id}` |                 msg += `\n      - ${id}${layer.id}.${filter.id}` | ||||||
|  | @ -970,8 +991,7 @@ export class DetectDuplicateFilters extends DesugaringStep<{ layers: LayerConfig | ||||||
|             result: json, |             result: json, | ||||||
|             errors, |             errors, | ||||||
|             warnings, |             warnings, | ||||||
|             information |             information, | ||||||
|         }; |         } | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,15 +1,15 @@ | ||||||
| import {Translation} from "../../UI/i18n/Translation" | import { Translation } from "../../UI/i18n/Translation" | ||||||
| import {TagsFilter} from "../../Logic/Tags/TagsFilter" | import { TagsFilter } from "../../Logic/Tags/TagsFilter" | ||||||
| import FilterConfigJson from "./Json/FilterConfigJson" | import FilterConfigJson from "./Json/FilterConfigJson" | ||||||
| import Translations from "../../UI/i18n/Translations" | import Translations from "../../UI/i18n/Translations" | ||||||
| import {TagUtils} from "../../Logic/Tags/TagUtils" | import { TagUtils } from "../../Logic/Tags/TagUtils" | ||||||
| import ValidatedTextField from "../../UI/Input/ValidatedTextField" | import ValidatedTextField from "../../UI/Input/ValidatedTextField" | ||||||
| import {TagConfigJson} from "./Json/TagConfigJson" | import { TagConfigJson } from "./Json/TagConfigJson" | ||||||
| import {UIEventSource} from "../../Logic/UIEventSource" | import { UIEventSource } from "../../Logic/UIEventSource" | ||||||
| import {FilterState} from "../FilteredLayer" | import { FilterState } from "../FilteredLayer" | ||||||
| import {QueryParameters} from "../../Logic/Web/QueryParameters" | import { QueryParameters } from "../../Logic/Web/QueryParameters" | ||||||
| import {Utils} from "../../Utils" | import { Utils } from "../../Utils" | ||||||
| import {RegexTag} from "../../Logic/Tags/RegexTag" | import { RegexTag } from "../../Logic/Tags/RegexTag" | ||||||
| 
 | 
 | ||||||
| export default class FilterConfig { | export default class FilterConfig { | ||||||
|     public readonly id: string |     public readonly id: string | ||||||
|  |  | ||||||
|  | @ -267,7 +267,10 @@ export default class TagRenderingConfig { | ||||||
|                 if (this.freeform.key === "wikidata" && txt.indexOf("{wikipedia()") >= 0) { |                 if (this.freeform.key === "wikidata" && txt.indexOf("{wikipedia()") >= 0) { | ||||||
|                     continue |                     continue | ||||||
|                 } |                 } | ||||||
|                 if (this.freeform.type === "wikidata" && txt.indexOf(`{wikidata_label(${this.freeform.key})`) >= 0) { |                 if ( | ||||||
|  |                     this.freeform.type === "wikidata" && | ||||||
|  |                     txt.indexOf(`{wikidata_label(${this.freeform.key})`) >= 0 | ||||||
|  |                 ) { | ||||||
|                     continue |                     continue | ||||||
|                 } |                 } | ||||||
|                 throw `${context}: The rendering for language ${ln} does not contain the freeform key {${this.freeform.key}}. This is a bug, as this rendering should show exactly this freeform key!\nThe rendering is ${txt} ` |                 throw `${context}: The rendering for language ${ln} does not contain the freeform key {${this.freeform.key}}. This is a bug, as this rendering should show exactly this freeform key!\nThe rendering is ${txt} ` | ||||||
|  |  | ||||||
|  | @ -7,7 +7,6 @@ import Svg from "../../Svg" | ||||||
|  * The little 'translate'-icon next to every icon + some static helper functions |  * The little 'translate'-icon next to every icon + some static helper functions | ||||||
|  */ |  */ | ||||||
| export default class LinkToWeblate extends VariableUiElement { | export default class LinkToWeblate extends VariableUiElement { | ||||||
| 
 |  | ||||||
|     constructor(context: string, availableTranslations: object) { |     constructor(context: string, availableTranslations: object) { | ||||||
|         super( |         super( | ||||||
|             Locale.language.map( |             Locale.language.map( | ||||||
|  |  | ||||||
|  | @ -3,7 +3,7 @@ import Loc from "../../Models/Loc" | ||||||
| import BaseLayer from "../../Models/BaseLayer" | import BaseLayer from "../../Models/BaseLayer" | ||||||
| import { UIEventSource } from "../../Logic/UIEventSource" | import { UIEventSource } from "../../Logic/UIEventSource" | ||||||
| import { BBox } from "../../Logic/BBox" | import { BBox } from "../../Logic/BBox" | ||||||
| import {deprecate} from "util"; | import { deprecate } from "util" | ||||||
| 
 | 
 | ||||||
| export interface MinimapOptions { | export interface MinimapOptions { | ||||||
|     background?: UIEventSource<BaseLayer> |     background?: UIEventSource<BaseLayer> | ||||||
|  | @ -27,7 +27,7 @@ export interface MinimapObj { | ||||||
| 
 | 
 | ||||||
|     TakeScreenshot(format): Promise<string> |     TakeScreenshot(format): Promise<string> | ||||||
|     TakeScreenshot(format: "image"): Promise<string> |     TakeScreenshot(format: "image"): Promise<string> | ||||||
|     TakeScreenshot(format:"blob"): Promise<Blob> |     TakeScreenshot(format: "blob"): Promise<Blob> | ||||||
|     TakeScreenshot(format?: "image" | "blob"): Promise<string | Blob> |     TakeScreenshot(format?: "image" | "blob"): Promise<string | Blob> | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -114,22 +114,22 @@ export default class MinimapImplementation extends BaseUIElement implements Mini | ||||||
|      * @param format: image: give a base64 encoded png image; |      * @param format: image: give a base64 encoded png image; | ||||||
|      * @constructor |      * @constructor | ||||||
|      */ |      */ | ||||||
|     public async TakeScreenshot(): Promise<string> ; |     public async TakeScreenshot(): Promise<string> | ||||||
|     public async TakeScreenshot(format: "image"): Promise<string> ; |     public async TakeScreenshot(format: "image"): Promise<string> | ||||||
|     public async TakeScreenshot(format: "blob"): Promise<Blob> ; |     public async TakeScreenshot(format: "blob"): Promise<Blob> | ||||||
|     public async TakeScreenshot(format: "image" | "blob"): Promise<string | Blob> ; |     public async TakeScreenshot(format: "image" | "blob"): Promise<string | Blob> | ||||||
|     public async TakeScreenshot(format: "image" | "blob" = "image"): Promise<string | Blob> { |     public async TakeScreenshot(format: "image" | "blob" = "image"): Promise<string | Blob> { | ||||||
|         console.log("Taking a screenshot...") |         console.log("Taking a screenshot...") | ||||||
|         const screenshotter = new SimpleMapScreenshoter() |         const screenshotter = new SimpleMapScreenshoter() | ||||||
|         screenshotter.addTo(this.leafletMap.data) |         screenshotter.addTo(this.leafletMap.data) | ||||||
|         const result = <any> await screenshotter.takeScreen((<any> format) ?? "image") |         const result = <any>await screenshotter.takeScreen(<any>format ?? "image") | ||||||
|         if(format === "image" && typeof result === "string"){ |         if (format === "image" && typeof result === "string") { | ||||||
|             return result |             return result | ||||||
|         } |         } | ||||||
|         if(format === "blob" && result instanceof Blob){ |         if (format === "blob" && result instanceof Blob) { | ||||||
|             return result |             return result | ||||||
|         } |         } | ||||||
|         throw "Something went wrong while creating the screenshot: "+result |         throw "Something went wrong while creating the screenshot: " + result | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     protected InnerConstructElement(): HTMLElement { |     protected InnerConstructElement(): HTMLElement { | ||||||
|  |  | ||||||
|  | @ -1,62 +1,73 @@ | ||||||
| import Combine from "../Base/Combine"; | import Combine from "../Base/Combine" | ||||||
| import {FlowPanelFactory, FlowStep} from "../ImportFlow/FlowStep"; | import { FlowPanelFactory, FlowStep } from "../ImportFlow/FlowStep" | ||||||
| import {ImmutableStore, Store, UIEventSource} from "../../Logic/UIEventSource"; | import { ImmutableStore, Store, UIEventSource } from "../../Logic/UIEventSource" | ||||||
| import {InputElement} from "../Input/InputElement"; | import { InputElement } from "../Input/InputElement" | ||||||
| import {SvgToPdf, SvgToPdfOptions} from "../../Utils/svgToPdf"; | import { SvgToPdf, SvgToPdfOptions } from "../../Utils/svgToPdf" | ||||||
| import {FixedInputElement} from "../Input/FixedInputElement"; | import { FixedInputElement } from "../Input/FixedInputElement" | ||||||
| import {FixedUiElement} from "../Base/FixedUiElement"; | import { FixedUiElement } from "../Base/FixedUiElement" | ||||||
| import FileSelectorButton from "../Input/FileSelectorButton"; | import FileSelectorButton from "../Input/FileSelectorButton" | ||||||
| import InputElementMap from "../Input/InputElementMap"; | import InputElementMap from "../Input/InputElementMap" | ||||||
| import {RadioButton} from "../Input/RadioButton"; | import { RadioButton } from "../Input/RadioButton" | ||||||
| import {Utils} from "../../Utils"; | import { Utils } from "../../Utils" | ||||||
| import {VariableUiElement} from "../Base/VariableUIElement"; | import { VariableUiElement } from "../Base/VariableUIElement" | ||||||
| import Loading from "../Base/Loading"; | import Loading from "../Base/Loading" | ||||||
| import BaseUIElement from "../BaseUIElement"; | import BaseUIElement from "../BaseUIElement" | ||||||
| import Img from "../Base/Img"; | import Img from "../Base/Img" | ||||||
| import Title from "../Base/Title"; | import Title from "../Base/Title" | ||||||
| import {CheckBox} from "../Input/Checkboxes"; | import { CheckBox } from "../Input/Checkboxes" | ||||||
| import Minimap from "../Base/Minimap"; | import Minimap from "../Base/Minimap" | ||||||
| import SearchAndGo from "./SearchAndGo"; | import SearchAndGo from "./SearchAndGo" | ||||||
| import Toggle from "../Input/Toggle"; | import Toggle from "../Input/Toggle" | ||||||
| import List from "../Base/List"; | import List from "../Base/List" | ||||||
| import LeftIndex from "../Base/LeftIndex"; | import LeftIndex from "../Base/LeftIndex" | ||||||
| import Constants from "../../Models/Constants"; | import Constants from "../../Models/Constants" | ||||||
| import Toggleable from "../Base/Toggleable"; | import Toggleable from "../Base/Toggleable" | ||||||
| import Lazy from "../Base/Lazy"; | import Lazy from "../Base/Lazy" | ||||||
| import LinkToWeblate from "../Base/LinkToWeblate"; | import LinkToWeblate from "../Base/LinkToWeblate" | ||||||
| import Link from "../Base/Link"; | import Link from "../Base/Link" | ||||||
| import {SearchablePillsSelector} from "../Input/SearchableMappingsSelector"; | import { SearchablePillsSelector } from "../Input/SearchableMappingsSelector" | ||||||
| import * as languages from "../../assets/language_translations.json" | import * as languages from "../../assets/language_translations.json" | ||||||
| import {Translation} from "../i18n/Translation"; | import { Translation } from "../i18n/Translation" | ||||||
| 
 | 
 | ||||||
| class SelectTemplate extends Combine implements FlowStep<{ title: string, pages: string[] }> { | class SelectTemplate extends Combine implements FlowStep<{ title: string; pages: string[] }> { | ||||||
|     readonly IsValid: Store<boolean>; |     readonly IsValid: Store<boolean> | ||||||
|     readonly Value: Store<{ title: string, pages: string[] }>; |     readonly Value: Store<{ title: string; pages: string[] }> | ||||||
| 
 | 
 | ||||||
|     constructor() { |     constructor() { | ||||||
|         const elements: InputElement<{ templateName: string, pages: string[] }>[] = [] |         const elements: InputElement<{ templateName: string; pages: string[] }>[] = [] | ||||||
|         for (const templateName in SvgToPdf.templates) { |         for (const templateName in SvgToPdf.templates) { | ||||||
|             const template = SvgToPdf.templates[templateName] |             const template = SvgToPdf.templates[templateName] | ||||||
|             elements.push(new FixedInputElement( |             elements.push( | ||||||
|                 new Combine([new FixedUiElement(templateName).SetClass("font-bold pr-2"), |                 new FixedInputElement( | ||||||
|                     template.description |                     new Combine([ | ||||||
|                 ]) |                         new FixedUiElement(templateName).SetClass("font-bold pr-2"), | ||||||
|                 , new UIEventSource({templateName, pages: template.pages}))) |                         template.description, | ||||||
|  |                     ]), | ||||||
|  |                     new UIEventSource({ templateName, pages: template.pages }) | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         const file = new FileSelectorButton(new FixedUiElement("Select an svg image which acts as template"), { |         const file = new FileSelectorButton( | ||||||
|             acceptType: "image/svg+xml", |             new FixedUiElement("Select an svg image which acts as template"), | ||||||
|             allowMultiple: true |             { | ||||||
|         }) |                 acceptType: "image/svg+xml", | ||||||
|         const fileMapped = new InputElementMap<FileList, { templateName: string, pages: string[], fromFile: true }>(file, (x0, x1) => x0 === x1, |                 allowMultiple: true, | ||||||
|  |             } | ||||||
|  |         ) | ||||||
|  |         const fileMapped = new InputElementMap< | ||||||
|  |             FileList, | ||||||
|  |             { templateName: string; pages: string[]; fromFile: true } | ||||||
|  |         >( | ||||||
|  |             file, | ||||||
|  |             (x0, x1) => x0 === x1, | ||||||
|             (filelist) => { |             (filelist) => { | ||||||
|                 if (filelist === undefined) { |                 if (filelist === undefined) { | ||||||
|                     return undefined; |                     return undefined | ||||||
|                 } |                 } | ||||||
|                 const pages = [] |                 const pages = [] | ||||||
|                 let templateName: string = undefined; |                 let templateName: string = undefined | ||||||
|                 for (const file of Array.from(filelist)) { |                 for (const file of Array.from(filelist)) { | ||||||
| 
 |  | ||||||
|                     if (templateName == undefined) { |                     if (templateName == undefined) { | ||||||
|                         templateName = file.name.substring(file.name.lastIndexOf("/") + 1) |                         templateName = file.name.substring(file.name.lastIndexOf("/") + 1) | ||||||
|                         templateName = templateName.substring(0, templateName.lastIndexOf(".")) |                         templateName = templateName.substring(0, templateName.lastIndexOf(".")) | ||||||
|  | @ -67,40 +78,46 @@ class SelectTemplate extends Combine implements FlowStep<{ title: string, pages: | ||||||
|                 return { |                 return { | ||||||
|                     templateName, |                     templateName, | ||||||
|                     pages, |                     pages, | ||||||
|                     fromFile: true |                     fromFile: true, | ||||||
|                 } |                 } | ||||||
| 
 |  | ||||||
|             }, |             }, | ||||||
|             _ => undefined |             (_) => undefined | ||||||
|         ) |         ) | ||||||
|         elements.push(fileMapped) |         elements.push(fileMapped) | ||||||
|         const radio = new RadioButton(elements, {selectFirstAsDefault: true}) |         const radio = new RadioButton(elements, { selectFirstAsDefault: true }) | ||||||
| 
 | 
 | ||||||
|         const loaded: Store<{ success: { title: string, pages: string[] } } | { error: any }> = radio.GetValue().bind(template => { |         const loaded: Store<{ success: { title: string; pages: string[] } } | { error: any }> = | ||||||
|             if (template === undefined) { |             radio.GetValue().bind((template) => { | ||||||
|                 return undefined |                 if (template === undefined) { | ||||||
|             } |                     return undefined | ||||||
|             if (template["fromFile"]) { |                 } | ||||||
|                 return UIEventSource.FromPromiseWithErr(Promise.all(template.pages).then(pages => ({ |                 if (template["fromFile"]) { | ||||||
|  |                     return UIEventSource.FromPromiseWithErr( | ||||||
|  |                         Promise.all(template.pages).then((pages) => ({ | ||||||
|  |                             title: template.templateName, | ||||||
|  |                             pages, | ||||||
|  |                         })) | ||||||
|  |                     ) | ||||||
|  |                 } | ||||||
|  |                 const urls = template.pages.map((p) => SelectTemplate.ToUrl(p)) | ||||||
|  |                 const dloadAll: Promise<{ title: string; pages: string[] }> = Promise.all( | ||||||
|  |                     urls.map((url) => Utils.download(url)) | ||||||
|  |                 ).then((pages) => ({ | ||||||
|  |                     pages, | ||||||
|                     title: template.templateName, |                     title: template.templateName, | ||||||
|                     pages |                 })) | ||||||
|                 }))) |  | ||||||
|             } |  | ||||||
|             const urls = template.pages.map(p => SelectTemplate.ToUrl(p)) |  | ||||||
|             const dloadAll: Promise<{ title: string, pages: string[] }> = Promise.all(urls.map(url => Utils.download(url))).then(pages => ({ |  | ||||||
|                 pages, |  | ||||||
|                 title: template.templateName |  | ||||||
|             })) |  | ||||||
| 
 | 
 | ||||||
|             return UIEventSource.FromPromiseWithErr(dloadAll) |                 return UIEventSource.FromPromiseWithErr(dloadAll) | ||||||
|         }) |             }) | ||||||
|         const preview = new VariableUiElement( |         const preview = new VariableUiElement( | ||||||
|             loaded.map(pages => { |             loaded.map((pages) => { | ||||||
|                 if (pages === undefined) { |                 if (pages === undefined) { | ||||||
|                     return new Loading() |                     return new Loading() | ||||||
|                 } |                 } | ||||||
|                 if (pages["error"] !== undefined) { |                 if (pages["error"] !== undefined) { | ||||||
|                     return new FixedUiElement("Loading preview failed: " + pages["err"]).SetClass("alert") |                     return new FixedUiElement("Loading preview failed: " + pages["err"]).SetClass( | ||||||
|  |                         "alert" | ||||||
|  |                     ) | ||||||
|                 } |                 } | ||||||
|                 const svgs = pages["success"].pages |                 const svgs = pages["success"].pages | ||||||
|                 if (svgs.length === 0) { |                 if (svgs.length === 0) { | ||||||
|  | @ -108,22 +125,16 @@ class SelectTemplate extends Combine implements FlowStep<{ title: string, pages: | ||||||
|                 } |                 } | ||||||
|                 const els: BaseUIElement[] = [] |                 const els: BaseUIElement[] = [] | ||||||
|                 for (const pageSrc of svgs) { |                 for (const pageSrc of svgs) { | ||||||
|                     const el = new Img(pageSrc, true) |                     const el = new Img(pageSrc, true).SetClass("w-96 m-2 border-black border-2") | ||||||
|                         .SetClass("w-96 m-2 border-black border-2") |  | ||||||
|                     els.push(el) |                     els.push(el) | ||||||
|                 } |                 } | ||||||
|                 return new Combine(els).SetClass("flex border border-subtle rounded-xl"); |                 return new Combine(els).SetClass("flex border border-subtle rounded-xl") | ||||||
|             }) |             }) | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|         super([ |         super([new Title("Select template"), radio, new Title("Preview"), preview]) | ||||||
|             new Title("Select template"), |         this.Value = loaded.map((l) => (l === undefined ? undefined : l["success"])) | ||||||
|             radio, |         this.IsValid = this.Value.map((v) => v !== undefined) | ||||||
|             new Title("Preview"), |  | ||||||
|             preview |  | ||||||
|         ]); |  | ||||||
|         this.Value = loaded.map(l => l === undefined ? undefined : l["success"]) |  | ||||||
|         this.IsValid = this.Value.map(v => v !== undefined) |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public static ToUrl(spec: string) { |     public static ToUrl(spec: string) { | ||||||
|  | @ -134,63 +145,78 @@ class SelectTemplate extends Combine implements FlowStep<{ title: string, pages: | ||||||
|         path = path.substring(0, path.lastIndexOf("/")) |         path = path.substring(0, path.lastIndexOf("/")) | ||||||
|         return window.location.protocol + "//" + window.location.host + path + "/" + spec |         return window.location.protocol + "//" + window.location.host + path + "/" + spec | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| class SelectPdfOptions extends Combine implements FlowStep<{ title: string, pages: string[], options: SvgToPdfOptions }> { | class SelectPdfOptions | ||||||
|     readonly IsValid: Store<boolean>; |     extends Combine | ||||||
|     readonly Value: Store<{ title: string, pages: string[], options: SvgToPdfOptions }>; |     implements FlowStep<{ title: string; pages: string[]; options: SvgToPdfOptions }> | ||||||
|  | { | ||||||
|  |     readonly IsValid: Store<boolean> | ||||||
|  |     readonly Value: Store<{ title: string; pages: string[]; options: SvgToPdfOptions }> | ||||||
| 
 | 
 | ||||||
|     constructor(title: string, pages: string[], getFreeDiv: () => string) { |     constructor(title: string, pages: string[], getFreeDiv: () => string) { | ||||||
|         const dummy = new CheckBox("Don't add data to the map (to quickly preview the PDF)", false) |         const dummy = new CheckBox("Don't add data to the map (to quickly preview the PDF)", false) | ||||||
|         const overrideMapLocation = new CheckBox("Override map location: use a selected location instead of the location set in the template", false) |         const overrideMapLocation = new CheckBox( | ||||||
|  |             "Override map location: use a selected location instead of the location set in the template", | ||||||
|  |             false | ||||||
|  |         ) | ||||||
|         const locationInput = Minimap.createMiniMap().SetClass("block w-full") |         const locationInput = Minimap.createMiniMap().SetClass("block w-full") | ||||||
|         const searchField = new SearchAndGo({leafletMap: locationInput.leafletMap}) |         const searchField = new SearchAndGo({ leafletMap: locationInput.leafletMap }) | ||||||
|         const selectLocation = |         const selectLocation = new Combine([ | ||||||
|             new Combine([ |             new Toggle( | ||||||
|                 new Toggle(new Combine([new Title("Select override location"), searchField]).SetClass("flex"), undefined, overrideMapLocation.GetValue()), |                 new Combine([new Title("Select override location"), searchField]).SetClass("flex"), | ||||||
|                 new Toggle(locationInput.SetStyle("height: 20rem"), undefined, overrideMapLocation.GetValue()).SetStyle("height: 20rem") |                 undefined, | ||||||
|             ]).SetClass("block").SetStyle("height: 25rem") |                 overrideMapLocation.GetValue() | ||||||
|         super([new Title("Select options"), |             ), | ||||||
|             dummy, |             new Toggle( | ||||||
|             overrideMapLocation, |                 locationInput.SetStyle("height: 20rem"), | ||||||
|             selectLocation |                 undefined, | ||||||
|         ]); |                 overrideMapLocation.GetValue() | ||||||
|         this.Value = dummy.GetValue().map((disableMaps) => { |             ).SetStyle("height: 20rem"), | ||||||
|             return { |         ]) | ||||||
|                 pages, |             .SetClass("block") | ||||||
|                 title, |             .SetStyle("height: 25rem") | ||||||
|                 options: <SvgToPdfOptions>{ |         super([new Title("Select options"), dummy, overrideMapLocation, selectLocation]) | ||||||
|                     disableMaps, |         this.Value = dummy.GetValue().map( | ||||||
|                     getFreeDiv, |             (disableMaps) => { | ||||||
|                     overrideLocation: overrideMapLocation.GetValue().data ? locationInput.location.data : undefined |                 return { | ||||||
|  |                     pages, | ||||||
|  |                     title, | ||||||
|  |                     options: <SvgToPdfOptions>{ | ||||||
|  |                         disableMaps, | ||||||
|  |                         getFreeDiv, | ||||||
|  |                         overrideLocation: overrideMapLocation.GetValue().data | ||||||
|  |                             ? locationInput.location.data | ||||||
|  |                             : undefined, | ||||||
|  |                     }, | ||||||
|                 } |                 } | ||||||
|             } |             }, | ||||||
|         }, [overrideMapLocation.GetValue(), locationInput.location]) |             [overrideMapLocation.GetValue(), locationInput.location] | ||||||
|  |         ) | ||||||
|         this.IsValid = new ImmutableStore(true) |         this.IsValid = new ImmutableStore(true) | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| class PreparePdf extends Combine implements FlowStep<{ svgToPdf: SvgToPdf, languages: string[] }> { | class PreparePdf extends Combine implements FlowStep<{ svgToPdf: SvgToPdf; languages: string[] }> { | ||||||
|     readonly IsValid: Store<boolean>; |     readonly IsValid: Store<boolean> | ||||||
|     readonly Value: Store<{ svgToPdf: SvgToPdf, languages: string[] }>; |     readonly Value: Store<{ svgToPdf: SvgToPdf; languages: string[] }> | ||||||
| 
 | 
 | ||||||
|     constructor(title: string, pages: string[], options: SvgToPdfOptions) { |     constructor(title: string, pages: string[], options: SvgToPdfOptions) { | ||||||
|         const svgToPdf = new SvgToPdf(title, pages, options) |         const svgToPdf = new SvgToPdf(title, pages, options) | ||||||
|         const languageOptions = [ |         const languageOptions = [ | ||||||
|             new FixedInputElement("Nederlands", "nl"), |             new FixedInputElement("Nederlands", "nl"), | ||||||
|             new FixedInputElement("English", "en") |             new FixedInputElement("English", "en"), | ||||||
|         ] |         ] | ||||||
|         const langs: string[] = Array.from(Object.keys(languages["default"] ?? languages)) |         const langs: string[] = Array.from(Object.keys(languages["default"] ?? languages)) | ||||||
|         console.log("Available languages are:", langs) |         console.log("Available languages are:", langs) | ||||||
|         const languageSelector = new SearchablePillsSelector( |         const languageSelector = new SearchablePillsSelector( | ||||||
|             langs.map(l => ({ |             langs.map((l) => ({ | ||||||
|                 show: new Translation(languages[l]), |                 show: new Translation(languages[l]), | ||||||
|                 value: l, |                 value: l, | ||||||
|                 mainTerm: languages[l] |                 mainTerm: languages[l], | ||||||
|             })), { |             })), | ||||||
|                 mode: "select-many" |             { | ||||||
|  |                 mode: "select-many", | ||||||
|             } |             } | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|  | @ -202,45 +228,54 @@ class PreparePdf extends Combine implements FlowStep<{ svgToPdf: SvgToPdf, langu | ||||||
|             new Toggle( |             new Toggle( | ||||||
|                 new Loading("Preparing maps..."), |                 new Loading("Preparing maps..."), | ||||||
|                 undefined, |                 undefined, | ||||||
|                 isPrepared.map(p => p === undefined) |                 isPrepared.map((p) => p === undefined) | ||||||
|             ) |             ), | ||||||
|         ]); |         ]) | ||||||
|         this.Value = isPrepared.map(isPrepped => { |         this.Value = isPrepared.map( | ||||||
|             if (isPrepped === undefined) { |             (isPrepped) => { | ||||||
|                 return undefined |                 if (isPrepped === undefined) { | ||||||
|             } |  | ||||||
|             if (isPrepped["success"] !== undefined) { |  | ||||||
|                 const svgToPdf = isPrepped["success"] |  | ||||||
|                 const langs = languageSelector.GetValue().data |  | ||||||
|                 console.log("Languages are", langs) |  | ||||||
|                 if (langs.length === 0) { |  | ||||||
|                     return undefined |                     return undefined | ||||||
|                 } |                 } | ||||||
|                 return {svgToPdf, languages: langs} |                 if (isPrepped["success"] !== undefined) { | ||||||
|             } |                     const svgToPdf = isPrepped["success"] | ||||||
|             return undefined; |                     const langs = languageSelector.GetValue().data | ||||||
|         }, [languageSelector.GetValue()]) |                     console.log("Languages are", langs) | ||||||
|         this.IsValid = this.Value.map(v => v !== undefined) |                     if (langs.length === 0) { | ||||||
|  |                         return undefined | ||||||
|  |                     } | ||||||
|  |                     return { svgToPdf, languages: langs } | ||||||
|  |                 } | ||||||
|  |                 return undefined | ||||||
|  |             }, | ||||||
|  |             [languageSelector.GetValue()] | ||||||
|  |         ) | ||||||
|  |         this.IsValid = this.Value.map((v) => v !== undefined) | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| class InspectStrings extends Toggle implements FlowStep<{ svgToPdf: SvgToPdf, languages: string[] }> { | class InspectStrings | ||||||
|     readonly IsValid: Store<boolean>; |     extends Toggle | ||||||
|     readonly Value: Store<{ svgToPdf: SvgToPdf; languages: string[] }>; |     implements FlowStep<{ svgToPdf: SvgToPdf; languages: string[] }> | ||||||
|  | { | ||||||
|  |     readonly IsValid: Store<boolean> | ||||||
|  |     readonly Value: Store<{ svgToPdf: SvgToPdf; languages: string[] }> | ||||||
| 
 | 
 | ||||||
|     constructor(svgToPdf: SvgToPdf, languages: string[]) { |     constructor(svgToPdf: SvgToPdf, languages: string[]) { | ||||||
|  |         const didLoadLanguages = UIEventSource.FromPromiseWithErr( | ||||||
|  |             svgToPdf.PrepareLanguages(languages) | ||||||
|  |         ).map((l) => l !== undefined && l["success"] !== undefined) | ||||||
| 
 | 
 | ||||||
|         const didLoadLanguages = UIEventSource.FromPromiseWithErr(svgToPdf.PrepareLanguages(languages)).map(l => l !== undefined && l["success"] !== undefined) |         super( | ||||||
| 
 |             new Combine([ | ||||||
|         super(new Combine([ |  | ||||||
|                 new Title("Inspect translation strings"), |                 new Title("Inspect translation strings"), | ||||||
|                 ...languages.map(l => new Lazy(() => InspectStrings.createOverviewPanel(svgToPdf, l))) |                 ...languages.map( | ||||||
|  |                     (l) => new Lazy(() => InspectStrings.createOverviewPanel(svgToPdf, l)) | ||||||
|  |                 ), | ||||||
|             ]), |             ]), | ||||||
|             new Loading(), |             new Loading(), | ||||||
|             didLoadLanguages |             didLoadLanguages | ||||||
|         ); |         ) | ||||||
|         this.Value = new ImmutableStore({svgToPdf, languages}) |         this.Value = new ImmutableStore({ svgToPdf, languages }) | ||||||
|         this.IsValid = didLoadLanguages |         this.IsValid = didLoadLanguages | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -259,64 +294,82 @@ class InspectStrings extends Toggle implements FlowStep<{ svgToPdf: SvgToPdf, la | ||||||
|             if (translated) { |             if (translated) { | ||||||
|                 foundTranslations++ |                 foundTranslations++ | ||||||
|             } |             } | ||||||
|             const linkToWeblate = new Link(spec, LinkToWeblate.hrefToWeblate(language, spec), true).SetClass("font-bold link-underline") |             const linkToWeblate = new Link( | ||||||
|             elements.push(new Combine([ |                 spec, | ||||||
|                 linkToWeblate, |                 LinkToWeblate.hrefToWeblate(language, spec), | ||||||
|                 " ", |                 true | ||||||
|                 translated ?? new FixedUiElement("No translation found!").SetClass("alert") |             ).SetClass("font-bold link-underline") | ||||||
| 
 |             elements.push( | ||||||
|             ])) |                 new Combine([ | ||||||
|  |                     linkToWeblate, | ||||||
|  |                     " ", | ||||||
|  |                     translated ?? new FixedUiElement("No translation found!").SetClass("alert"), | ||||||
|  |                 ]) | ||||||
|  |             ) | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         return new Toggleable( |         return new Toggleable( | ||||||
|             new Title("Translations for " + language), |             new Title("Translations for " + language), | ||||||
|             new Combine([ |             new Combine([ | ||||||
|                 `${foundTranslations}/${allKeys.length} of translations are found (${Math.floor(100 * foundTranslations / allKeys.length)}%)`, |                 `${foundTranslations}/${allKeys.length} of translations are found (${Math.floor( | ||||||
|  |                     (100 * foundTranslations) / allKeys.length | ||||||
|  |                 )}%)`,
 | ||||||
|                 "The following keys are used:", |                 "The following keys are used:", | ||||||
|                 new List(elements) |                 new List(elements), | ||||||
|             ]), |             ]), | ||||||
|             {closeOnClick: false, height: "15rem"}) |             { closeOnClick: false, height: "15rem" } | ||||||
|  |         ) | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| class SavePdf extends Combine { | class SavePdf extends Combine { | ||||||
| 
 |  | ||||||
|     constructor(svgToPdf: SvgToPdf, languages: string[]) { |     constructor(svgToPdf: SvgToPdf, languages: string[]) { | ||||||
| 
 |  | ||||||
|         super([ |         super([ | ||||||
|             new Title("Generating your pdfs..."), |             new Title("Generating your pdfs..."), | ||||||
|             new List(languages.map(lng => new Toggle( |             new List( | ||||||
|                 lng + " is done!", |                 languages.map( | ||||||
|                 new Loading("Creating pdf for " + lng), |                     (lng) => | ||||||
|                 UIEventSource.FromPromiseWithErr(svgToPdf.ConvertSvg(lng).then(() => true)) |                         new Toggle( | ||||||
|                     .map(x => x !== undefined && x["success"] === true) |                             lng + " is done!", | ||||||
|             ))) |                             new Loading("Creating pdf for " + lng), | ||||||
|         ]); |                             UIEventSource.FromPromiseWithErr( | ||||||
|  |                                 svgToPdf.ConvertSvg(lng).then(() => true) | ||||||
|  |                             ).map((x) => x !== undefined && x["success"] === true) | ||||||
|  |                         ) | ||||||
|  |                 ) | ||||||
|  |             ), | ||||||
|  |         ]) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export class PdfExportGui extends LeftIndex { | export class PdfExportGui extends LeftIndex { | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|     constructor(freeDivId: string) { |     constructor(freeDivId: string) { | ||||||
| 
 |  | ||||||
|         let i = 0 |         let i = 0 | ||||||
|         const createDiv = (): string => { |         const createDiv = (): string => { | ||||||
|             const div = document.createElement("div") |             const div = document.createElement("div") | ||||||
|             div.id = "freediv-" + (i++) |             div.id = "freediv-" + i++ | ||||||
|             document.getElementById(freeDivId).append(div) |             document.getElementById(freeDivId).append(div) | ||||||
|             return div.id |             return div.id | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         Constants.defaultOverpassUrls.splice(0, 1) |         Constants.defaultOverpassUrls.splice(0, 1) | ||||||
|         const {flow, furthestStep, titles} = FlowPanelFactory.start( |         const { flow, furthestStep, titles } = FlowPanelFactory.start( | ||||||
|             new Title("Select template"), new SelectTemplate() |             new Title("Select template"), | ||||||
|         ).then(new Title("Select options"), ({title, pages}) => new SelectPdfOptions(title, pages, createDiv)) |             new SelectTemplate() | ||||||
|             .then("Generate maps...", ({title, pages, options}) => new PreparePdf(title, pages, options)) |         ) | ||||||
|             .then("Inspect translations", (({svgToPdf, languages}) => new InspectStrings(svgToPdf, languages))) |             .then( | ||||||
|             .finish("Generating...", ({svgToPdf, languages}) => new SavePdf(svgToPdf, languages)) |                 new Title("Select options"), | ||||||
| 
 |                 ({ title, pages }) => new SelectPdfOptions(title, pages, createDiv) | ||||||
|  |             ) | ||||||
|  |             .then( | ||||||
|  |                 "Generate maps...", | ||||||
|  |                 ({ title, pages, options }) => new PreparePdf(title, pages, options) | ||||||
|  |             ) | ||||||
|  |             .then( | ||||||
|  |                 "Inspect translations", | ||||||
|  |                 ({ svgToPdf, languages }) => new InspectStrings(svgToPdf, languages) | ||||||
|  |             ) | ||||||
|  |             .finish("Generating...", ({ svgToPdf, languages }) => new SavePdf(svgToPdf, languages)) | ||||||
| 
 | 
 | ||||||
|         const toc = new List( |         const toc = new List( | ||||||
|             titles.map( |             titles.map( | ||||||
|  | @ -338,9 +391,7 @@ export class PdfExportGui extends LeftIndex { | ||||||
|             true |             true | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|         const leftContents: BaseUIElement[] = [ |         const leftContents: BaseUIElement[] = [toc].map((el) => el?.SetClass("pl-4")) | ||||||
|             toc |  | ||||||
|         ].map((el) => el?.SetClass("pl-4")) |  | ||||||
| 
 | 
 | ||||||
|         super(leftContents, flow) |         super(leftContents, flow) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -26,7 +26,7 @@ import BaseLayer from "../../Models/BaseLayer" | ||||||
| import Loading from "../Base/Loading" | import Loading from "../Base/Loading" | ||||||
| import Hash from "../../Logic/Web/Hash" | import Hash from "../../Logic/Web/Hash" | ||||||
| import { GlobalFilter } from "../../Logic/State/MapState" | import { GlobalFilter } from "../../Logic/State/MapState" | ||||||
| import {WayId} from "../../Models/OsmFeature"; | import { WayId } from "../../Models/OsmFeature" | ||||||
| 
 | 
 | ||||||
| /* | /* | ||||||
|  * The SimpleAddUI is a single panel, which can have multiple states: |  * The SimpleAddUI is a single panel, which can have multiple states: | ||||||
|  |  | ||||||
|  | @ -3,10 +3,10 @@ import { UIEventSource } from "../../Logic/UIEventSource" | ||||||
| import { Utils } from "../../Utils" | import { Utils } from "../../Utils" | ||||||
| import BaseUIElement from "../BaseUIElement" | import BaseUIElement from "../BaseUIElement" | ||||||
| import InputElementMap from "./InputElementMap" | import InputElementMap from "./InputElementMap" | ||||||
| import Translations from "../i18n/Translations"; | import Translations from "../i18n/Translations" | ||||||
| 
 | 
 | ||||||
| export class CheckBox extends InputElementMap<number[], boolean> { | export class CheckBox extends InputElementMap<number[], boolean> { | ||||||
|     constructor(el: (BaseUIElement | string), defaultValue?: boolean) { |     constructor(el: BaseUIElement | string, defaultValue?: boolean) { | ||||||
|         super( |         super( | ||||||
|             new CheckBoxes([Translations.W(el)]), |             new CheckBoxes([Translations.W(el)]), | ||||||
|             (x0, x1) => x0 === x1, |             (x0, x1) => x0 === x1, | ||||||
|  |  | ||||||
|  | @ -1,38 +1,40 @@ | ||||||
| import {ReadonlyInputElement} from "./InputElement" | import { ReadonlyInputElement } from "./InputElement" | ||||||
| import Loc from "../../Models/Loc" | import Loc from "../../Models/Loc" | ||||||
| import {Store, UIEventSource} from "../../Logic/UIEventSource" | import { Store, UIEventSource } from "../../Logic/UIEventSource" | ||||||
| import Minimap, {MinimapObj} from "../Base/Minimap" | import Minimap, { MinimapObj } from "../Base/Minimap" | ||||||
| import BaseLayer from "../../Models/BaseLayer" | import BaseLayer from "../../Models/BaseLayer" | ||||||
| import Combine from "../Base/Combine" | import Combine from "../Base/Combine" | ||||||
| import Svg from "../../Svg" | import Svg from "../../Svg" | ||||||
| import {GeoOperations} from "../../Logic/GeoOperations" | import { GeoOperations } from "../../Logic/GeoOperations" | ||||||
| import ShowDataMultiLayer from "../ShowDataLayer/ShowDataMultiLayer" | import ShowDataMultiLayer from "../ShowDataLayer/ShowDataMultiLayer" | ||||||
| import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource" | import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource" | ||||||
| import LayerConfig from "../../Models/ThemeConfig/LayerConfig" | import LayerConfig from "../../Models/ThemeConfig/LayerConfig" | ||||||
| import {BBox} from "../../Logic/BBox" | import { BBox } from "../../Logic/BBox" | ||||||
| import {FixedUiElement} from "../Base/FixedUiElement" | import { FixedUiElement } from "../Base/FixedUiElement" | ||||||
| import ShowDataLayer from "../ShowDataLayer/ShowDataLayer" | import ShowDataLayer from "../ShowDataLayer/ShowDataLayer" | ||||||
| import BaseUIElement from "../BaseUIElement" | import BaseUIElement from "../BaseUIElement" | ||||||
| import Toggle from "./Toggle" | import Toggle from "./Toggle" | ||||||
| import * as matchpoint from "../../assets/layers/matchpoint/matchpoint.json" | import * as matchpoint from "../../assets/layers/matchpoint/matchpoint.json" | ||||||
| import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; | import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig" | ||||||
| import FilteredLayer from "../../Models/FilteredLayer"; | import FilteredLayer from "../../Models/FilteredLayer" | ||||||
| import {ElementStorage} from "../../Logic/ElementStorage"; | import { ElementStorage } from "../../Logic/ElementStorage" | ||||||
| import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers"; | import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers" | ||||||
| import {RelationId, WayId} from "../../Models/OsmFeature"; | import { RelationId, WayId } from "../../Models/OsmFeature" | ||||||
| import {Feature, LineString, Polygon} from "geojson"; | import { Feature, LineString, Polygon } from "geojson" | ||||||
| import {OsmObject, OsmWay} from "../../Logic/Osm/OsmObject"; | import { OsmObject, OsmWay } from "../../Logic/Osm/OsmObject" | ||||||
| 
 | 
 | ||||||
| export default class LocationInput | export default class LocationInput | ||||||
|     extends BaseUIElement |     extends BaseUIElement | ||||||
|     implements ReadonlyInputElement<Loc>, MinimapObj { |     implements ReadonlyInputElement<Loc>, MinimapObj | ||||||
|  | { | ||||||
|     private static readonly matchLayer = new LayerConfig( |     private static readonly matchLayer = new LayerConfig( | ||||||
|         matchpoint, |         matchpoint, | ||||||
|         "LocationInput.matchpoint", |         "LocationInput.matchpoint", | ||||||
|         true |         true | ||||||
|     ) |     ) | ||||||
| 
 | 
 | ||||||
|     public readonly snappedOnto: UIEventSource<Feature & { properties : { id : WayId} }> = new UIEventSource(undefined) |     public readonly snappedOnto: UIEventSource<Feature & { properties: { id: WayId } }> = | ||||||
|  |         new UIEventSource(undefined) | ||||||
|     public readonly _matching_layer: LayerConfig |     public readonly _matching_layer: LayerConfig | ||||||
|     public readonly leafletMap: UIEventSource<any> |     public readonly leafletMap: UIEventSource<any> | ||||||
|     public readonly bounds |     public readonly bounds | ||||||
|  | @ -43,13 +45,15 @@ export default class LocationInput | ||||||
|      * The features to which the input should be snapped |      * The features to which the input should be snapped | ||||||
|      * @private |      * @private | ||||||
|      */ |      */ | ||||||
|     private readonly _snapTo: Store< (Feature<LineString | Polygon> & {properties: {id : WayId}})[]> |     private readonly _snapTo: Store< | ||||||
|  |         (Feature<LineString | Polygon> & { properties: { id: WayId } })[] | ||||||
|  |     > | ||||||
|     /** |     /** | ||||||
|      * The features to which the input should be snapped without cleanup of relations and memberships |      * The features to which the input should be snapped without cleanup of relations and memberships | ||||||
|      * Used for rendering |      * Used for rendering | ||||||
|      * @private |      * @private | ||||||
|      */ |      */ | ||||||
|     private readonly _snapToRaw: Store< {feature: Feature}[]> |     private readonly _snapToRaw: Store<{ feature: Feature }[]> | ||||||
|     private readonly _value: Store<Loc> |     private readonly _value: Store<Loc> | ||||||
|     private readonly _snappedPoint: Store<any> |     private readonly _snappedPoint: Store<any> | ||||||
|     private readonly _maxSnapDistance: number |     private readonly _maxSnapDistance: number | ||||||
|  | @ -59,10 +63,10 @@ export default class LocationInput | ||||||
|     private readonly clickLocation: UIEventSource<Loc> |     private readonly clickLocation: UIEventSource<Loc> | ||||||
|     private readonly _minZoom: number |     private readonly _minZoom: number | ||||||
|     private readonly _state: { |     private readonly _state: { | ||||||
|         readonly filteredLayers: Store<FilteredLayer[]>; |         readonly filteredLayers: Store<FilteredLayer[]> | ||||||
|         readonly backgroundLayer: UIEventSource<BaseLayer>; |         readonly backgroundLayer: UIEventSource<BaseLayer> | ||||||
|         readonly layoutToUse: LayoutConfig; |         readonly layoutToUse: LayoutConfig | ||||||
|         readonly selectedElement: UIEventSource<any>; |         readonly selectedElement: UIEventSource<any> | ||||||
|         readonly allElements: ElementStorage |         readonly allElements: ElementStorage | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -74,27 +78,35 @@ export default class LocationInput | ||||||
|      * |      * | ||||||
|      * @private |      * @private | ||||||
|      */ |      */ | ||||||
|     private static async prepareSnapOnto(features: Feature[]): Promise<(Feature<LineString | Polygon> & {properties : {id: WayId}})[]> { |     private static async prepareSnapOnto( | ||||||
|         const linesAndPolygon : Feature<LineString | Polygon>[] = <any> features.filter(f => f.geometry.type !== "Point") |         features: Feature[] | ||||||
|  |     ): Promise<(Feature<LineString | Polygon> & { properties: { id: WayId } })[]> { | ||||||
|  |         const linesAndPolygon: Feature<LineString | Polygon>[] = <any>( | ||||||
|  |             features.filter((f) => f.geometry.type !== "Point") | ||||||
|  |         ) | ||||||
|         // Clean the features: multipolygons are split into their it's members
 |         // Clean the features: multipolygons are split into their it's members
 | ||||||
|         const linestrings : (Feature<LineString | Polygon> & {properties: {id: WayId}})[] = [] |         const linestrings: (Feature<LineString | Polygon> & { properties: { id: WayId } })[] = [] | ||||||
|         for (const feature of linesAndPolygon) { |         for (const feature of linesAndPolygon) { | ||||||
|             if(feature.properties.id.startsWith("way")){ |             if (feature.properties.id.startsWith("way")) { | ||||||
|                 // A normal way - we continue
 |                 // A normal way - we continue
 | ||||||
|                 linestrings.push(<any> feature) |                 linestrings.push(<any>feature) | ||||||
|                 continue |                 continue | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             // We have a multipolygon, thus: a relation
 |             // We have a multipolygon, thus: a relation
 | ||||||
|             // Download the members
 |             // Download the members
 | ||||||
|             const relation = await OsmObject.DownloadObjectAsync(<RelationId> feature.properties.id, 60 * 60) |             const relation = await OsmObject.DownloadObjectAsync( | ||||||
|             const members: OsmWay[] = await Promise.all(relation.members |                 <RelationId>feature.properties.id, | ||||||
|                 .filter(m => m.type === "way") |                 60 * 60 | ||||||
|                 .map(m => OsmObject.DownloadObjectAsync(<WayId> ("way/"+m.ref), 60 * 60))) |             ) | ||||||
|             linestrings.push(...members.map(m => m.asGeoJson())) |             const members: OsmWay[] = await Promise.all( | ||||||
|  |                 relation.members | ||||||
|  |                     .filter((m) => m.type === "way") | ||||||
|  |                     .map((m) => OsmObject.DownloadObjectAsync(<WayId>("way/" + m.ref), 60 * 60)) | ||||||
|  |             ) | ||||||
|  |             linestrings.push(...members.map((m) => m.asGeoJson())) | ||||||
|         } |         } | ||||||
|         return linestrings |         return linestrings | ||||||
| 
 |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     constructor(options?: { |     constructor(options?: { | ||||||
|  | @ -107,20 +119,32 @@ export default class LocationInput | ||||||
|         centerLocation?: UIEventSource<Loc> |         centerLocation?: UIEventSource<Loc> | ||||||
|         bounds?: UIEventSource<BBox> |         bounds?: UIEventSource<BBox> | ||||||
|         state?: { |         state?: { | ||||||
|             readonly filteredLayers: Store<FilteredLayer[]>; |             readonly filteredLayers: Store<FilteredLayer[]> | ||||||
|             readonly backgroundLayer: UIEventSource<BaseLayer>; |             readonly backgroundLayer: UIEventSource<BaseLayer> | ||||||
|             readonly layoutToUse: LayoutConfig; |             readonly layoutToUse: LayoutConfig | ||||||
|             readonly selectedElement: UIEventSource<any>; |             readonly selectedElement: UIEventSource<any> | ||||||
|             readonly allElements: ElementStorage |             readonly allElements: ElementStorage | ||||||
|         } |         } | ||||||
|     }) { |     }) { | ||||||
|         super() |         super() | ||||||
|         this._snapToRaw = options?.snapTo?.map(feats => feats.filter(f => f.feature.geometry.type !== "Point")) |         this._snapToRaw = options?.snapTo?.map((feats) => | ||||||
|         this._snapTo = options?.snapTo?.bind((features) => UIEventSource.FromPromise(LocationInput.prepareSnapOnto(features.map(f => f.feature))))?.map(f => f ?? []) |             feats.filter((f) => f.feature.geometry.type !== "Point") | ||||||
|  |         ) | ||||||
|  |         this._snapTo = options?.snapTo | ||||||
|  |             ?.bind((features) => | ||||||
|  |                 UIEventSource.FromPromise( | ||||||
|  |                     LocationInput.prepareSnapOnto(features.map((f) => f.feature)) | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|  |             ?.map((f) => f ?? []) | ||||||
|         this._maxSnapDistance = options?.maxSnapDistance |         this._maxSnapDistance = options?.maxSnapDistance | ||||||
|         this._centerLocation = options?.centerLocation ?? new UIEventSource<Loc>({ |         this._centerLocation = | ||||||
|             lat: 0, lon: 0, zoom: 0 |             options?.centerLocation ?? | ||||||
|         }) |             new UIEventSource<Loc>({ | ||||||
|  |                 lat: 0, | ||||||
|  |                 lon: 0, | ||||||
|  |                 zoom: 0, | ||||||
|  |             }) | ||||||
|         this._snappedPointTags = options?.snappedPointTags |         this._snappedPointTags = options?.snappedPointTags | ||||||
|         this._bounds = options?.bounds |         this._bounds = options?.bounds | ||||||
|         this._minZoom = options?.minZoom |         this._minZoom = options?.minZoom | ||||||
|  | @ -152,11 +176,11 @@ export default class LocationInput | ||||||
|                         return undefined |                         return undefined | ||||||
|                     } |                     } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|                     // We reproject the location onto every 'snap-to-feature' and select the closest
 |                     // We reproject the location onto every 'snap-to-feature' and select the closest
 | ||||||
| 
 | 
 | ||||||
|                     let min = undefined |                     let min = undefined | ||||||
|                     let matchedWay: Feature<LineString | Polygon> & {properties : {id : WayId}} = undefined |                     let matchedWay: Feature<LineString | Polygon> & { properties: { id: WayId } } = | ||||||
|  |                         undefined | ||||||
|                     for (const feature of self._snapTo.data ?? []) { |                     for (const feature of self._snapTo.data ?? []) { | ||||||
|                         try { |                         try { | ||||||
|                             const nearestPointOnLine = GeoOperations.nearestPoint(feature, [ |                             const nearestPointOnLine = GeoOperations.nearestPoint(feature, [ | ||||||
|  | @ -191,18 +215,16 @@ export default class LocationInput | ||||||
|                             return { |                             return { | ||||||
|                                 type: "Feature", |                                 type: "Feature", | ||||||
|                                 properties: options?.snappedPointTags ?? min.properties, |                                 properties: options?.snappedPointTags ?? min.properties, | ||||||
|                                 geometry: {type: "Point", coordinates: [loc.lon, loc.lat]}, |                                 geometry: { type: "Point", coordinates: [loc.lon, loc.lat] }, | ||||||
|                             } |                             } | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|                     min.properties = options?.snappedPointTags ?? min.properties |                     min.properties = options?.snappedPointTags ?? min.properties | ||||||
|                     if(matchedWay.properties.id.startsWith("relation/")){ |                     if (matchedWay.properties.id.startsWith("relation/")) { | ||||||
|                         // We matched a relation instead of a way
 |                         // We matched a relation instead of a way
 | ||||||
|                         console.log("Snapping onto a relation. The relation is", matchedWay) |                         console.log("Snapping onto a relation. The relation is", matchedWay) | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|                     } |                     } | ||||||
|                     self.snappedOnto.setData(<any> matchedWay) |                     self.snappedOnto.setData(<any>matchedWay) | ||||||
|                     return min |                     return min | ||||||
|                 }, |                 }, | ||||||
|                 [this._snapTo] |                 [this._snapTo] | ||||||
|  | @ -217,7 +239,10 @@ export default class LocationInput | ||||||
|                 } |                 } | ||||||
|             }) |             }) | ||||||
|         } |         } | ||||||
|         this.mapBackground = options?.mapBackground ?? this._state?.backgroundLayer ?? new UIEventSource<BaseLayer>(AvailableBaseLayers.osmCarto) |         this.mapBackground = | ||||||
|  |             options?.mapBackground ?? | ||||||
|  |             this._state?.backgroundLayer ?? | ||||||
|  |             new UIEventSource<BaseLayer>(AvailableBaseLayers.osmCarto) | ||||||
|         this.SetClass("block h-full") |         this.SetClass("block h-full") | ||||||
| 
 | 
 | ||||||
|         this.clickLocation = new UIEventSource<Loc>(undefined) |         this.clickLocation = new UIEventSource<Loc>(undefined) | ||||||
|  | @ -249,7 +274,7 @@ export default class LocationInput | ||||||
|         try { |         try { | ||||||
|             const self = this |             const self = this | ||||||
|             const hasMoved = new UIEventSource(false) |             const hasMoved = new UIEventSource(false) | ||||||
|             const startLocation = {...this._centerLocation.data} |             const startLocation = { ...this._centerLocation.data } | ||||||
|             this._centerLocation.addCallbackD((newLocation) => { |             this._centerLocation.addCallbackD((newLocation) => { | ||||||
|                 const f = 100000 |                 const f = 100000 | ||||||
|                 console.log(newLocation.lon, startLocation.lon) |                 console.log(newLocation.lon, startLocation.lon) | ||||||
|  | @ -279,7 +304,7 @@ export default class LocationInput | ||||||
|                     if (loc === undefined) { |                     if (loc === undefined) { | ||||||
|                         return [] |                         return [] | ||||||
|                     } |                     } | ||||||
|                     return [{feature: loc}] |                     return [{ feature: loc }] | ||||||
|                 }) |                 }) | ||||||
|                 console.log("Constructing the match layer", matchPoint) |                 console.log("Constructing the match layer", matchPoint) | ||||||
| 
 | 
 | ||||||
|  | @ -335,9 +360,9 @@ export default class LocationInput | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     TakeScreenshot(format: "image"): Promise<string>; |     TakeScreenshot(format: "image"): Promise<string> | ||||||
|     TakeScreenshot(format: "blob"): Promise<Blob>; |     TakeScreenshot(format: "blob"): Promise<Blob> | ||||||
|     TakeScreenshot(format: "image" | "blob"): Promise<string | Blob>; |     TakeScreenshot(format: "image" | "blob"): Promise<string | Blob> | ||||||
|     TakeScreenshot(format: "image" | "blob"): Promise<string | Blob> { |     TakeScreenshot(format: "image" | "blob"): Promise<string | Blob> { | ||||||
|         return this.map.TakeScreenshot(format) |         return this.map.TakeScreenshot(format) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -18,7 +18,7 @@ import Title from "../Base/Title" | ||||||
| import { GlobalFilter } from "../../Logic/State/MapState" | import { GlobalFilter } from "../../Logic/State/MapState" | ||||||
| import { VariableUiElement } from "../Base/VariableUIElement" | import { VariableUiElement } from "../Base/VariableUIElement" | ||||||
| import { Tag } from "../../Logic/Tags/Tag" | import { Tag } from "../../Logic/Tags/Tag" | ||||||
| import {WayId} from "../../Models/OsmFeature"; | import { WayId } from "../../Models/OsmFeature" | ||||||
| 
 | 
 | ||||||
| export default class ConfirmLocationOfPoint extends Combine { | export default class ConfirmLocationOfPoint extends Combine { | ||||||
|     constructor( |     constructor( | ||||||
|  | @ -76,7 +76,7 @@ export default class ConfirmLocationOfPoint extends Combine { | ||||||
|                 snappedPointTags: tags, |                 snappedPointTags: tags, | ||||||
|                 maxSnapDistance: preset.preciseInput.maxSnapDistance, |                 maxSnapDistance: preset.preciseInput.maxSnapDistance, | ||||||
|                 bounds: mapBounds, |                 bounds: mapBounds, | ||||||
|                 state: <any> state |                 state: <any>state, | ||||||
|             }) |             }) | ||||||
|             preciseInput.installBounds(preset.boundsFactor ?? 0.25, true) |             preciseInput.installBounds(preset.boundsFactor ?? 0.25, true) | ||||||
|             preciseInput |             preciseInput | ||||||
|  |  | ||||||
|  | @ -22,7 +22,7 @@ import Title from "../Base/Title" | ||||||
| import { SubstitutedTranslation } from "../SubstitutedTranslation" | import { SubstitutedTranslation } from "../SubstitutedTranslation" | ||||||
| import FeaturePipelineState from "../../Logic/State/FeaturePipelineState" | import FeaturePipelineState from "../../Logic/State/FeaturePipelineState" | ||||||
| import TagRenderingQuestion from "./TagRenderingQuestion" | import TagRenderingQuestion from "./TagRenderingQuestion" | ||||||
| import {OsmId} from "../../Models/OsmFeature"; | import { OsmId } from "../../Models/OsmFeature" | ||||||
| 
 | 
 | ||||||
| export default class DeleteWizard extends Toggle { | export default class DeleteWizard extends Toggle { | ||||||
|     /** |     /** | ||||||
|  |  | ||||||
|  | @ -248,29 +248,27 @@ export default class FeatureInfoBox extends ScrollableFullScreen { | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|         editElements.push( |         editElements.push( | ||||||
|             Toggle.If(state.featureSwitchIsDebugging, |             Toggle.If(state.featureSwitchIsDebugging, () => { | ||||||
|                 () => { |                 const config_all_tags: TagRenderingConfig = new TagRenderingConfig( | ||||||
|                     const config_all_tags: TagRenderingConfig = new TagRenderingConfig( |                     { render: "{all_tags()}" }, | ||||||
|                         { render: "{all_tags()}" }, |                     "" | ||||||
|                         "" |  | ||||||
|                     ) |  | ||||||
|                     const config_download: TagRenderingConfig = new TagRenderingConfig( |  | ||||||
|                         { render: "{export_as_geojson()}" }, |  | ||||||
|                         "" |  | ||||||
|                     ) |  | ||||||
|                     const config_id: TagRenderingConfig = new TagRenderingConfig( |  | ||||||
|                         { render: "{open_in_iD()}" }, |  | ||||||
|                         "" |  | ||||||
|                     ) |  | ||||||
| 
 |  | ||||||
|                     return new Combine([ |  | ||||||
|                         new TagRenderingAnswer(tags, config_all_tags, state), |  | ||||||
|                         new TagRenderingAnswer(tags, config_download, state), |  | ||||||
|                         new TagRenderingAnswer(tags, config_id, state), |  | ||||||
|                         "This is layer " + layerConfig.id, |  | ||||||
|                     ]) |  | ||||||
|                 } |  | ||||||
|                 ) |                 ) | ||||||
|  |                 const config_download: TagRenderingConfig = new TagRenderingConfig( | ||||||
|  |                     { render: "{export_as_geojson()}" }, | ||||||
|  |                     "" | ||||||
|  |                 ) | ||||||
|  |                 const config_id: TagRenderingConfig = new TagRenderingConfig( | ||||||
|  |                     { render: "{open_in_iD()}" }, | ||||||
|  |                     "" | ||||||
|  |                 ) | ||||||
|  | 
 | ||||||
|  |                 return new Combine([ | ||||||
|  |                     new TagRenderingAnswer(tags, config_all_tags, state), | ||||||
|  |                     new TagRenderingAnswer(tags, config_download, state), | ||||||
|  |                     new TagRenderingAnswer(tags, config_id, state), | ||||||
|  |                     "This is layer " + layerConfig.id, | ||||||
|  |                 ]) | ||||||
|  |             }) | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|         return new Combine(editElements).SetClass("flex flex-col") |         return new Combine(editElements).SetClass("flex flex-col") | ||||||
|  |  | ||||||
|  | @ -145,7 +145,7 @@ export default class MoveWizard extends Toggle { | ||||||
|                 minZoom: reason.minZoom, |                 minZoom: reason.minZoom, | ||||||
|                 centerLocation: loc, |                 centerLocation: loc, | ||||||
|                 mapBackground: new UIEventSource<BaseLayer>(preferredBackground), // We detach the layer
 |                 mapBackground: new UIEventSource<BaseLayer>(preferredBackground), // We detach the layer
 | ||||||
|                 state: <any> state |                 state: <any>state, | ||||||
|             }) |             }) | ||||||
| 
 | 
 | ||||||
|             if (reason.lockBounds) { |             if (reason.lockBounds) { | ||||||
|  |  | ||||||
|  | @ -51,18 +51,18 @@ export default class TagApplyButton implements AutoAction { | ||||||
|      * // Should handle escaped ";"
 |      * // Should handle escaped ";"
 | ||||||
|      * TagApplyButton.parseTagSpec("key=value;key0=value0\\;value1") // => [["key","value"],["key0","value0;value1"]]
 |      * TagApplyButton.parseTagSpec("key=value;key0=value0\\;value1") // => [["key","value"],["key0","value0;value1"]]
 | ||||||
|      */ |      */ | ||||||
|     private static parseTagSpec(spec: string): [string, string][]{ |     private static parseTagSpec(spec: string): [string, string][] { | ||||||
|         const tgsSpec : [string, string][] = [] |         const tgsSpec: [string, string][] = [] | ||||||
| 
 | 
 | ||||||
|         while(spec.length > 0){ |         while (spec.length > 0) { | ||||||
|             const [part] = spec.match(/((\\;)|[^;])*/) |             const [part] = spec.match(/((\\;)|[^;])*/) | ||||||
|             spec = spec.substring(part.length + 1) // +1 to remove the pending ';' as well
 |             spec = spec.substring(part.length + 1) // +1 to remove the pending ';' as well
 | ||||||
|             const kv = part.split("=").map((s) => s.trim().replace("\\;",";")) |             const kv = part.split("=").map((s) => s.trim().replace("\\;", ";")) | ||||||
|             if (kv.length == 2) { |             if (kv.length == 2) { | ||||||
|                 tgsSpec.push(<[string, string]> kv) |                 tgsSpec.push(<[string, string]>kv) | ||||||
|             }else if (kv.length < 2) { |             } else if (kv.length < 2) { | ||||||
|                 throw "Invalid key spec: no '=' found in " + spec |                 throw "Invalid key spec: no '=' found in " + spec | ||||||
|             }else{ |             } else { | ||||||
|                 throw "Invalid key spec: multiple '=' found in " + spec |                 throw "Invalid key spec: multiple '=' found in " + spec | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  | @ -83,7 +83,7 @@ export default class TagApplyButton implements AutoAction { | ||||||
|             spec = tagSource.data[spec.replace("$", "")] |             spec = tagSource.data[spec.replace("$", "")] | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|        const tgsSpec = TagApplyButton.parseTagSpec(spec) |         const tgsSpec = TagApplyButton.parseTagSpec(spec) | ||||||
| 
 | 
 | ||||||
|         return tagSource.map((tags) => { |         return tagSource.map((tags) => { | ||||||
|             const newTags: Tag[] = [] |             const newTags: Tag[] = [] | ||||||
|  |  | ||||||
|  | @ -304,7 +304,7 @@ export default class TagRenderingQuestion extends Combine { | ||||||
|             const patchedMapping = <Mapping>{ |             const patchedMapping = <Mapping>{ | ||||||
|                 ...mapping, |                 ...mapping, | ||||||
|                 iconClass: mapping.iconClass ?? `small-height`, |                 iconClass: mapping.iconClass ?? `small-height`, | ||||||
|                 icon: mapping.icon ?? (addIcons ? "./assets/svg/none.svg" : undefined) |                 icon: mapping.icon ?? (addIcons ? "./assets/svg/none.svg" : undefined), | ||||||
|             } |             } | ||||||
|             const fancy = TagRenderingQuestion.GenerateMappingContent( |             const fancy = TagRenderingQuestion.GenerateMappingContent( | ||||||
|                 patchedMapping, |                 patchedMapping, | ||||||
|  |  | ||||||
|  | @ -10,12 +10,15 @@ import * as clusterstyle from "../../assets/layers/cluster_style/cluster_style.j | ||||||
| export default class ShowTileInfo { | export default class ShowTileInfo { | ||||||
|     public static readonly styling = new LayerConfig(clusterstyle, "ShowTileInfo", true) |     public static readonly styling = new LayerConfig(clusterstyle, "ShowTileInfo", true) | ||||||
| 
 | 
 | ||||||
|     constructor(options: { |     constructor( | ||||||
|         source: FeatureSource & Tiled |         options: { | ||||||
|         leafletMap: UIEventSource<any> |             source: FeatureSource & Tiled | ||||||
|         layer?: LayerConfig |             leafletMap: UIEventSource<any> | ||||||
|         doShowLayer?: UIEventSource<boolean> |             layer?: LayerConfig | ||||||
|     }, state) { |             doShowLayer?: UIEventSource<boolean> | ||||||
|  |         }, | ||||||
|  |         state | ||||||
|  |     ) { | ||||||
|         const source = options.source |         const source = options.source | ||||||
|         const metaFeature: Store<{ feature; freshness: Date }[]> = source.features.map( |         const metaFeature: Store<{ feature; freshness: Date }[]> = source.features.map( | ||||||
|             (features) => { |             (features) => { | ||||||
|  | @ -55,7 +58,7 @@ export default class ShowTileInfo { | ||||||
|             features: new StaticFeatureSource(metaFeature), |             features: new StaticFeatureSource(metaFeature), | ||||||
|             leafletMap: options.leafletMap, |             leafletMap: options.leafletMap, | ||||||
|             doShowLayer: options.doShowLayer, |             doShowLayer: options.doShowLayer, | ||||||
|             state |             state, | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -41,7 +41,10 @@ export default class Translations { | ||||||
|      * translation.textFor("nl") // => "Nederlands"
 |      * translation.textFor("nl") // => "Nederlands"
 | ||||||
|      * |      * | ||||||
|      */ |      */ | ||||||
|     static T(t: string | undefined | null | Translation | TypedTranslation<object>, context = undefined): TypedTranslation<object> { |     static T( | ||||||
|  |         t: string | undefined | null | Translation | TypedTranslation<object>, | ||||||
|  |         context = undefined | ||||||
|  |     ): TypedTranslation<object> { | ||||||
|         if (t === undefined || t === null) { |         if (t === undefined || t === null) { | ||||||
|             return undefined |             return undefined | ||||||
|         } |         } | ||||||
|  |  | ||||||
							
								
								
									
										2
									
								
								Utils.ts
									
										
									
									
									
								
							
							
						
						
									
										2
									
								
								Utils.ts
									
										
									
									
									
								
							|  | @ -823,7 +823,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be | ||||||
|                 } else if (xhr.status === 509 || xhr.status === 429) { |                 } else if (xhr.status === 509 || xhr.status === 429) { | ||||||
|                     reject("rate limited") |                     reject("rate limited") | ||||||
|                 } else { |                 } else { | ||||||
|                     reject("Could not download "+url+" due to "+xhr.statusText) |                     reject("Could not download " + url + " due to " + xhr.statusText) | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             xhr.open("GET", url) |             xhr.open("GET", url) | ||||||
|  |  | ||||||
|  | @ -1,11 +1,11 @@ | ||||||
| import MinimapImplementation from "./UI/Base/MinimapImplementation"; | import MinimapImplementation from "./UI/Base/MinimapImplementation" | ||||||
| 
 | 
 | ||||||
| import { Utils } from "./Utils" | import { Utils } from "./Utils" | ||||||
| import AllThemesGui from "./UI/AllThemesGui" | import AllThemesGui from "./UI/AllThemesGui" | ||||||
| import { QueryParameters } from "./Logic/Web/QueryParameters" | import { QueryParameters } from "./Logic/Web/QueryParameters" | ||||||
| import StatisticsGUI from "./UI/StatisticsGUI" | import StatisticsGUI from "./UI/StatisticsGUI" | ||||||
| import { FixedUiElement } from "./UI/Base/FixedUiElement" | import { FixedUiElement } from "./UI/Base/FixedUiElement" | ||||||
| import {PdfExportGui} from "./UI/BigComponents/PdfExportGui"; | import { PdfExportGui } from "./UI/BigComponents/PdfExportGui" | ||||||
| 
 | 
 | ||||||
| const layout = QueryParameters.GetQueryParameter("layout", undefined).data ?? "" | const layout = QueryParameters.GetQueryParameter("layout", undefined).data ?? "" | ||||||
| const customLayout = QueryParameters.GetQueryParameter("userlayout", undefined).data ?? "" | const customLayout = QueryParameters.GetQueryParameter("userlayout", undefined).data ?? "" | ||||||
|  | @ -45,7 +45,7 @@ if (mode.data === "statistics") { | ||||||
|     console.log("Statistics mode!") |     console.log("Statistics mode!") | ||||||
|     new FixedUiElement("").AttachTo("centermessage") |     new FixedUiElement("").AttachTo("centermessage") | ||||||
|     new StatisticsGUI().SetClass("w-full h-full pointer-events-auto").AttachTo("topleft-tools") |     new StatisticsGUI().SetClass("w-full h-full pointer-events-auto").AttachTo("topleft-tools") | ||||||
| } else if(mode.data === "pdf"){ | } else if (mode.data === "pdf") { | ||||||
|     MinimapImplementation.initialize() |     MinimapImplementation.initialize() | ||||||
|     new FixedUiElement("").AttachTo("centermessage") |     new FixedUiElement("").AttachTo("centermessage") | ||||||
|     const div = document.createElement("div") |     const div = document.createElement("div") | ||||||
|  |  | ||||||
|  | @ -108,10 +108,11 @@ function main() { | ||||||
|             { |             { | ||||||
|                 id: "tactile_writing-braille", |                 id: "tactile_writing-braille", | ||||||
|                 // @ts-ignore
 |                 // @ts-ignore
 | ||||||
|                 description: "Enables to pick *multiple* 'tactile_writing:braille=<lng>' within the mappings", |                 description: | ||||||
|  |                     "Enables to pick *multiple* 'tactile_writing:braille=<lng>' within the mappings", | ||||||
|                 multiAnswer: true, |                 multiAnswer: true, | ||||||
|                 mappings: brailemappings, |                 mappings: brailemappings, | ||||||
|             } |             }, | ||||||
|         ], |         ], | ||||||
|     } |     } | ||||||
|     const dir = "./assets/layers/wikidata/" |     const dir = "./assets/layers/wikidata/" | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue