Formatting

This commit is contained in:
Pieter Vander Vennet 2022-10-27 01:50:41 +02:00
parent 6d822b42ca
commit 61aebc61eb
32 changed files with 664 additions and 511 deletions

View file

@ -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

View file

@ -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)
} }
}) })

View file

@ -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) {

View file

@ -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.

View file

@ -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(

View file

@ -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,
@ -42,8 +41,9 @@ export default class DeleteAction extends OsmChangeAction {
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 [

View file

@ -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: {

View file

@ -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
} }

View file

@ -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)
} }

View file

@ -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)
} }
} }

View file

@ -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<

View file

@ -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[]
@ -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 }
} }
} }
@ -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)
} }
@ -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++) {
@ -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++) {
@ -704,9 +703,9 @@ export class ValidateLayer extends DesugaringStep<LayerConfigJson> {
} }
} }
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)
} }
} }
@ -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
@ -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<
string,
{
layer: LayerConfigJson
layout: LayoutConfigJson | undefined
filter: FilterConfigJson filter: FilterConfigJson
}[]>, layout?: LayoutConfigJson | undefined): void { }[]
>,
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[] },
convert(json: { layers: LayerConfigJson[]; themes: LayoutConfigJson[] }, context: string): { result: { layers: LayerConfigJson[]; themes: LayoutConfigJson[] }; errors?: string[]; warnings?: string[]; information?: string[] } { 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, {
layer: LayerConfigJson
layout: LayoutConfigJson | undefined
filter: FilterConfigJson 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,
}; }
} }
} }

View file

@ -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

View file

@ -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} `

View file

@ -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(

View file

@ -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>
} }

View file

@ -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 {

View file

@ -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(
new FixedUiElement("Select an svg image which acts as template"),
{
acceptType: "image/svg+xml", acceptType: "image/svg+xml",
allowMultiple: true allowMultiple: true,
}) }
const fileMapped = new InputElementMap<FileList, { templateName: string, pages: string[], fromFile: true }>(file, (x0, x1) => x0 === x1, )
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 }> =
radio.GetValue().bind((template) => {
if (template === undefined) { if (template === undefined) {
return undefined return undefined
} }
if (template["fromFile"]) { if (template["fromFile"]) {
return UIEventSource.FromPromiseWithErr(Promise.all(template.pages).then(pages => ({ return UIEventSource.FromPromiseWithErr(
Promise.all(template.pages).then((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, pages,
title: template.templateName }))
)
}
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"),
])
.SetClass("block")
.SetStyle("height: 25rem")
super([new Title("Select options"), dummy, overrideMapLocation, selectLocation])
this.Value = dummy.GetValue().map(
(disableMaps) => {
return { return {
pages, pages,
title, title,
options: <SvgToPdfOptions>{ options: <SvgToPdfOptions>{
disableMaps, disableMaps,
getFreeDiv, getFreeDiv,
overrideLocation: overrideMapLocation.GetValue().data ? locationInput.location.data : undefined 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,10 +228,11 @@ 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(
(isPrepped) => {
if (isPrepped === undefined) { if (isPrepped === undefined) {
return undefined return undefined
} }
@ -216,31 +243,39 @@ class PreparePdf extends Combine implements FlowStep<{ svgToPdf: SvgToPdf, langu
if (langs.length === 0) { if (langs.length === 0) {
return undefined return undefined
} }
return {svgToPdf, languages: langs} return { svgToPdf, languages: langs }
} }
return undefined; return undefined
}, [languageSelector.GetValue()]) },
this.IsValid = this.Value.map(v => v !== 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.hrefToWeblate(language, spec),
true
).SetClass("font-bold link-underline")
elements.push(
new Combine([
linkToWeblate, linkToWeblate,
"&nbsp;", "&nbsp;",
translated ?? new FixedUiElement("No translation found!").SetClass("alert") 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(
languages.map(
(lng) =>
new Toggle(
lng + " is done!", lng + " is done!",
new Loading("Creating pdf for " + lng), new Loading("Creating pdf for " + lng),
UIEventSource.FromPromiseWithErr(svgToPdf.ConvertSvg(lng).then(() => true)) UIEventSource.FromPromiseWithErr(
.map(x => x !== undefined && x["success"] === true) 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)
} }

View file

@ -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:

View file

@ -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,

View file

@ -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,19 +119,31 @@ 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
@ -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)
} }

View file

@ -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

View file

@ -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 {
/** /**

View file

@ -248,8 +248,7 @@ 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()}" },
"" ""
@ -269,8 +268,7 @@ export default class FeatureInfoBox extends ScrollableFullScreen {
new TagRenderingAnswer(tags, config_id, state), new TagRenderingAnswer(tags, config_id, state),
"This is layer " + layerConfig.id, "This is layer " + layerConfig.id,
]) ])
} })
)
) )
return new Combine(editElements).SetClass("flex flex-col") return new Combine(editElements).SetClass("flex flex-col")

View file

@ -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) {

View file

@ -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
} }
} }

View file

@ -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,

View file

@ -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(
options: {
source: FeatureSource & Tiled source: FeatureSource & Tiled
leafletMap: UIEventSource<any> leafletMap: UIEventSource<any>
layer?: LayerConfig layer?: LayerConfig
doShowLayer?: UIEventSource<boolean> doShowLayer?: UIEventSource<boolean>
}, state) { },
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,
}) })
} }
} }

View file

@ -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
} }

View file

@ -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)

View file

@ -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")

View file

@ -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/"