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 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"
|
||||
}
|
||||
let bounds: BBox
|
||||
|
|
|
@ -46,9 +46,9 @@ export default class TitleHandler {
|
|||
if (Utils.runningFromConsole) {
|
||||
return
|
||||
}
|
||||
try{
|
||||
document.title = title
|
||||
}catch (e) {
|
||||
try {
|
||||
document.title = title
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
})
|
||||
|
|
|
@ -84,7 +84,7 @@ export class NewGeometryFromChangesFeatureSource implements FeatureSource {
|
|||
}
|
||||
|
||||
try {
|
||||
const tags: OsmTags & {id: OsmId & string} = {
|
||||
const tags: OsmTags & { id: OsmId & string } = {
|
||||
id: <OsmId & string>(change.type + "/" + change.id),
|
||||
}
|
||||
for (const kv of change.tags) {
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import FeatureSource, {FeatureSourceForLayer, Tiled} from "../FeatureSource"
|
||||
import {ImmutableStore, Store} from "../../UIEventSource"
|
||||
import FeatureSource, { FeatureSourceForLayer, Tiled } from "../FeatureSource"
|
||||
import { ImmutableStore, Store } from "../../UIEventSource"
|
||||
import FilteredLayer from "../../../Models/FilteredLayer"
|
||||
import {BBox} from "../../BBox"
|
||||
import {Feature} from "geojson";
|
||||
import { BBox } from "../../BBox"
|
||||
import { Feature } from "geojson"
|
||||
|
||||
/**
|
||||
* A simple, read only feature store.
|
||||
|
|
|
@ -19,7 +19,11 @@ export default class CreateNewNodeAction extends OsmCreateAction {
|
|||
private readonly _lon: number
|
||||
private readonly _snapOnto: OsmWay
|
||||
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
|
||||
|
||||
constructor(
|
||||
|
|
|
@ -3,10 +3,10 @@ import OsmChangeAction from "./OsmChangeAction"
|
|||
import { Changes } from "../Changes"
|
||||
import { ChangeDescription } from "./ChangeDescription"
|
||||
import ChangeTagAction from "./ChangeTagAction"
|
||||
import {TagsFilter} from "../../Tags/TagsFilter"
|
||||
import {And} from "../../Tags/And"
|
||||
import {Tag} from "../../Tags/Tag"
|
||||
import {OsmId} from "../../../Models/OsmFeature";
|
||||
import { TagsFilter } from "../../Tags/TagsFilter"
|
||||
import { And } from "../../Tags/And"
|
||||
import { Tag } from "../../Tags/Tag"
|
||||
import { OsmId } from "../../../Models/OsmFeature"
|
||||
import { Utils } from "../../../Utils"
|
||||
|
||||
export default class DeleteAction extends OsmChangeAction {
|
||||
|
@ -19,7 +19,6 @@ export default class DeleteAction extends OsmChangeAction {
|
|||
private readonly _id: OsmId
|
||||
private readonly _hardDelete: boolean
|
||||
|
||||
|
||||
constructor(
|
||||
id: OsmId,
|
||||
softDeletionTags: TagsFilter | undefined,
|
||||
|
@ -39,11 +38,12 @@ export default class DeleteAction extends OsmChangeAction {
|
|||
this._softDeletionTags = new And(
|
||||
Utils.NoNull([
|
||||
softDeletionTags,
|
||||
new Tag(
|
||||
"fixme",
|
||||
`A mapcomplete user marked this feature to be deleted (${meta.specialMotivation})`
|
||||
)
|
||||
]))
|
||||
new Tag(
|
||||
"fixme",
|
||||
`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)
|
||||
* descr[0] // => {doDelete: true, meta: {theme: "test", specialMotivation: "Testcase", changeType: "deletion"}, type: "node",id: 1 }
|
||||
*/
|
||||
public async CreateChangeDescriptions(changes: Changes, object?: OsmObject): Promise<ChangeDescription[]> {
|
||||
const osmObject = object ?? await OsmObject.DownloadObjectAsync(this._id)
|
||||
public async CreateChangeDescriptions(
|
||||
changes: Changes,
|
||||
object?: OsmObject
|
||||
): Promise<ChangeDescription[]> {
|
||||
const osmObject = object ?? (await OsmObject.DownloadObjectAsync(this._id))
|
||||
|
||||
if (this._hardDelete) {
|
||||
return [
|
||||
|
|
|
@ -13,7 +13,7 @@ import { Utils } from "../../../Utils"
|
|||
import { OsmConnection } from "../OsmConnection"
|
||||
import { Feature } from "@turf/turf"
|
||||
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 {
|
||||
/**
|
||||
|
@ -85,7 +85,7 @@ export default class ReplaceGeometryAction extends OsmChangeAction {
|
|||
public async getPreview(): Promise<FeatureSource> {
|
||||
const { closestIds, allNodesById, detachedNodes, reprojectedNodes } =
|
||||
await this.GetClosestIds()
|
||||
const preview: Feature<Geometry> [] = closestIds.map((newId, i) => {
|
||||
const preview: Feature<Geometry>[] = closestIds.map((newId, i) => {
|
||||
if (this.identicalTo[i] !== undefined) {
|
||||
return undefined
|
||||
}
|
||||
|
@ -390,7 +390,7 @@ export default class ReplaceGeometryAction extends OsmChangeAction {
|
|||
const node = allNodesById.get(id)
|
||||
|
||||
// Project the node onto the target way to calculate the new coordinates
|
||||
const way = <Feature<LineString>> {
|
||||
const way = <Feature<LineString>>{
|
||||
type: "Feature",
|
||||
properties: {},
|
||||
geometry: {
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import {Utils} from "../../Utils"
|
||||
import { Utils } from "../../Utils"
|
||||
import * as polygon_features from "../../assets/polygon-features.json"
|
||||
import {Store, UIEventSource} from "../UIEventSource"
|
||||
import {BBox} from "../BBox"
|
||||
import { Store, UIEventSource } from "../UIEventSource"
|
||||
import { BBox } from "../BBox"
|
||||
import * as OsmToGeoJson from "osmtogeojson"
|
||||
import {NodeId, OsmFeature, OsmId, OsmTags, RelationId, WayId} from "../../Models/OsmFeature"
|
||||
import {Feature, LineString, Polygon} from "geojson";
|
||||
import { NodeId, OsmFeature, OsmId, OsmTags, RelationId, WayId } from "../../Models/OsmFeature"
|
||||
import { Feature, LineString, Polygon } from "geojson"
|
||||
|
||||
export abstract class OsmObject {
|
||||
private static defaultBackend = "https://www.openstreetmap.org/"
|
||||
|
@ -17,7 +17,7 @@ export abstract class OsmObject {
|
|||
/**
|
||||
* The OSM tags as simple object
|
||||
*/
|
||||
tags: OsmTags & {id: OsmId}
|
||||
tags: OsmTags & { id: OsmId }
|
||||
version: number
|
||||
public changed: boolean = false
|
||||
timestamp: Date
|
||||
|
@ -41,9 +41,9 @@ export abstract class OsmObject {
|
|||
this.backendURL = url
|
||||
}
|
||||
|
||||
public static DownloadObject(id: NodeId, forceRefresh?: boolean): Store<OsmNode> ;
|
||||
public static DownloadObject(id: RelationId, forceRefresh?: boolean): Store<OsmRelation> ;
|
||||
public static DownloadObject(id: WayId, forceRefresh?: boolean): Store<OsmWay> ;
|
||||
public static DownloadObject(id: NodeId, forceRefresh?: boolean): Store<OsmNode>
|
||||
public static DownloadObject(id: RelationId, forceRefresh?: boolean): Store<OsmRelation>
|
||||
public static DownloadObject(id: WayId, forceRefresh?: boolean): Store<OsmWay>
|
||||
public static DownloadObject(id: string, forceRefresh: boolean = false): Store<OsmObject> {
|
||||
let src: UIEventSource<OsmObject>
|
||||
if (OsmObject.objectCache.has(id)) {
|
||||
|
@ -73,12 +73,30 @@ export abstract class OsmObject {
|
|||
return rawData.elements[0].tags
|
||||
}
|
||||
|
||||
static async DownloadObjectAsync(id: NodeId, maxCacheAgeInSecs?: number): Promise<OsmNode | undefined>
|
||||
static async DownloadObjectAsync(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> {
|
||||
static async DownloadObjectAsync(
|
||||
id: NodeId,
|
||||
maxCacheAgeInSecs?: number
|
||||
): Promise<OsmNode | undefined>
|
||||
static async DownloadObjectAsync(
|
||||
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 type = splitted[0]
|
||||
const idN = Number(splitted[1])
|
||||
|
@ -210,7 +228,7 @@ export abstract class OsmObject {
|
|||
case "relation":
|
||||
osmObject = new OsmRelation(idN)
|
||||
const allGeojsons = OsmToGeoJson.default(
|
||||
{elements},
|
||||
{ elements },
|
||||
// @ts-ignore
|
||||
{
|
||||
flatProperties: true,
|
||||
|
@ -264,14 +282,16 @@ export abstract class OsmObject {
|
|||
return false
|
||||
}
|
||||
|
||||
private static constructPolygonFeatures(): Map<string,
|
||||
{ values: Set<string>; blacklist: boolean }> {
|
||||
private static constructPolygonFeatures(): 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) {
|
||||
const key = polygonFeature.key
|
||||
|
||||
if (polygonFeature.polygon === "all") {
|
||||
result.set(key, {values: null, blacklist: false})
|
||||
result.set(key, { values: null, blacklist: false })
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import {Tag} from "./Tag"
|
||||
import {TagsFilter} from "./TagsFilter"
|
||||
import {And} from "./And"
|
||||
import {Utils} from "../../Utils"
|
||||
import { Tag } from "./Tag"
|
||||
import { TagsFilter } from "./TagsFilter"
|
||||
import { And } from "./And"
|
||||
import { Utils } from "../../Utils"
|
||||
import ComparingTag from "./ComparingTag"
|
||||
import {RegexTag} from "./RegexTag"
|
||||
import { RegexTag } from "./RegexTag"
|
||||
import SubstitutingTag from "./SubstitutingTag"
|
||||
import {Or} from "./Or"
|
||||
import {TagConfigJson} from "../../Models/ThemeConfig/Json/TagConfigJson"
|
||||
import {isRegExp} from "util"
|
||||
import { Or } from "./Or"
|
||||
import { TagConfigJson } from "../../Models/ThemeConfig/Json/TagConfigJson"
|
||||
import { isRegExp } from "util"
|
||||
import * as key_counts from "../../assets/key_totals.json"
|
||||
|
||||
type Tags = Record<string, string>
|
||||
|
@ -373,7 +373,7 @@ export class TagUtils {
|
|||
return null
|
||||
}
|
||||
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) {
|
||||
const split = Utils.SplitFirst(tag, "~~")
|
||||
let keyRegex: RegExp;
|
||||
let keyRegex: RegExp
|
||||
if (split[0] === "*") {
|
||||
keyRegex = new RegExp(".+","i")
|
||||
keyRegex = new RegExp(".+", "i")
|
||||
} else {
|
||||
keyRegex = new RegExp("^(" + split[0] + ")$")
|
||||
}
|
||||
|
@ -569,10 +569,7 @@ export class TagUtils {
|
|||
} else {
|
||||
valueRegex = new RegExp("^(" + split[1] + ")$", "s")
|
||||
}
|
||||
return new RegexTag(
|
||||
keyRegex,
|
||||
valueRegex
|
||||
)
|
||||
return new RegexTag(keyRegex, valueRegex)
|
||||
}
|
||||
const withRegex = TagUtils.parseRegexOperator(tag)
|
||||
if (withRegex != null) {
|
||||
|
@ -627,7 +624,7 @@ export class TagUtils {
|
|||
)
|
||||
}
|
||||
if (split[1] === "") {
|
||||
return new RegexTag(split[0], /.+/si)
|
||||
return new RegexTag(split[0], /.+/is)
|
||||
}
|
||||
return new RegexTag(split[0], split[1], true)
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import Hash from "./Hash"
|
|||
import { Utils } from "../../Utils"
|
||||
|
||||
export class QueryParameters {
|
||||
static defaults : Record<string, string> = {}
|
||||
static defaults: Record<string, string> = {}
|
||||
static documentation: Map<string, string> = new Map<string, string>()
|
||||
private static order: string[] = ["layout", "test", "z", "lat", "lon"]
|
||||
protected static readonly _wasInitialized: Set<string> = new Set()
|
||||
|
@ -105,9 +105,9 @@ export class QueryParameters {
|
|||
}
|
||||
if (!Utils.runningFromConsole) {
|
||||
// Don't pollute the history every time a parameter changes
|
||||
try{
|
||||
try {
|
||||
history.replaceState(null, "", "?" + parts.join("&") + Hash.Current())
|
||||
}catch(e){
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,64 +1,90 @@
|
|||
import {Concat, Conversion, DesugaringContext, DesugaringStep, Each, FirstOf, Fuse, On, SetDefault,} from "./Conversion"
|
||||
import {LayerConfigJson} from "../Json/LayerConfigJson"
|
||||
import {TagRenderingConfigJson} from "../Json/TagRenderingConfigJson"
|
||||
import {Utils} from "../../../Utils"
|
||||
import {
|
||||
Concat,
|
||||
Conversion,
|
||||
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 SpecialVisualizations from "../../../UI/SpecialVisualizations"
|
||||
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 {AddContextToTranslations} from "./AddContextToTranslations"
|
||||
import FilterConfigJson from "../Json/FilterConfigJson";
|
||||
import { AddContextToTranslations } from "./AddContextToTranslations"
|
||||
import FilterConfigJson from "../Json/FilterConfigJson"
|
||||
import * as predifined_filters from "../../../assets/layers/filters/filters.json"
|
||||
|
||||
class ExpandFilter extends DesugaringStep<LayerConfigJson>{
|
||||
|
||||
|
||||
private static load_filters(): Map<string, FilterConfigJson>{
|
||||
let filters = new Map<string, FilterConfigJson>();
|
||||
for (const filter of (<FilterConfigJson[]>predifined_filters.filter)) {
|
||||
class ExpandFilter extends DesugaringStep<LayerConfigJson> {
|
||||
private static load_filters(): Map<string, FilterConfigJson> {
|
||||
let filters = new Map<string, FilterConfigJson>()
|
||||
for (const filter of <FilterConfigJson[]>predifined_filters.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() {
|
||||
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[] } {
|
||||
if(json.filter === undefined || json.filter === null){
|
||||
return {result: json} // Nothing to change here
|
||||
convert(
|
||||
json: LayerConfigJson,
|
||||
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){
|
||||
return {result: json} // Nothing to change here
|
||||
if (json.filter["sameAs"] !== undefined) {
|
||||
return { result: json } // Nothing to change here
|
||||
}
|
||||
|
||||
const newFilters : FilterConfigJson[] = []
|
||||
const errors :string[]= []
|
||||
for (const filter of (<(FilterConfigJson|string)[]> json.filter)) {
|
||||
const newFilters: FilterConfigJson[] = []
|
||||
const errors: string[] = []
|
||||
for (const filter of <(FilterConfigJson | string)[]>json.filter) {
|
||||
if (typeof filter !== "string") {
|
||||
newFilters.push(filter)
|
||||
continue
|
||||
}
|
||||
// Search for the filter:
|
||||
const found = ExpandFilter.predefinedFilters.get(filter)
|
||||
if(found === undefined){
|
||||
const suggestions = Utils.sortedByLevenshteinDistance(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
|
||||
if (found === undefined) {
|
||||
const suggestions = Utils.sortedByLevenshteinDistance(
|
||||
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)
|
||||
}
|
||||
newFilters.push(found)
|
||||
}
|
||||
return {result: {
|
||||
...json, filter: newFilters
|
||||
}, errors};
|
||||
return {
|
||||
result: {
|
||||
...json,
|
||||
filter: newFilters,
|
||||
},
|
||||
errors,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
class ExpandTagRendering extends Conversion<
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
import {DesugaringStep, Each, Fuse, On} from "./Conversion"
|
||||
import {LayerConfigJson} from "../Json/LayerConfigJson"
|
||||
import { DesugaringStep, Each, Fuse, On } from "./Conversion"
|
||||
import { LayerConfigJson } from "../Json/LayerConfigJson"
|
||||
import LayerConfig from "../LayerConfig"
|
||||
import {Utils} from "../../../Utils"
|
||||
import { Utils } from "../../../Utils"
|
||||
import Constants from "../../Constants"
|
||||
import {Translation} from "../../../UI/i18n/Translation"
|
||||
import {LayoutConfigJson} from "../Json/LayoutConfigJson"
|
||||
import { Translation } from "../../../UI/i18n/Translation"
|
||||
import { LayoutConfigJson } from "../Json/LayoutConfigJson"
|
||||
import LayoutConfig from "../LayoutConfig"
|
||||
import {TagRenderingConfigJson} from "../Json/TagRenderingConfigJson"
|
||||
import {TagUtils} from "../../../Logic/Tags/TagUtils"
|
||||
import {ExtractImages} from "./FixImages"
|
||||
import { TagRenderingConfigJson } from "../Json/TagRenderingConfigJson"
|
||||
import { TagUtils } from "../../../Logic/Tags/TagUtils"
|
||||
import { ExtractImages } from "./FixImages"
|
||||
import ScriptUtils from "../../../scripts/ScriptUtils"
|
||||
import {And} from "../../../Logic/Tags/And"
|
||||
import { And } from "../../../Logic/Tags/And"
|
||||
import Translations from "../../../UI/i18n/Translations"
|
||||
import Svg from "../../../Svg"
|
||||
import {QuestionableTagRenderingConfigJson} from "../Json/QuestionableTagRenderingConfigJson"
|
||||
import FilterConfigJson from "../Json/FilterConfigJson";
|
||||
import DeleteConfig from "../DeleteConfig";
|
||||
import { QuestionableTagRenderingConfigJson } from "../Json/QuestionableTagRenderingConfigJson"
|
||||
import FilterConfigJson from "../Json/FilterConfigJson"
|
||||
import DeleteConfig from "../DeleteConfig"
|
||||
|
||||
class ValidateLanguageCompleteness extends DesugaringStep<any> {
|
||||
private readonly _languages: string[]
|
||||
|
@ -42,12 +42,12 @@ class ValidateLanguageCompleteness extends DesugaringStep<any> {
|
|||
.forEach((missing) => {
|
||||
errors.push(
|
||||
context +
|
||||
"A theme should be translation-complete for " +
|
||||
neededLanguage +
|
||||
", but it lacks a translation for " +
|
||||
missing.context +
|
||||
".\n\tThe known translation is " +
|
||||
missing.tr.textFor("en")
|
||||
"A theme should be translation-complete for " +
|
||||
neededLanguage +
|
||||
", but it lacks a translation for " +
|
||||
missing.context +
|
||||
".\n\tThe known translation is " +
|
||||
missing.tr.textFor("en")
|
||||
)
|
||||
})
|
||||
}
|
||||
|
@ -81,16 +81,16 @@ export class DoesImageExist extends DesugaringStep<string> {
|
|||
const information = []
|
||||
if (image.indexOf("{") >= 0) {
|
||||
information.push("Ignoring image with { in the path: " + image)
|
||||
return {result: image}
|
||||
return { result: image }
|
||||
}
|
||||
|
||||
if (image === "assets/SocialImage.png") {
|
||||
return {result: image}
|
||||
return { result: image }
|
||||
}
|
||||
if (image.match(/[a-z]*/)) {
|
||||
if (Svg.All[image + ".svg"] !== undefined) {
|
||||
// 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) {
|
||||
errors.push(
|
||||
"The theme " +
|
||||
json.id +
|
||||
" has units defined - these should be defined on the layer instead. (Hint: use overrideAll: { '+units': ... }) "
|
||||
json.id +
|
||||
" has units defined - these should be defined on the layer instead. (Hint: use overrideAll: { '+units': ... }) "
|
||||
)
|
||||
}
|
||||
if (json["roamingRenderings"] !== undefined) {
|
||||
errors.push(
|
||||
"Theme " +
|
||||
json.id +
|
||||
" contains an old 'roamingRenderings'. Use an 'overrideAll' instead"
|
||||
json.id +
|
||||
" contains an old 'roamingRenderings'. Use an 'overrideAll' instead"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -180,10 +180,10 @@ class ValidateTheme extends DesugaringStep<LayoutConfigJson> {
|
|||
for (const remoteImage of remoteImages) {
|
||||
errors.push(
|
||||
"Found a remote image: " +
|
||||
remoteImage +
|
||||
" in theme " +
|
||||
json.id +
|
||||
", please download it."
|
||||
remoteImage +
|
||||
" in theme " +
|
||||
json.id +
|
||||
", please download it."
|
||||
)
|
||||
}
|
||||
for (const image of images) {
|
||||
|
@ -212,10 +212,10 @@ class ValidateTheme extends DesugaringStep<LayoutConfigJson> {
|
|||
const h = parseInt(height)
|
||||
if (w < 370 || h < 370) {
|
||||
const e: string = [
|
||||
`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.`,
|
||||
` Width = ${width} height = ${height}; we recommend a size of at least 500px * 500px and to use a square aspect ratio.`,
|
||||
].join("\n")
|
||||
`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.`,
|
||||
` Width = ${width} height = ${height}; we recommend a size of at least 500px * 500px and to use a square aspect ratio.`,
|
||||
].join("\n")
|
||||
;(json.hideFromOverview ? warnings : errors).push(e)
|
||||
}
|
||||
})
|
||||
|
@ -227,7 +227,6 @@ class ValidateTheme extends DesugaringStep<LayoutConfigJson> {
|
|||
|
||||
try {
|
||||
if (this._isBuiltin) {
|
||||
|
||||
if (theme.id !== theme.id.toLowerCase()) {
|
||||
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) {
|
||||
errors.push(
|
||||
"Theme ids should be the same as the name.json, but we got id: " +
|
||||
theme.id +
|
||||
" and filename " +
|
||||
filename +
|
||||
" (" +
|
||||
this._path +
|
||||
")"
|
||||
theme.id +
|
||||
" and filename " +
|
||||
filename +
|
||||
" (" +
|
||||
this._path +
|
||||
")"
|
||||
)
|
||||
}
|
||||
this._validateImage.convertJoin(
|
||||
|
@ -323,7 +322,7 @@ class OverrideShadowingCheck extends DesugaringStep<LayoutConfigJson> {
|
|||
): { result: LayoutConfigJson; errors?: string[]; warnings?: string[] } {
|
||||
const overrideAll = json.overrideAll
|
||||
if (overrideAll === undefined) {
|
||||
return {result: json}
|
||||
return { result: json }
|
||||
}
|
||||
|
||||
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 warnings = []
|
||||
if (json.mappings === undefined || json.mappings.length === 0) {
|
||||
return {result: json}
|
||||
return { result: json }
|
||||
}
|
||||
const defaultProperties = {}
|
||||
for (const calculatedTagName of this._calculatedTagNames) {
|
||||
|
@ -489,7 +488,7 @@ export class DetectShadowedMappings extends DesugaringStep<QuestionableTagRender
|
|||
}
|
||||
const keyValues = parsedConditions[i].asChange(defaultProperties)
|
||||
const properties = {}
|
||||
keyValues.forEach(({k, v}) => {
|
||||
keyValues.forEach(({ k, v }) => {
|
||||
properties[k] = v
|
||||
})
|
||||
for (let j = 0; j < i; j++) {
|
||||
|
@ -506,10 +505,10 @@ export class DetectShadowedMappings extends DesugaringStep<QuestionableTagRender
|
|||
// The current mapping is shadowed!
|
||||
errors.push(`At ${context}: Mapping ${i} is shadowed by mapping ${j} and will thus never be shown:
|
||||
The mapping ${parsedConditions[i].asHumanString(
|
||||
false,
|
||||
false,
|
||||
{}
|
||||
)} is fully matched by a previous mapping (namely ${j}), which matches:
|
||||
false,
|
||||
false,
|
||||
{}
|
||||
)} is fully matched by a previous mapping (namely ${j}), which matches:
|
||||
${parsedConditions[j].asHumanString(false, false, {})}.
|
||||
|
||||
To fix this problem, you can try to:
|
||||
|
@ -578,7 +577,7 @@ export class DetectMappingsWithImages extends DesugaringStep<TagRenderingConfigJ
|
|||
const warnings: string[] = []
|
||||
const information: string[] = []
|
||||
if (json.mappings === undefined || json.mappings.length === 0) {
|
||||
return {result: json}
|
||||
return { result: json }
|
||||
}
|
||||
const ignoreToken = "ignore-image-in-then"
|
||||
for (let i = 0; i < json.mappings.length; i++) {
|
||||
|
@ -664,13 +663,13 @@ export class ValidateLayer extends DesugaringStep<LayerConfigJson> {
|
|||
if (json.title === undefined) {
|
||||
errors.push(
|
||||
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) {
|
||||
information.push(
|
||||
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) {
|
||||
errors.push(
|
||||
"At " +
|
||||
context +
|
||||
": some tagrenderings have a duplicate id: " +
|
||||
duplicates.join(", ")
|
||||
context +
|
||||
": some tagrenderings have a duplicate id: " +
|
||||
duplicates.join(", ")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if(json.deletion !== undefined && json.deletion instanceof DeleteConfig){
|
||||
if(json.deletion.softDeletionTags === undefined){
|
||||
warnings.push("No soft-deletion tags in deletion block for layer "+json.id)
|
||||
if (json.deletion !== undefined && json.deletion instanceof DeleteConfig) {
|
||||
if (json.deletion.softDeletionTags === undefined) {
|
||||
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) {
|
||||
errors.push(
|
||||
"Layer " +
|
||||
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)'
|
||||
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)'
|
||||
)
|
||||
}
|
||||
const forbiddenTopLevel = [
|
||||
|
@ -736,18 +735,18 @@ export class ValidateLayer extends DesugaringStep<LayerConfigJson> {
|
|||
if (json[forbiddenKey] !== undefined)
|
||||
errors.push(
|
||||
context +
|
||||
": layer " +
|
||||
json.id +
|
||||
" still has a forbidden key " +
|
||||
forbiddenKey
|
||||
": layer " +
|
||||
json.id +
|
||||
" still has a forbidden key " +
|
||||
forbiddenKey
|
||||
)
|
||||
}
|
||||
if (json["hideUnderlayingFeaturesMinPercentage"] !== undefined) {
|
||||
errors.push(
|
||||
context +
|
||||
": layer " +
|
||||
json.id +
|
||||
" contains an old 'hideUnderlayingFeaturesMinPercentage'"
|
||||
": layer " +
|
||||
json.id +
|
||||
" contains an old 'hideUnderlayingFeaturesMinPercentage'"
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -764,9 +763,9 @@ export class ValidateLayer extends DesugaringStep<LayerConfigJson> {
|
|||
if (this._path != undefined && this._path.indexOf(expected) < 0) {
|
||||
errors.push(
|
||||
"Layer is in an incorrect place. The path is " +
|
||||
this._path +
|
||||
", but expected " +
|
||||
expected
|
||||
this._path +
|
||||
", but expected " +
|
||||
expected
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -824,9 +823,9 @@ export class ValidateLayer extends DesugaringStep<LayerConfigJson> {
|
|||
if (hasCondition?.length > 0) {
|
||||
errors.push(
|
||||
"At " +
|
||||
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" +
|
||||
JSON.stringify(hasCondition, null, " ")
|
||||
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" +
|
||||
JSON.stringify(hasCondition, null, " ")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -838,7 +837,7 @@ export class ValidateLayer extends DesugaringStep<LayerConfigJson> {
|
|||
const preset = json.presets[i]
|
||||
const tags: { k: string; v: string }[] = new And(
|
||||
preset.tags.map((t) => TagUtils.Tag(t))
|
||||
).asChange({id: "node/-1"})
|
||||
).asChange({ id: "node/-1" })
|
||||
const properties = {}
|
||||
for (const tag of tags) {
|
||||
properties[tag.k] = tag.v
|
||||
|
@ -847,12 +846,12 @@ export class ValidateLayer extends DesugaringStep<LayerConfigJson> {
|
|||
if (!doMatch) {
|
||||
errors.push(
|
||||
context +
|
||||
".presets[" +
|
||||
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: " +
|
||||
JSON.stringify(properties) +
|
||||
"\n The required tags are: " +
|
||||
baseTags.asHumanString(false, false, {})
|
||||
".presets[" +
|
||||
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: " +
|
||||
JSON.stringify(properties) +
|
||||
"\n The required tags are: " +
|
||||
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() {
|
||||
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'
|
||||
*/
|
||||
private addLayerFilters(layer: LayerConfigJson, perOsmTag: Map<string, {
|
||||
private addLayerFilters(
|
||||
layer: LayerConfigJson,
|
||||
layout: LayoutConfigJson | undefined,
|
||||
filter: FilterConfigJson
|
||||
}[]>, layout?: LayoutConfigJson | undefined): void {
|
||||
perOsmTag: Map<
|
||||
string,
|
||||
{
|
||||
layer: LayerConfigJson
|
||||
layout: LayoutConfigJson | undefined
|
||||
filter: FilterConfigJson
|
||||
}[]
|
||||
>,
|
||||
layout?: LayoutConfigJson | undefined
|
||||
): void {
|
||||
if (layer.filter === undefined || layer.filter === null) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
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") {
|
||||
continue
|
||||
}
|
||||
|
||||
if(filter["#"]?.indexOf("ignore-possible-duplicate")>=0){
|
||||
if (filter["#"]?.indexOf("ignore-possible-duplicate") >= 0) {
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -908,57 +920,66 @@ export class DetectDuplicateFilters extends DesugaringStep<{ layers: LayerConfig
|
|||
perOsmTag.set(key, [])
|
||||
}
|
||||
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 warnings: string[] = []
|
||||
const information: string[] = []
|
||||
|
||||
const {layers, themes} = json
|
||||
const perOsmTag = new Map<string, {
|
||||
layer: LayerConfigJson,
|
||||
layout: LayoutConfigJson | undefined,
|
||||
filter: FilterConfigJson
|
||||
}[]>()
|
||||
const { layers, themes } = json
|
||||
const perOsmTag = new Map<
|
||||
string,
|
||||
{
|
||||
layer: LayerConfigJson
|
||||
layout: LayoutConfigJson | undefined
|
||||
filter: FilterConfigJson
|
||||
}[]
|
||||
>()
|
||||
|
||||
for (const layer of layers) {
|
||||
this.addLayerFilters(layer, perOsmTag)
|
||||
}
|
||||
|
||||
for (const theme of themes) {
|
||||
if(theme.id === "personal"){
|
||||
if (theme.id === "personal") {
|
||||
continue
|
||||
}
|
||||
for (const layer of theme.layers) {
|
||||
if(typeof layer === "string"){
|
||||
if (typeof layer === "string") {
|
||||
continue
|
||||
}
|
||||
if(layer["builtin"] !== undefined){
|
||||
if (layer["builtin"] !== undefined) {
|
||||
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
|
||||
perOsmTag.forEach((value, key) => {
|
||||
if(value.length <= 1){
|
||||
if (value.length <= 1) {
|
||||
// Seen this key just once, it is unique
|
||||
return;
|
||||
return
|
||||
}
|
||||
let msg = "Possible duplicate filter: "+ key
|
||||
for (const {filter, layer, layout} of value) {
|
||||
let msg = "Possible duplicate filter: " + key
|
||||
for (const { filter, layer, layout } of value) {
|
||||
let id = ""
|
||||
if(layout !== undefined){
|
||||
if (layout !== undefined) {
|
||||
id = layout.id + ":"
|
||||
}
|
||||
msg += `\n - ${id}${layer.id}.${filter.id}`
|
||||
|
@ -970,8 +991,7 @@ export class DetectDuplicateFilters extends DesugaringStep<{ layers: LayerConfig
|
|||
result: json,
|
||||
errors,
|
||||
warnings,
|
||||
information
|
||||
};
|
||||
information,
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
import {Translation} from "../../UI/i18n/Translation"
|
||||
import {TagsFilter} from "../../Logic/Tags/TagsFilter"
|
||||
import { Translation } from "../../UI/i18n/Translation"
|
||||
import { TagsFilter } from "../../Logic/Tags/TagsFilter"
|
||||
import FilterConfigJson from "./Json/FilterConfigJson"
|
||||
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 {TagConfigJson} from "./Json/TagConfigJson"
|
||||
import {UIEventSource} from "../../Logic/UIEventSource"
|
||||
import {FilterState} from "../FilteredLayer"
|
||||
import {QueryParameters} from "../../Logic/Web/QueryParameters"
|
||||
import {Utils} from "../../Utils"
|
||||
import {RegexTag} from "../../Logic/Tags/RegexTag"
|
||||
import { TagConfigJson } from "./Json/TagConfigJson"
|
||||
import { UIEventSource } from "../../Logic/UIEventSource"
|
||||
import { FilterState } from "../FilteredLayer"
|
||||
import { QueryParameters } from "../../Logic/Web/QueryParameters"
|
||||
import { Utils } from "../../Utils"
|
||||
import { RegexTag } from "../../Logic/Tags/RegexTag"
|
||||
|
||||
export default class FilterConfig {
|
||||
public readonly id: string
|
||||
|
|
|
@ -267,7 +267,10 @@ export default class TagRenderingConfig {
|
|||
if (this.freeform.key === "wikidata" && txt.indexOf("{wikipedia()") >= 0) {
|
||||
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
|
||||
}
|
||||
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
|
||||
*/
|
||||
export default class LinkToWeblate extends VariableUiElement {
|
||||
|
||||
constructor(context: string, availableTranslations: object) {
|
||||
super(
|
||||
Locale.language.map(
|
||||
|
|
|
@ -3,7 +3,7 @@ import Loc from "../../Models/Loc"
|
|||
import BaseLayer from "../../Models/BaseLayer"
|
||||
import { UIEventSource } from "../../Logic/UIEventSource"
|
||||
import { BBox } from "../../Logic/BBox"
|
||||
import {deprecate} from "util";
|
||||
import { deprecate } from "util"
|
||||
|
||||
export interface MinimapOptions {
|
||||
background?: UIEventSource<BaseLayer>
|
||||
|
@ -27,7 +27,7 @@ export interface MinimapObj {
|
|||
|
||||
TakeScreenshot(format): Promise<string>
|
||||
TakeScreenshot(format: "image"): Promise<string>
|
||||
TakeScreenshot(format:"blob"): Promise<Blob>
|
||||
TakeScreenshot(format: "blob"): Promise<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;
|
||||
* @constructor
|
||||
*/
|
||||
public async TakeScreenshot(): Promise<string> ;
|
||||
public async TakeScreenshot(format: "image"): Promise<string> ;
|
||||
public async TakeScreenshot(format: "blob"): Promise<Blob> ;
|
||||
public async TakeScreenshot(format: "image" | "blob"): Promise<string | Blob> ;
|
||||
public async TakeScreenshot(): Promise<string>
|
||||
public async TakeScreenshot(format: "image"): Promise<string>
|
||||
public async TakeScreenshot(format: "blob"): Promise<Blob>
|
||||
public async TakeScreenshot(format: "image" | "blob"): Promise<string | Blob>
|
||||
public async TakeScreenshot(format: "image" | "blob" = "image"): Promise<string | Blob> {
|
||||
console.log("Taking a screenshot...")
|
||||
const screenshotter = new SimpleMapScreenshoter()
|
||||
screenshotter.addTo(this.leafletMap.data)
|
||||
const result = <any> await screenshotter.takeScreen((<any> format) ?? "image")
|
||||
if(format === "image" && typeof result === "string"){
|
||||
const result = <any>await screenshotter.takeScreen(<any>format ?? "image")
|
||||
if (format === "image" && typeof result === "string") {
|
||||
return result
|
||||
}
|
||||
if(format === "blob" && result instanceof Blob){
|
||||
if (format === "blob" && result instanceof Blob) {
|
||||
return result
|
||||
}
|
||||
throw "Something went wrong while creating the screenshot: "+result
|
||||
throw "Something went wrong while creating the screenshot: " + result
|
||||
}
|
||||
|
||||
protected InnerConstructElement(): HTMLElement {
|
||||
|
|
|
@ -1,62 +1,73 @@
|
|||
import Combine from "../Base/Combine";
|
||||
import {FlowPanelFactory, FlowStep} from "../ImportFlow/FlowStep";
|
||||
import {ImmutableStore, Store, UIEventSource} from "../../Logic/UIEventSource";
|
||||
import {InputElement} from "../Input/InputElement";
|
||||
import {SvgToPdf, SvgToPdfOptions} from "../../Utils/svgToPdf";
|
||||
import {FixedInputElement} from "../Input/FixedInputElement";
|
||||
import {FixedUiElement} from "../Base/FixedUiElement";
|
||||
import FileSelectorButton from "../Input/FileSelectorButton";
|
||||
import InputElementMap from "../Input/InputElementMap";
|
||||
import {RadioButton} from "../Input/RadioButton";
|
||||
import {Utils} from "../../Utils";
|
||||
import {VariableUiElement} from "../Base/VariableUIElement";
|
||||
import Loading from "../Base/Loading";
|
||||
import BaseUIElement from "../BaseUIElement";
|
||||
import Img from "../Base/Img";
|
||||
import Title from "../Base/Title";
|
||||
import {CheckBox} from "../Input/Checkboxes";
|
||||
import Minimap from "../Base/Minimap";
|
||||
import SearchAndGo from "./SearchAndGo";
|
||||
import Toggle from "../Input/Toggle";
|
||||
import List from "../Base/List";
|
||||
import LeftIndex from "../Base/LeftIndex";
|
||||
import Constants from "../../Models/Constants";
|
||||
import Toggleable from "../Base/Toggleable";
|
||||
import Lazy from "../Base/Lazy";
|
||||
import LinkToWeblate from "../Base/LinkToWeblate";
|
||||
import Link from "../Base/Link";
|
||||
import {SearchablePillsSelector} from "../Input/SearchableMappingsSelector";
|
||||
import Combine from "../Base/Combine"
|
||||
import { FlowPanelFactory, FlowStep } from "../ImportFlow/FlowStep"
|
||||
import { ImmutableStore, Store, UIEventSource } from "../../Logic/UIEventSource"
|
||||
import { InputElement } from "../Input/InputElement"
|
||||
import { SvgToPdf, SvgToPdfOptions } from "../../Utils/svgToPdf"
|
||||
import { FixedInputElement } from "../Input/FixedInputElement"
|
||||
import { FixedUiElement } from "../Base/FixedUiElement"
|
||||
import FileSelectorButton from "../Input/FileSelectorButton"
|
||||
import InputElementMap from "../Input/InputElementMap"
|
||||
import { RadioButton } from "../Input/RadioButton"
|
||||
import { Utils } from "../../Utils"
|
||||
import { VariableUiElement } from "../Base/VariableUIElement"
|
||||
import Loading from "../Base/Loading"
|
||||
import BaseUIElement from "../BaseUIElement"
|
||||
import Img from "../Base/Img"
|
||||
import Title from "../Base/Title"
|
||||
import { CheckBox } from "../Input/Checkboxes"
|
||||
import Minimap from "../Base/Minimap"
|
||||
import SearchAndGo from "./SearchAndGo"
|
||||
import Toggle from "../Input/Toggle"
|
||||
import List from "../Base/List"
|
||||
import LeftIndex from "../Base/LeftIndex"
|
||||
import Constants from "../../Models/Constants"
|
||||
import Toggleable from "../Base/Toggleable"
|
||||
import Lazy from "../Base/Lazy"
|
||||
import LinkToWeblate from "../Base/LinkToWeblate"
|
||||
import Link from "../Base/Link"
|
||||
import { SearchablePillsSelector } from "../Input/SearchableMappingsSelector"
|
||||
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[] }> {
|
||||
readonly IsValid: Store<boolean>;
|
||||
readonly Value: Store<{ title: string, pages: string[] }>;
|
||||
class SelectTemplate extends Combine implements FlowStep<{ title: string; pages: string[] }> {
|
||||
readonly IsValid: Store<boolean>
|
||||
readonly Value: Store<{ title: string; pages: string[] }>
|
||||
|
||||
constructor() {
|
||||
const elements: InputElement<{ templateName: string, pages: string[] }>[] = []
|
||||
const elements: InputElement<{ templateName: string; pages: string[] }>[] = []
|
||||
for (const templateName in SvgToPdf.templates) {
|
||||
const template = SvgToPdf.templates[templateName]
|
||||
elements.push(new FixedInputElement(
|
||||
new Combine([new FixedUiElement(templateName).SetClass("font-bold pr-2"),
|
||||
template.description
|
||||
])
|
||||
, new UIEventSource({templateName, pages: template.pages})))
|
||||
elements.push(
|
||||
new FixedInputElement(
|
||||
new Combine([
|
||||
new FixedUiElement(templateName).SetClass("font-bold pr-2"),
|
||||
template.description,
|
||||
]),
|
||||
new UIEventSource({ templateName, pages: template.pages })
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
const file = new FileSelectorButton(new FixedUiElement("Select an svg image which acts as template"), {
|
||||
acceptType: "image/svg+xml",
|
||||
allowMultiple: true
|
||||
})
|
||||
const fileMapped = new InputElementMap<FileList, { templateName: string, pages: string[], fromFile: true }>(file, (x0, x1) => x0 === x1,
|
||||
const file = new FileSelectorButton(
|
||||
new FixedUiElement("Select an svg image which acts as template"),
|
||||
{
|
||||
acceptType: "image/svg+xml",
|
||||
allowMultiple: true,
|
||||
}
|
||||
)
|
||||
const fileMapped = new InputElementMap<
|
||||
FileList,
|
||||
{ templateName: string; pages: string[]; fromFile: true }
|
||||
>(
|
||||
file,
|
||||
(x0, x1) => x0 === x1,
|
||||
(filelist) => {
|
||||
if (filelist === undefined) {
|
||||
return undefined;
|
||||
return undefined
|
||||
}
|
||||
const pages = []
|
||||
let templateName: string = undefined;
|
||||
let templateName: string = undefined
|
||||
for (const file of Array.from(filelist)) {
|
||||
|
||||
if (templateName == undefined) {
|
||||
templateName = file.name.substring(file.name.lastIndexOf("/") + 1)
|
||||
templateName = templateName.substring(0, templateName.lastIndexOf("."))
|
||||
|
@ -67,40 +78,46 @@ class SelectTemplate extends Combine implements FlowStep<{ title: string, pages:
|
|||
return {
|
||||
templateName,
|
||||
pages,
|
||||
fromFile: true
|
||||
fromFile: true,
|
||||
}
|
||||
|
||||
},
|
||||
_ => undefined
|
||||
(_) => undefined
|
||||
)
|
||||
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 => {
|
||||
if (template === undefined) {
|
||||
return undefined
|
||||
}
|
||||
if (template["fromFile"]) {
|
||||
return UIEventSource.FromPromiseWithErr(Promise.all(template.pages).then(pages => ({
|
||||
const loaded: Store<{ success: { title: string; pages: string[] } } | { error: any }> =
|
||||
radio.GetValue().bind((template) => {
|
||||
if (template === undefined) {
|
||||
return undefined
|
||||
}
|
||||
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,
|
||||
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(
|
||||
loaded.map(pages => {
|
||||
loaded.map((pages) => {
|
||||
if (pages === undefined) {
|
||||
return new Loading()
|
||||
}
|
||||
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
|
||||
if (svgs.length === 0) {
|
||||
|
@ -108,22 +125,16 @@ class SelectTemplate extends Combine implements FlowStep<{ title: string, pages:
|
|||
}
|
||||
const els: BaseUIElement[] = []
|
||||
for (const pageSrc of svgs) {
|
||||
const el = new Img(pageSrc, true)
|
||||
.SetClass("w-96 m-2 border-black border-2")
|
||||
const el = new Img(pageSrc, true).SetClass("w-96 m-2 border-black border-2")
|
||||
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([
|
||||
new Title("Select template"),
|
||||
radio,
|
||||
new Title("Preview"),
|
||||
preview
|
||||
]);
|
||||
this.Value = loaded.map(l => l === undefined ? undefined : l["success"])
|
||||
this.IsValid = this.Value.map(v => v !== undefined)
|
||||
super([new Title("Select template"), radio, 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) {
|
||||
|
@ -134,63 +145,78 @@ class SelectTemplate extends Combine implements FlowStep<{ title: string, pages:
|
|||
path = path.substring(0, path.lastIndexOf("/"))
|
||||
return window.location.protocol + "//" + window.location.host + path + "/" + spec
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class SelectPdfOptions extends Combine implements FlowStep<{ title: string, pages: string[], options: SvgToPdfOptions }> {
|
||||
readonly IsValid: Store<boolean>;
|
||||
readonly Value: Store<{ title: string, pages: string[], options: SvgToPdfOptions }>;
|
||||
class SelectPdfOptions
|
||||
extends Combine
|
||||
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) {
|
||||
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 searchField = new SearchAndGo({leafletMap: locationInput.leafletMap})
|
||||
const selectLocation =
|
||||
new Combine([
|
||||
new Toggle(new Combine([new Title("Select override location"), searchField]).SetClass("flex"), undefined, overrideMapLocation.GetValue()),
|
||||
new Toggle(locationInput.SetStyle("height: 20rem"), undefined, overrideMapLocation.GetValue()).SetStyle("height: 20rem")
|
||||
]).SetClass("block").SetStyle("height: 25rem")
|
||||
super([new Title("Select options"),
|
||||
dummy,
|
||||
overrideMapLocation,
|
||||
selectLocation
|
||||
]);
|
||||
this.Value = dummy.GetValue().map((disableMaps) => {
|
||||
return {
|
||||
pages,
|
||||
title,
|
||||
options: <SvgToPdfOptions>{
|
||||
disableMaps,
|
||||
getFreeDiv,
|
||||
overrideLocation: overrideMapLocation.GetValue().data ? locationInput.location.data : undefined
|
||||
const searchField = new SearchAndGo({ leafletMap: locationInput.leafletMap })
|
||||
const selectLocation = new Combine([
|
||||
new Toggle(
|
||||
new Combine([new Title("Select override location"), searchField]).SetClass("flex"),
|
||||
undefined,
|
||||
overrideMapLocation.GetValue()
|
||||
),
|
||||
new Toggle(
|
||||
locationInput.SetStyle("height: 20rem"),
|
||||
undefined,
|
||||
overrideMapLocation.GetValue()
|
||||
).SetStyle("height: 20rem"),
|
||||
])
|
||||
.SetClass("block")
|
||||
.SetStyle("height: 25rem")
|
||||
super([new Title("Select options"), dummy, overrideMapLocation, selectLocation])
|
||||
this.Value = dummy.GetValue().map(
|
||||
(disableMaps) => {
|
||||
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)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class PreparePdf extends Combine implements FlowStep<{ svgToPdf: SvgToPdf, languages: string[] }> {
|
||||
readonly IsValid: Store<boolean>;
|
||||
readonly Value: Store<{ svgToPdf: SvgToPdf, languages: string[] }>;
|
||||
class PreparePdf extends Combine implements FlowStep<{ svgToPdf: SvgToPdf; languages: string[] }> {
|
||||
readonly IsValid: Store<boolean>
|
||||
readonly Value: Store<{ svgToPdf: SvgToPdf; languages: string[] }>
|
||||
|
||||
constructor(title: string, pages: string[], options: SvgToPdfOptions) {
|
||||
const svgToPdf = new SvgToPdf(title, pages, options)
|
||||
const languageOptions = [
|
||||
new FixedInputElement("Nederlands", "nl"),
|
||||
new FixedInputElement("English", "en")
|
||||
new FixedInputElement("English", "en"),
|
||||
]
|
||||
const langs: string[] = Array.from(Object.keys(languages["default"] ?? languages))
|
||||
console.log("Available languages are:", langs)
|
||||
const languageSelector = new SearchablePillsSelector(
|
||||
langs.map(l => ({
|
||||
langs.map((l) => ({
|
||||
show: new Translation(languages[l]),
|
||||
value: l,
|
||||
mainTerm: languages[l]
|
||||
})), {
|
||||
mode: "select-many"
|
||||
mainTerm: languages[l],
|
||||
})),
|
||||
{
|
||||
mode: "select-many",
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -202,45 +228,54 @@ class PreparePdf extends Combine implements FlowStep<{ svgToPdf: SvgToPdf, langu
|
|||
new Toggle(
|
||||
new Loading("Preparing maps..."),
|
||||
undefined,
|
||||
isPrepared.map(p => p === undefined)
|
||||
)
|
||||
]);
|
||||
this.Value = isPrepared.map(isPrepped => {
|
||||
if (isPrepped === undefined) {
|
||||
return undefined
|
||||
}
|
||||
if (isPrepped["success"] !== undefined) {
|
||||
const svgToPdf = isPrepped["success"]
|
||||
const langs = languageSelector.GetValue().data
|
||||
console.log("Languages are", langs)
|
||||
if (langs.length === 0) {
|
||||
isPrepared.map((p) => p === undefined)
|
||||
),
|
||||
])
|
||||
this.Value = isPrepared.map(
|
||||
(isPrepped) => {
|
||||
if (isPrepped === undefined) {
|
||||
return undefined
|
||||
}
|
||||
return {svgToPdf, languages: langs}
|
||||
}
|
||||
return undefined;
|
||||
}, [languageSelector.GetValue()])
|
||||
this.IsValid = this.Value.map(v => v !== 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 { 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[] }> {
|
||||
readonly IsValid: Store<boolean>;
|
||||
readonly Value: Store<{ svgToPdf: SvgToPdf; languages: string[] }>;
|
||||
class InspectStrings
|
||||
extends Toggle
|
||||
implements FlowStep<{ svgToPdf: SvgToPdf; languages: string[] }>
|
||||
{
|
||||
readonly IsValid: Store<boolean>
|
||||
readonly Value: Store<{ 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"),
|
||||
...languages.map(l => new Lazy(() => InspectStrings.createOverviewPanel(svgToPdf, l)))
|
||||
...languages.map(
|
||||
(l) => new Lazy(() => InspectStrings.createOverviewPanel(svgToPdf, l))
|
||||
),
|
||||
]),
|
||||
new Loading(),
|
||||
didLoadLanguages
|
||||
);
|
||||
this.Value = new ImmutableStore({svgToPdf, languages})
|
||||
)
|
||||
this.Value = new ImmutableStore({ svgToPdf, languages })
|
||||
this.IsValid = didLoadLanguages
|
||||
}
|
||||
|
||||
|
@ -259,64 +294,82 @@ class InspectStrings extends Toggle implements FlowStep<{ svgToPdf: SvgToPdf, la
|
|||
if (translated) {
|
||||
foundTranslations++
|
||||
}
|
||||
const linkToWeblate = new Link(spec, LinkToWeblate.hrefToWeblate(language, spec), true).SetClass("font-bold link-underline")
|
||||
elements.push(new Combine([
|
||||
linkToWeblate,
|
||||
" ",
|
||||
translated ?? new FixedUiElement("No translation found!").SetClass("alert")
|
||||
|
||||
]))
|
||||
const linkToWeblate = new Link(
|
||||
spec,
|
||||
LinkToWeblate.hrefToWeblate(language, spec),
|
||||
true
|
||||
).SetClass("font-bold link-underline")
|
||||
elements.push(
|
||||
new Combine([
|
||||
linkToWeblate,
|
||||
" ",
|
||||
translated ?? new FixedUiElement("No translation found!").SetClass("alert"),
|
||||
])
|
||||
)
|
||||
}
|
||||
|
||||
return new Toggleable(
|
||||
new Title("Translations for " + language),
|
||||
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:",
|
||||
new List(elements)
|
||||
new List(elements),
|
||||
]),
|
||||
{closeOnClick: false, height: "15rem"})
|
||||
{ closeOnClick: false, height: "15rem" }
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class SavePdf extends Combine {
|
||||
|
||||
constructor(svgToPdf: SvgToPdf, languages: string[]) {
|
||||
|
||||
super([
|
||||
new Title("Generating your pdfs..."),
|
||||
new List(languages.map(lng => new Toggle(
|
||||
lng + " is done!",
|
||||
new Loading("Creating pdf for " + lng),
|
||||
UIEventSource.FromPromiseWithErr(svgToPdf.ConvertSvg(lng).then(() => true))
|
||||
.map(x => x !== undefined && x["success"] === true)
|
||||
)))
|
||||
]);
|
||||
new List(
|
||||
languages.map(
|
||||
(lng) =>
|
||||
new Toggle(
|
||||
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 {
|
||||
|
||||
|
||||
constructor(freeDivId: string) {
|
||||
|
||||
let i = 0
|
||||
const createDiv = (): string => {
|
||||
const div = document.createElement("div")
|
||||
div.id = "freediv-" + (i++)
|
||||
div.id = "freediv-" + i++
|
||||
document.getElementById(freeDivId).append(div)
|
||||
return div.id
|
||||
}
|
||||
|
||||
Constants.defaultOverpassUrls.splice(0, 1)
|
||||
const {flow, furthestStep, titles} = FlowPanelFactory.start(
|
||||
new Title("Select template"), new SelectTemplate()
|
||||
).then(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 { flow, furthestStep, titles } = FlowPanelFactory.start(
|
||||
new Title("Select template"),
|
||||
new SelectTemplate()
|
||||
)
|
||||
.then(
|
||||
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(
|
||||
titles.map(
|
||||
|
@ -338,9 +391,7 @@ export class PdfExportGui extends LeftIndex {
|
|||
true
|
||||
)
|
||||
|
||||
const leftContents: BaseUIElement[] = [
|
||||
toc
|
||||
].map((el) => el?.SetClass("pl-4"))
|
||||
const leftContents: BaseUIElement[] = [toc].map((el) => el?.SetClass("pl-4"))
|
||||
|
||||
super(leftContents, flow)
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ import BaseLayer from "../../Models/BaseLayer"
|
|||
import Loading from "../Base/Loading"
|
||||
import Hash from "../../Logic/Web/Hash"
|
||||
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:
|
||||
|
|
|
@ -3,10 +3,10 @@ import { UIEventSource } from "../../Logic/UIEventSource"
|
|||
import { Utils } from "../../Utils"
|
||||
import BaseUIElement from "../BaseUIElement"
|
||||
import InputElementMap from "./InputElementMap"
|
||||
import Translations from "../i18n/Translations";
|
||||
import Translations from "../i18n/Translations"
|
||||
|
||||
export class CheckBox extends InputElementMap<number[], boolean> {
|
||||
constructor(el: (BaseUIElement | string), defaultValue?: boolean) {
|
||||
constructor(el: BaseUIElement | string, defaultValue?: boolean) {
|
||||
super(
|
||||
new CheckBoxes([Translations.W(el)]),
|
||||
(x0, x1) => x0 === x1,
|
||||
|
|
|
@ -1,38 +1,40 @@
|
|||
import {ReadonlyInputElement} from "./InputElement"
|
||||
import { ReadonlyInputElement } from "./InputElement"
|
||||
import Loc from "../../Models/Loc"
|
||||
import {Store, UIEventSource} from "../../Logic/UIEventSource"
|
||||
import Minimap, {MinimapObj} from "../Base/Minimap"
|
||||
import { Store, UIEventSource } from "../../Logic/UIEventSource"
|
||||
import Minimap, { MinimapObj } from "../Base/Minimap"
|
||||
import BaseLayer from "../../Models/BaseLayer"
|
||||
import Combine from "../Base/Combine"
|
||||
import Svg from "../../Svg"
|
||||
import {GeoOperations} from "../../Logic/GeoOperations"
|
||||
import { GeoOperations } from "../../Logic/GeoOperations"
|
||||
import ShowDataMultiLayer from "../ShowDataLayer/ShowDataMultiLayer"
|
||||
import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource"
|
||||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
||||
import {BBox} from "../../Logic/BBox"
|
||||
import {FixedUiElement} from "../Base/FixedUiElement"
|
||||
import { BBox } from "../../Logic/BBox"
|
||||
import { FixedUiElement } from "../Base/FixedUiElement"
|
||||
import ShowDataLayer from "../ShowDataLayer/ShowDataLayer"
|
||||
import BaseUIElement from "../BaseUIElement"
|
||||
import Toggle from "./Toggle"
|
||||
import * as matchpoint from "../../assets/layers/matchpoint/matchpoint.json"
|
||||
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig";
|
||||
import FilteredLayer from "../../Models/FilteredLayer";
|
||||
import {ElementStorage} from "../../Logic/ElementStorage";
|
||||
import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers";
|
||||
import {RelationId, WayId} from "../../Models/OsmFeature";
|
||||
import {Feature, LineString, Polygon} from "geojson";
|
||||
import {OsmObject, OsmWay} from "../../Logic/Osm/OsmObject";
|
||||
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"
|
||||
import FilteredLayer from "../../Models/FilteredLayer"
|
||||
import { ElementStorage } from "../../Logic/ElementStorage"
|
||||
import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers"
|
||||
import { RelationId, WayId } from "../../Models/OsmFeature"
|
||||
import { Feature, LineString, Polygon } from "geojson"
|
||||
import { OsmObject, OsmWay } from "../../Logic/Osm/OsmObject"
|
||||
|
||||
export default class LocationInput
|
||||
extends BaseUIElement
|
||||
implements ReadonlyInputElement<Loc>, MinimapObj {
|
||||
implements ReadonlyInputElement<Loc>, MinimapObj
|
||||
{
|
||||
private static readonly matchLayer = new LayerConfig(
|
||||
matchpoint,
|
||||
"LocationInput.matchpoint",
|
||||
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 leafletMap: UIEventSource<any>
|
||||
public readonly bounds
|
||||
|
@ -43,13 +45,15 @@ export default class LocationInput
|
|||
* The features to which the input should be snapped
|
||||
* @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
|
||||
* Used for rendering
|
||||
* @private
|
||||
*/
|
||||
private readonly _snapToRaw: Store< {feature: Feature}[]>
|
||||
private readonly _snapToRaw: Store<{ feature: Feature }[]>
|
||||
private readonly _value: Store<Loc>
|
||||
private readonly _snappedPoint: Store<any>
|
||||
private readonly _maxSnapDistance: number
|
||||
|
@ -59,10 +63,10 @@ export default class LocationInput
|
|||
private readonly clickLocation: UIEventSource<Loc>
|
||||
private readonly _minZoom: number
|
||||
private readonly _state: {
|
||||
readonly filteredLayers: Store<FilteredLayer[]>;
|
||||
readonly backgroundLayer: UIEventSource<BaseLayer>;
|
||||
readonly layoutToUse: LayoutConfig;
|
||||
readonly selectedElement: UIEventSource<any>;
|
||||
readonly filteredLayers: Store<FilteredLayer[]>
|
||||
readonly backgroundLayer: UIEventSource<BaseLayer>
|
||||
readonly layoutToUse: LayoutConfig
|
||||
readonly selectedElement: UIEventSource<any>
|
||||
readonly allElements: ElementStorage
|
||||
}
|
||||
|
||||
|
@ -74,27 +78,35 @@ export default class LocationInput
|
|||
*
|
||||
* @private
|
||||
*/
|
||||
private static async prepareSnapOnto(features: Feature[]): Promise<(Feature<LineString | Polygon> & {properties : {id: WayId}})[]> {
|
||||
const linesAndPolygon : Feature<LineString | Polygon>[] = <any> features.filter(f => f.geometry.type !== "Point")
|
||||
private static async prepareSnapOnto(
|
||||
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
|
||||
const linestrings : (Feature<LineString | Polygon> & {properties: {id: WayId}})[] = []
|
||||
const linestrings: (Feature<LineString | Polygon> & { properties: { id: WayId } })[] = []
|
||||
for (const feature of linesAndPolygon) {
|
||||
if(feature.properties.id.startsWith("way")){
|
||||
if (feature.properties.id.startsWith("way")) {
|
||||
// A normal way - we continue
|
||||
linestrings.push(<any> feature)
|
||||
linestrings.push(<any>feature)
|
||||
continue
|
||||
}
|
||||
|
||||
// We have a multipolygon, thus: a relation
|
||||
// Download the members
|
||||
const relation = await OsmObject.DownloadObjectAsync(<RelationId> feature.properties.id, 60 * 60)
|
||||
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()))
|
||||
const relation = await OsmObject.DownloadObjectAsync(
|
||||
<RelationId>feature.properties.id,
|
||||
60 * 60
|
||||
)
|
||||
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
|
||||
|
||||
}
|
||||
|
||||
constructor(options?: {
|
||||
|
@ -107,20 +119,32 @@ export default class LocationInput
|
|||
centerLocation?: UIEventSource<Loc>
|
||||
bounds?: UIEventSource<BBox>
|
||||
state?: {
|
||||
readonly filteredLayers: Store<FilteredLayer[]>;
|
||||
readonly backgroundLayer: UIEventSource<BaseLayer>;
|
||||
readonly layoutToUse: LayoutConfig;
|
||||
readonly selectedElement: UIEventSource<any>;
|
||||
readonly filteredLayers: Store<FilteredLayer[]>
|
||||
readonly backgroundLayer: UIEventSource<BaseLayer>
|
||||
readonly layoutToUse: LayoutConfig
|
||||
readonly selectedElement: UIEventSource<any>
|
||||
readonly allElements: ElementStorage
|
||||
}
|
||||
}) {
|
||||
super()
|
||||
this._snapToRaw = options?.snapTo?.map(feats => 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._snapToRaw = options?.snapTo?.map((feats) =>
|
||||
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._centerLocation = options?.centerLocation ?? new UIEventSource<Loc>({
|
||||
lat: 0, lon: 0, zoom: 0
|
||||
})
|
||||
this._centerLocation =
|
||||
options?.centerLocation ??
|
||||
new UIEventSource<Loc>({
|
||||
lat: 0,
|
||||
lon: 0,
|
||||
zoom: 0,
|
||||
})
|
||||
this._snappedPointTags = options?.snappedPointTags
|
||||
this._bounds = options?.bounds
|
||||
this._minZoom = options?.minZoom
|
||||
|
@ -152,11 +176,11 @@ export default class LocationInput
|
|||
return undefined
|
||||
}
|
||||
|
||||
|
||||
// We reproject the location onto every 'snap-to-feature' and select the closest
|
||||
|
||||
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 ?? []) {
|
||||
try {
|
||||
const nearestPointOnLine = GeoOperations.nearestPoint(feature, [
|
||||
|
@ -191,18 +215,16 @@ export default class LocationInput
|
|||
return {
|
||||
type: "Feature",
|
||||
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
|
||||
if(matchedWay.properties.id.startsWith("relation/")){
|
||||
if (matchedWay.properties.id.startsWith("relation/")) {
|
||||
// We matched a relation instead of a way
|
||||
console.log("Snapping onto a relation. The relation is", matchedWay)
|
||||
|
||||
|
||||
}
|
||||
self.snappedOnto.setData(<any> matchedWay)
|
||||
self.snappedOnto.setData(<any>matchedWay)
|
||||
return min
|
||||
},
|
||||
[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.clickLocation = new UIEventSource<Loc>(undefined)
|
||||
|
@ -249,7 +274,7 @@ export default class LocationInput
|
|||
try {
|
||||
const self = this
|
||||
const hasMoved = new UIEventSource(false)
|
||||
const startLocation = {...this._centerLocation.data}
|
||||
const startLocation = { ...this._centerLocation.data }
|
||||
this._centerLocation.addCallbackD((newLocation) => {
|
||||
const f = 100000
|
||||
console.log(newLocation.lon, startLocation.lon)
|
||||
|
@ -279,7 +304,7 @@ export default class LocationInput
|
|||
if (loc === undefined) {
|
||||
return []
|
||||
}
|
||||
return [{feature: loc}]
|
||||
return [{ feature: loc }]
|
||||
})
|
||||
console.log("Constructing the match layer", matchPoint)
|
||||
|
||||
|
@ -335,9 +360,9 @@ export default class LocationInput
|
|||
}
|
||||
}
|
||||
|
||||
TakeScreenshot(format: "image"): Promise<string>;
|
||||
TakeScreenshot(format: "blob"): Promise<Blob>;
|
||||
TakeScreenshot(format: "image" | "blob"): Promise<string | Blob>;
|
||||
TakeScreenshot(format: "image"): Promise<string>
|
||||
TakeScreenshot(format: "blob"): Promise<Blob>
|
||||
TakeScreenshot(format: "image" | "blob"): Promise<string | Blob>
|
||||
TakeScreenshot(format: "image" | "blob"): Promise<string | Blob> {
|
||||
return this.map.TakeScreenshot(format)
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ import Title from "../Base/Title"
|
|||
import { GlobalFilter } from "../../Logic/State/MapState"
|
||||
import { VariableUiElement } from "../Base/VariableUIElement"
|
||||
import { Tag } from "../../Logic/Tags/Tag"
|
||||
import {WayId} from "../../Models/OsmFeature";
|
||||
import { WayId } from "../../Models/OsmFeature"
|
||||
|
||||
export default class ConfirmLocationOfPoint extends Combine {
|
||||
constructor(
|
||||
|
@ -76,7 +76,7 @@ export default class ConfirmLocationOfPoint extends Combine {
|
|||
snappedPointTags: tags,
|
||||
maxSnapDistance: preset.preciseInput.maxSnapDistance,
|
||||
bounds: mapBounds,
|
||||
state: <any> state
|
||||
state: <any>state,
|
||||
})
|
||||
preciseInput.installBounds(preset.boundsFactor ?? 0.25, true)
|
||||
preciseInput
|
||||
|
|
|
@ -22,7 +22,7 @@ import Title from "../Base/Title"
|
|||
import { SubstitutedTranslation } from "../SubstitutedTranslation"
|
||||
import FeaturePipelineState from "../../Logic/State/FeaturePipelineState"
|
||||
import TagRenderingQuestion from "./TagRenderingQuestion"
|
||||
import {OsmId} from "../../Models/OsmFeature";
|
||||
import { OsmId } from "../../Models/OsmFeature"
|
||||
|
||||
export default class DeleteWizard extends Toggle {
|
||||
/**
|
||||
|
|
|
@ -248,29 +248,27 @@ export default class FeatureInfoBox extends ScrollableFullScreen {
|
|||
)
|
||||
|
||||
editElements.push(
|
||||
Toggle.If(state.featureSwitchIsDebugging,
|
||||
() => {
|
||||
const config_all_tags: TagRenderingConfig = new TagRenderingConfig(
|
||||
{ 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,
|
||||
])
|
||||
}
|
||||
Toggle.If(state.featureSwitchIsDebugging, () => {
|
||||
const config_all_tags: TagRenderingConfig = new TagRenderingConfig(
|
||||
{ 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,
|
||||
])
|
||||
})
|
||||
)
|
||||
|
||||
return new Combine(editElements).SetClass("flex flex-col")
|
||||
|
|
|
@ -145,7 +145,7 @@ export default class MoveWizard extends Toggle {
|
|||
minZoom: reason.minZoom,
|
||||
centerLocation: loc,
|
||||
mapBackground: new UIEventSource<BaseLayer>(preferredBackground), // We detach the layer
|
||||
state: <any> state
|
||||
state: <any>state,
|
||||
})
|
||||
|
||||
if (reason.lockBounds) {
|
||||
|
|
|
@ -51,18 +51,18 @@ export default class TagApplyButton implements AutoAction {
|
|||
* // Should handle escaped ";"
|
||||
* TagApplyButton.parseTagSpec("key=value;key0=value0\\;value1") // => [["key","value"],["key0","value0;value1"]]
|
||||
*/
|
||||
private static parseTagSpec(spec: string): [string, string][]{
|
||||
const tgsSpec : [string, string][] = []
|
||||
private static parseTagSpec(spec: string): [string, string][] {
|
||||
const tgsSpec: [string, string][] = []
|
||||
|
||||
while(spec.length > 0){
|
||||
while (spec.length > 0) {
|
||||
const [part] = spec.match(/((\\;)|[^;])*/)
|
||||
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) {
|
||||
tgsSpec.push(<[string, string]> kv)
|
||||
}else if (kv.length < 2) {
|
||||
tgsSpec.push(<[string, string]>kv)
|
||||
} else if (kv.length < 2) {
|
||||
throw "Invalid key spec: no '=' found in " + spec
|
||||
}else{
|
||||
} else {
|
||||
throw "Invalid key spec: multiple '=' found in " + spec
|
||||
}
|
||||
}
|
||||
|
@ -83,7 +83,7 @@ export default class TagApplyButton implements AutoAction {
|
|||
spec = tagSource.data[spec.replace("$", "")]
|
||||
}
|
||||
|
||||
const tgsSpec = TagApplyButton.parseTagSpec(spec)
|
||||
const tgsSpec = TagApplyButton.parseTagSpec(spec)
|
||||
|
||||
return tagSource.map((tags) => {
|
||||
const newTags: Tag[] = []
|
||||
|
|
|
@ -304,7 +304,7 @@ export default class TagRenderingQuestion extends Combine {
|
|||
const patchedMapping = <Mapping>{
|
||||
...mapping,
|
||||
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(
|
||||
patchedMapping,
|
||||
|
|
|
@ -10,12 +10,15 @@ import * as clusterstyle from "../../assets/layers/cluster_style/cluster_style.j
|
|||
export default class ShowTileInfo {
|
||||
public static readonly styling = new LayerConfig(clusterstyle, "ShowTileInfo", true)
|
||||
|
||||
constructor(options: {
|
||||
source: FeatureSource & Tiled
|
||||
leafletMap: UIEventSource<any>
|
||||
layer?: LayerConfig
|
||||
doShowLayer?: UIEventSource<boolean>
|
||||
}, state) {
|
||||
constructor(
|
||||
options: {
|
||||
source: FeatureSource & Tiled
|
||||
leafletMap: UIEventSource<any>
|
||||
layer?: LayerConfig
|
||||
doShowLayer?: UIEventSource<boolean>
|
||||
},
|
||||
state
|
||||
) {
|
||||
const source = options.source
|
||||
const metaFeature: Store<{ feature; freshness: Date }[]> = source.features.map(
|
||||
(features) => {
|
||||
|
@ -55,7 +58,7 @@ export default class ShowTileInfo {
|
|||
features: new StaticFeatureSource(metaFeature),
|
||||
leafletMap: options.leafletMap,
|
||||
doShowLayer: options.doShowLayer,
|
||||
state
|
||||
state,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,7 +41,10 @@ export default class Translations {
|
|||
* 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) {
|
||||
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) {
|
||||
reject("rate limited")
|
||||
} else {
|
||||
reject("Could not download "+url+" due to "+xhr.statusText)
|
||||
reject("Could not download " + url + " due to " + xhr.statusText)
|
||||
}
|
||||
}
|
||||
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 AllThemesGui from "./UI/AllThemesGui"
|
||||
import { QueryParameters } from "./Logic/Web/QueryParameters"
|
||||
import StatisticsGUI from "./UI/StatisticsGUI"
|
||||
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 customLayout = QueryParameters.GetQueryParameter("userlayout", undefined).data ?? ""
|
||||
|
@ -45,7 +45,7 @@ if (mode.data === "statistics") {
|
|||
console.log("Statistics mode!")
|
||||
new FixedUiElement("").AttachTo("centermessage")
|
||||
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()
|
||||
new FixedUiElement("").AttachTo("centermessage")
|
||||
const div = document.createElement("div")
|
||||
|
|
|
@ -108,10 +108,11 @@ function main() {
|
|||
{
|
||||
id: "tactile_writing-braille",
|
||||
// @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,
|
||||
mappings: brailemappings,
|
||||
}
|
||||
},
|
||||
],
|
||||
}
|
||||
const dir = "./assets/layers/wikidata/"
|
||||
|
|
Loading…
Reference in a new issue