forked from MapComplete/MapComplete
Refactoring: split 'Utils' into multiple files; fix some stray uppercase-method names
This commit is contained in:
parent
81be4db044
commit
3ec89826e4
97 changed files with 884 additions and 921 deletions
|
|
@ -3,6 +3,7 @@ import Locale from "../../UI/i18n/Locale"
|
|||
import { Utils } from "../../Utils"
|
||||
import { Feature } from "geojson"
|
||||
import { SpecialVisualizationState } from "../../UI/SpecialVisualization"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
|
||||
export default class TitleHandler {
|
||||
constructor(selectedElement: Store<Feature>, state: SpecialVisualizationState) {
|
||||
|
|
@ -22,7 +23,7 @@ export default class TitleHandler {
|
|||
if (layer.title === undefined) {
|
||||
return defaultTitle
|
||||
}
|
||||
const toRender = Utils.NoNull(layer?.title?.GetRenderValues(tags))
|
||||
const toRender = Lists.noNull(layer?.title?.GetRenderValues(tags))
|
||||
const titleUnsubbed = toRender[0]?.then?.textFor(lng)
|
||||
if (titleUnsubbed === undefined) {
|
||||
return defaultTitle
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import { ThemeConfigJson } from "../Models/ThemeConfig/Json/ThemeConfigJson"
|
|||
import { ValidateThemeAndLayers } from "../Models/ThemeConfig/Conversion/ValidateThemeAndLayers"
|
||||
import * as theme_overview from "../assets/generated/theme_overview.json"
|
||||
import * as favourite_layer from "../../assets/layers/favourite/favourite.json"
|
||||
import { Lists } from "../Utils/Lists"
|
||||
export default class DetermineTheme {
|
||||
private static readonly _knownImages = new Set(Array.from(licenses).map((l) => l.path))
|
||||
private static readonly loadCustomThemeParam = QueryParameters.GetQueryParameter(
|
||||
|
|
@ -143,27 +144,25 @@ export default class DetermineTheme {
|
|||
if (json.layers === undefined && json.tagRenderings !== undefined) {
|
||||
// We got fed a layer instead of a theme
|
||||
const layerConfig = <LayerConfigJson>json
|
||||
let icon = Utils.NoNull(
|
||||
layerConfig.pointRendering
|
||||
.flatMap((pr) => pr.marker)
|
||||
.map((iconSpec) => {
|
||||
if (!iconSpec) {
|
||||
return undefined
|
||||
}
|
||||
const icon = new TagRenderingConfig(<TagRenderingConfigJson>iconSpec.icon)
|
||||
.render.txt
|
||||
if (
|
||||
iconSpec.color === undefined ||
|
||||
icon.startsWith("http:") ||
|
||||
icon.startsWith("https:")
|
||||
) {
|
||||
return icon
|
||||
}
|
||||
const color = new TagRenderingConfig(<TagRenderingConfigJson>iconSpec.color)
|
||||
.render.txt
|
||||
return icon + ":" + color
|
||||
})
|
||||
).join(";")
|
||||
let icon = Lists.noNull(layerConfig.pointRendering
|
||||
.flatMap((pr) => pr.marker)
|
||||
.map((iconSpec) => {
|
||||
if (!iconSpec) {
|
||||
return undefined
|
||||
}
|
||||
const icon = new TagRenderingConfig(<TagRenderingConfigJson>iconSpec.icon)
|
||||
.render.txt
|
||||
if (
|
||||
iconSpec.color === undefined ||
|
||||
icon.startsWith("http:") ||
|
||||
icon.startsWith("https:")
|
||||
) {
|
||||
return icon
|
||||
}
|
||||
const color = new TagRenderingConfig(<TagRenderingConfigJson>iconSpec.color)
|
||||
.render.txt
|
||||
return icon + ":" + color
|
||||
})).join(";")
|
||||
|
||||
if (!icon) {
|
||||
icon = "./assets/svg/bug.svg"
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { Store, UIEventSource } from "../../UIEventSource"
|
||||
import { FeatureSource, IndexedFeatureSource, UpdatableFeatureSource } from "../FeatureSource"
|
||||
import { Feature } from "geojson"
|
||||
import { Utils } from "../../../Utils"
|
||||
import { OsmFeature } from "../../../Models/OsmFeature"
|
||||
import { Lists } from "../../../Utils/Lists"
|
||||
|
||||
/**
|
||||
* The featureSourceMerger receives complete geometries from various sources.
|
||||
|
|
@ -23,7 +23,7 @@ export default class FeatureSourceMerger<Src extends FeatureSource = FeatureSour
|
|||
constructor(...sources: Src[]) {
|
||||
this._featuresById = new UIEventSource<Map<string, Feature>>(new Map<string, Feature>())
|
||||
this.featuresById = this._featuresById
|
||||
sources = Utils.NoNull(sources)
|
||||
sources = Lists.noNull(sources)
|
||||
for (const source of sources) {
|
||||
source.features.addCallback(() => {
|
||||
this.addDataFromSources(sources)
|
||||
|
|
@ -69,7 +69,7 @@ export default class FeatureSourceMerger<Src extends FeatureSource = FeatureSour
|
|||
}
|
||||
|
||||
protected addData(sources: Feature[][]) {
|
||||
sources = Utils.NoNull(sources)
|
||||
sources = Lists.noNull(sources)
|
||||
let somethingChanged = false
|
||||
const all: Map<string, Feature> = new Map()
|
||||
const unseen = new Set<string>()
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import BaseUIElement from "../../../UI/BaseUIElement"
|
|||
import { Utils } from "../../../Utils"
|
||||
import { OsmTags } from "../../../Models/OsmFeature"
|
||||
import { FeatureSource } from "../FeatureSource"
|
||||
import { Lists } from "../../../Utils/Lists"
|
||||
|
||||
/**
|
||||
* Highly specialized feature source.
|
||||
|
|
@ -48,11 +49,9 @@ export class LastClickFeatureSource implements FeatureSource {
|
|||
allPresets.push(html)
|
||||
}
|
||||
|
||||
this.renderings = Utils.Dedup(
|
||||
allPresets.map((uiElem) =>
|
||||
Utils.runningFromConsole ? "" : uiElem.ConstructElement().innerHTML
|
||||
)
|
||||
)
|
||||
this.renderings = Lists.dedup(allPresets.map((uiElem) =>
|
||||
Utils.runningFromConsole ? "" : uiElem.ConstructElement().innerHTML
|
||||
))
|
||||
|
||||
this._features = new UIEventSource<Feature[]>([])
|
||||
this.features = this._features
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import { Feature } from "geojson"
|
|||
import FeatureSourceMerger from "../Sources/FeatureSourceMerger"
|
||||
import OsmObjectDownloader from "../../Osm/OsmObjectDownloader"
|
||||
import FullNodeDatabaseSource from "../TiledFeatureSource/FullNodeDatabaseSource"
|
||||
import { Lists } from "../../../Utils/Lists"
|
||||
|
||||
/**
|
||||
* If a tile is needed (requested via the UIEventSource in the constructor), will download the appropriate tile and pass it via 'handleTile'
|
||||
|
|
@ -173,7 +174,7 @@ export default class OsmFeatureSource extends FeatureSourceMerger {
|
|||
for (let i = 0; i < features.length; i++) {
|
||||
features[i] = await this.patchIncompleteRelations(features[i], <any>osmJson)
|
||||
}
|
||||
features = Utils.NoNull(features)
|
||||
features = Lists.noNull(features)
|
||||
features.forEach((f) => {
|
||||
f.properties["_backend"] = this._backend
|
||||
})
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import { Utils } from "../../../Utils"
|
|||
import { TagsFilter } from "../../Tags/TagsFilter"
|
||||
import { BBox } from "../../BBox"
|
||||
import { OsmTags } from "../../../Models/OsmFeature"
|
||||
import { Lists } from "../../../Utils/Lists"
|
||||
|
||||
("use strict")
|
||||
|
||||
|
|
@ -199,7 +200,7 @@ export default class OverpassFeatureSource implements UpdatableFeatureSource {
|
|||
*/
|
||||
private GetFilter(interpreterUrl: string, layersToDownload: LayerConfig[]): Overpass {
|
||||
let filters: TagsFilter[] = layersToDownload.map((layer) => layer.source.osmTags)
|
||||
filters = Utils.NoNull(filters)
|
||||
filters = Lists.noNull(filters)
|
||||
if (filters.length === 0) {
|
||||
return undefined
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { Utils } from "../../../Utils"
|
|||
import { Feature, MultiLineString, Position } from "geojson"
|
||||
import { GeoOperations } from "../../GeoOperations"
|
||||
import { UpdatableDynamicTileSource } from "./DynamicTileSource"
|
||||
import { Lists } from "../../../Utils/Lists"
|
||||
|
||||
/**
|
||||
* The PolygonSourceMerger receives various small pieces of bigger polygons and stitches them together.
|
||||
|
|
@ -32,7 +33,7 @@ export class LineSourceMerger extends UpdatableDynamicTileSource<
|
|||
}
|
||||
|
||||
protected addDataFromSources(sources: FeatureSourceForTile[]) {
|
||||
sources = Utils.NoNull(sources)
|
||||
sources = Lists.noNull(sources)
|
||||
const all: Map<string, Feature<MultiLineString>> = new Map()
|
||||
const currentZoom = this._zoomlevel?.data ?? 0
|
||||
for (const source of sources) {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { FeatureSourceForTile, UpdatableFeatureSource } from "../FeatureSource"
|
||||
import { Store } from "../../UIEventSource"
|
||||
import { BBox } from "../../BBox"
|
||||
import { Utils } from "../../../Utils"
|
||||
import { Feature } from "geojson"
|
||||
import { GeoOperations } from "../../GeoOperations"
|
||||
import { UpdatableDynamicTileSource } from "./DynamicTileSource"
|
||||
import { Lists } from "../../../Utils/Lists"
|
||||
|
||||
/**
|
||||
* The PolygonSourceMerger receives various small pieces of bigger polygons and stitches them together.
|
||||
|
|
@ -29,7 +29,7 @@ export class PolygonSourceMerger extends UpdatableDynamicTileSource<
|
|||
}
|
||||
|
||||
protected addDataFromSources(sources: FeatureSourceForTile[]) {
|
||||
sources = Utils.NoNull(sources)
|
||||
sources = Lists.noNull(sources)
|
||||
const all: Map<string, Feature> = new Map()
|
||||
const zooms: Map<string, number> = new Map()
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import {
|
|||
} from "geojson"
|
||||
import { Tiles } from "../Models/TileRange"
|
||||
import { Utils } from "../Utils"
|
||||
import { Lists } from "../Utils/Lists"
|
||||
|
||||
("use strict")
|
||||
|
||||
|
|
@ -597,7 +598,7 @@ export class GeoOperations {
|
|||
newFeatures.push(intersectionPart)
|
||||
}
|
||||
}
|
||||
return Utils.NoNull(newFeatures)
|
||||
return Lists.noNull(newFeatures)
|
||||
}
|
||||
|
||||
public static toGpx(
|
||||
|
|
@ -610,7 +611,7 @@ export class GeoOperations {
|
|||
if (title === undefined || title === "") {
|
||||
title = "Uploaded with MapComplete"
|
||||
}
|
||||
title = Utils.EncodeXmlValue(title)
|
||||
title = Utils.encodeXmlValue(title)
|
||||
const trackPoints: string[] = []
|
||||
let locationsWithMeta: Feature<Point>[]
|
||||
if (Array.isArray(locations)) {
|
||||
|
|
@ -664,7 +665,7 @@ export class GeoOperations {
|
|||
if (title === undefined || title === "") {
|
||||
title = "Created with MapComplete"
|
||||
}
|
||||
title = Utils.EncodeXmlValue(title)
|
||||
title = Utils.encodeXmlValue(title)
|
||||
const trackPoints: string[] = []
|
||||
for (const l of locations) {
|
||||
let trkpt = ` <wpt lat="${l.geometry.coordinates[1]}" lon="${l.geometry.coordinates[0]}">`
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import { WikidataImageProvider } from "./WikidataImageProvider"
|
|||
import Panoramax from "./Panoramax"
|
||||
import { Utils } from "../../Utils"
|
||||
import { ServerSourceInfo } from "../../Models/SourceOverview"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
|
||||
/**
|
||||
* A generic 'from the interwebz' image picker, without attribution
|
||||
|
|
@ -101,9 +102,7 @@ export default class AllImageProviders {
|
|||
Mapillary.singleton,
|
||||
AllImageProviders.genericImageProvider,
|
||||
]
|
||||
const allPrefixes = Utils.Dedup(
|
||||
prefixes ?? [].concat(...sources.map((s) => s.defaultKeyPrefixes))
|
||||
)
|
||||
const allPrefixes = Lists.dedup(prefixes ?? [].concat(...sources.map((s) => s.defaultKeyPrefixes)))
|
||||
for (const prefix of allPrefixes) {
|
||||
for (const k in tags) {
|
||||
const v = tags[k]
|
||||
|
|
@ -149,7 +148,7 @@ export default class AllImageProviders {
|
|||
allSources.push(singleSource)
|
||||
}
|
||||
const source = Stores.concat(allSources).map((result) => {
|
||||
const all = Utils.concat(result)
|
||||
const all = result.flatMap(x => x)
|
||||
return Utils.DedupOnId(all, (i) => [i?.id, i?.url, i?.alt_id])
|
||||
})
|
||||
this._cachedImageStores[cachekey] = source
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { Utils } from "../../Utils"
|
|||
import { Feature, Point } from "geojson"
|
||||
import { ServerSourceInfo } from "../../Models/SourceOverview"
|
||||
import { ComponentType } from "svelte/types/runtime/internal/dev"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
|
||||
export interface ProvidedImage {
|
||||
url: string
|
||||
|
|
@ -92,7 +93,7 @@ export default abstract class ImageProvider {
|
|||
) {
|
||||
continue
|
||||
}
|
||||
const values = Utils.NoEmpty(tags[key]?.split(";")?.map((v) => v.trim()) ?? [])
|
||||
const values = Lists.noEmpty(tags[key]?.split(";")?.map((v) => v.trim()) ?? [])
|
||||
for (const value of values) {
|
||||
if (seenValues.has(value)) {
|
||||
continue
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import NoteCommentElement from "../../UI/Popup/Notes/NoteCommentElement"
|
|||
import OsmObjectDownloader from "../Osm/OsmObjectDownloader"
|
||||
import ExifReader from "exifreader"
|
||||
import { Utils } from "../../Utils"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
|
||||
/**
|
||||
* The ImageUploadManager has a
|
||||
|
|
@ -176,7 +177,7 @@ export class ImageUploadManager {
|
|||
const failed: Set<ImageUploadArguments> = new Set()
|
||||
this.uploadingAll = true
|
||||
do {
|
||||
queue = Utils.NoNull(this._queue.imagesInQueue.data ?? []).filter(
|
||||
queue = Lists.noNull(this._queue.imagesInQueue.data ?? []).filter(
|
||||
(item) => !failed.has(item)
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { Feature, Point } from "geojson"
|
|||
import { Store, UIEventSource } from "../UIEventSource"
|
||||
import { ServerSourceInfo } from "../../Models/SourceOverview"
|
||||
import { ComponentType } from "svelte/types/runtime/internal/dev"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
|
||||
export class Mapillary extends ImageProvider {
|
||||
public static readonly singleton = new Mapillary()
|
||||
|
|
@ -74,11 +75,9 @@ export class Mapillary extends ImageProvider {
|
|||
pKey,
|
||||
}
|
||||
const baselink = `https://www.mapillary.com/app/?`
|
||||
const paramsStr = Utils.NoNull(
|
||||
Object.keys(params).map((k) =>
|
||||
params[k] === undefined ? undefined : k + "=" + params[k]
|
||||
)
|
||||
)
|
||||
const paramsStr = Lists.noNull(Object.keys(params).map((k) =>
|
||||
params[k] === undefined ? undefined : k + "=" + params[k]
|
||||
))
|
||||
return baselink + paramsStr.join("&")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { Utils } from "../../Utils"
|
|||
import { Feature, Point } from "geojson"
|
||||
import { ServerSourceInfo } from "../../Models/SourceOverview"
|
||||
import { ComponentType } from "svelte/types/runtime/internal/dev"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
|
||||
export class WikidataImageProvider extends ImageProvider {
|
||||
public static readonly singleton = new WikidataImageProvider()
|
||||
|
|
@ -13,7 +14,7 @@ export class WikidataImageProvider extends ImageProvider {
|
|||
public readonly name = "Wikidata"
|
||||
private static readonly keyBlacklist: ReadonlySet<string> = new Set([
|
||||
"mapillary",
|
||||
...Utils.Times((i) => "mapillary:" + i, 10),
|
||||
...Utils.times((i) => "mapillary:" + i, 10),
|
||||
])
|
||||
|
||||
private constructor() {
|
||||
|
|
@ -60,7 +61,7 @@ export class WikidataImageProvider extends ImageProvider {
|
|||
const promises = WikimediaImageProvider.singleton.ExtractUrls(undefined, commons)
|
||||
allImages.push(promises)
|
||||
}
|
||||
const resolved = await Promise.all(Utils.NoNull(allImages))
|
||||
const resolved = await Promise.all(Lists.noNull(allImages))
|
||||
const flattened = resolved.flatMap((x) => x)
|
||||
if (flattened.length === 1) {
|
||||
flattened[0].originalAttribute = { key, value }
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ import { TagsFilter } from "../../Tags/TagsFilter"
|
|||
import { And } from "../../Tags/And"
|
||||
import { Tag } from "../../Tags/Tag"
|
||||
import { OsmId } from "../../../Models/OsmFeature"
|
||||
import { Utils } from "../../../Utils"
|
||||
import OsmObjectDownloader from "../OsmObjectDownloader"
|
||||
import { Lists } from "../../../Utils/Lists"
|
||||
|
||||
export default class DeleteAction extends OsmChangeAction {
|
||||
private readonly _softDeletionTags: TagsFilter
|
||||
|
|
@ -50,12 +50,12 @@ export default class DeleteAction extends OsmChangeAction {
|
|||
this._softDeletionTags = softDeletionTags
|
||||
} else {
|
||||
this._softDeletionTags = new And(
|
||||
Utils.NoNull([
|
||||
softDeletionTags,
|
||||
new Tag(
|
||||
"fixme",
|
||||
`A mapcomplete user marked this feature to be deleted (${meta.specialMotivation})`
|
||||
),
|
||||
Lists.noNull([
|
||||
softDeletionTags,
|
||||
new Tag(
|
||||
"fixme",
|
||||
`A mapcomplete user marked this feature to be deleted (${meta.specialMotivation})`
|
||||
),
|
||||
])
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import { Utils } from "../../../Utils"
|
|||
import { OsmConnection } from "../OsmConnection"
|
||||
import { Feature, Geometry, LineString, Point } from "geojson"
|
||||
import FullNodeDatabaseSource from "../../FeatureSource/TiledFeatureSource/FullNodeDatabaseSource"
|
||||
import { Lists } from "../../../Utils/Lists"
|
||||
|
||||
export default class ReplaceGeometryAction extends OsmChangeAction implements PreviewableAction {
|
||||
/**
|
||||
|
|
@ -164,7 +165,7 @@ export default class ReplaceGeometryAction extends OsmChangeAction implements Pr
|
|||
preview.push(feature)
|
||||
})
|
||||
|
||||
return StaticFeatureSource.fromGeojson(Utils.NoNull(preview))
|
||||
return StaticFeatureSource.fromGeojson(Lists.noNull(preview))
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -317,7 +318,7 @@ export default class ReplaceGeometryAction extends OsmChangeAction implements Pr
|
|||
candidate = undefined
|
||||
moveDistance = Infinity
|
||||
distances.forEach((distances, nodeId) => {
|
||||
const minDist = Math.min(...Utils.NoNull(distances))
|
||||
const minDist = Math.min(...(Lists.noNull(distances)))
|
||||
if (moveDistance > minDist) {
|
||||
// We have found a candidate to move
|
||||
candidate = nodeId
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import DeleteAction from "./Actions/DeleteAction"
|
|||
import MarkdownUtils from "../../Utils/MarkdownUtils"
|
||||
import FeaturePropertiesStore from "../FeatureSource/Actors/FeaturePropertiesStore"
|
||||
import { Feature, Point } from "geojson"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
|
||||
/**
|
||||
* Handles all changes made to OSM.
|
||||
|
|
@ -260,7 +261,7 @@ export class Changes {
|
|||
}
|
||||
|
||||
public static GetNeededIds(changes: ChangeDescription[]) {
|
||||
return Utils.Dedup(changes.filter((c) => c.id >= 0).map((c) => c.type + "/" + c.id))
|
||||
return Lists.dedup(changes.filter((c) => c.id >= 0).map((c) => c.type + "/" + c.id))
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -467,8 +468,8 @@ export class Changes {
|
|||
if (change.changes !== undefined) {
|
||||
switch (change.type) {
|
||||
case "node": {
|
||||
const nlat = Utils.Round7(change.changes.lat)
|
||||
const nlon = Utils.Round7(change.changes.lon)
|
||||
const nlat = Utils.round7(change.changes.lat)
|
||||
const nlon = Utils.round7(change.changes.lon)
|
||||
const n = <OsmNode>obj
|
||||
if (n.lat !== nlat || n.lon !== nlon) {
|
||||
n.lat = nlat
|
||||
|
|
@ -717,11 +718,9 @@ export class Changes {
|
|||
* We _do not_ pass in the Changes object itself - we want the data from OSM directly in order to apply the changes
|
||||
*/
|
||||
const downloader = new OsmObjectDownloader(this.backend, undefined)
|
||||
const osmObjects = Utils.NoNull(
|
||||
await Promise.all<{ id: string; osmObj: OsmObject | "deleted" }>(
|
||||
neededIds.map((id) => this.getOsmObject(id, downloader))
|
||||
)
|
||||
)
|
||||
const osmObjects = Lists.noNull(await Promise.all<{ id: string; osmObj: OsmObject | "deleted" }>(
|
||||
neededIds.map((id) => this.getOsmObject(id, downloader))
|
||||
))
|
||||
|
||||
// Drop changes to deleted items
|
||||
for (const { osmObj, id } of osmObjects) {
|
||||
|
|
@ -801,7 +800,7 @@ export class Changes {
|
|||
value: descr.meta.specialMotivation,
|
||||
}))
|
||||
|
||||
const distances = Utils.NoNull(pending.map((descr) => descr.meta.distanceToObject))
|
||||
const distances = Lists.noNull(pending.map((descr) => descr.meta.distanceToObject))
|
||||
distances.sort((a, b) => a - b)
|
||||
const perBinCount = Constants.distanceToChangeObjectBins.map(() => 0)
|
||||
|
||||
|
|
@ -816,23 +815,21 @@ export class Changes {
|
|||
}
|
||||
}
|
||||
|
||||
const perBinMessage = Utils.NoNull(
|
||||
perBinCount.map((count, i) => {
|
||||
if (count === 0) {
|
||||
return undefined
|
||||
}
|
||||
const maxD = maxDistances[i]
|
||||
let key = `change_within_${maxD}m`
|
||||
if (maxD === Number.MAX_VALUE) {
|
||||
key = `change_over_${maxDistances[i - 1]}m`
|
||||
}
|
||||
return {
|
||||
key,
|
||||
value: count,
|
||||
aggregate: true,
|
||||
}
|
||||
})
|
||||
)
|
||||
const perBinMessage = Lists.noNull(perBinCount.map((count, i) => {
|
||||
if (count === 0) {
|
||||
return undefined
|
||||
}
|
||||
const maxD = maxDistances[i]
|
||||
let key = `change_within_${maxD}m`
|
||||
if (maxD === Number.MAX_VALUE) {
|
||||
key = `change_over_${maxDistances[i - 1]}m`
|
||||
}
|
||||
return {
|
||||
key,
|
||||
value: count,
|
||||
aggregate: true,
|
||||
}
|
||||
}))
|
||||
|
||||
// This method is only called with changedescriptions for this theme
|
||||
const theme = pending[0].meta.theme
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import { Utils } from "../../Utils"
|
|||
import FeaturePropertiesStore from "../FeatureSource/Actors/FeaturePropertiesStore"
|
||||
import { AndroidPolyfill } from "../Web/AndroidPolyfill"
|
||||
import ImageUploadQueue from "../ImageProviders/ImageUploadQueue"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
|
||||
export interface ChangesetTag {
|
||||
key: string
|
||||
|
|
@ -393,7 +394,7 @@ export class ChangesetHandler {
|
|||
private async UpdateTags(csId: number, tags: ChangesetTag[]) {
|
||||
tags = ChangesetHandler.removeDuplicateMetaTags(tags)
|
||||
|
||||
tags = Utils.NoNull(tags).filter(
|
||||
tags = Lists.noNull(tags).filter(
|
||||
(tag) =>
|
||||
tag.key !== undefined &&
|
||||
tag.value !== undefined &&
|
||||
|
|
|
|||
|
|
@ -150,7 +150,7 @@ export abstract class OsmObject {
|
|||
}
|
||||
const v = this.tags[key]
|
||||
if (v !== "" && v !== undefined) {
|
||||
tags += ` <tag k="${Utils.EncodeXmlValue(key)}" v="${Utils.EncodeXmlValue(
|
||||
tags += ` <tag k="${Utils.encodeXmlValue(key)}" v="${Utils.encodeXmlValue(
|
||||
this.tags[key]
|
||||
)}"/>
|
||||
`
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { Store, UIEventSource } from "../UIEventSource"
|
|||
import { OsmConnection } from "./OsmConnection"
|
||||
import { LocalStorageSource } from "../Web/LocalStorageSource"
|
||||
import { Utils } from "../../Utils"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
import OSMAuthInstance = OSMAuth.osmAuth
|
||||
|
||||
export class OsmPreferences {
|
||||
|
|
@ -270,7 +271,7 @@ export class OsmPreferences {
|
|||
return
|
||||
}
|
||||
// _All_ keys are deleted first, to avoid pending parts
|
||||
const keysToDelete = Utils.Dedup(OsmPreferences.keysStartingWith(this.seenKeys, k))
|
||||
const keysToDelete = Lists.dedup(OsmPreferences.keysStartingWith(this.seenKeys, k))
|
||||
if (v === null || v === undefined || v === "" || v === "undefined" || v === "null") {
|
||||
for (const k of keysToDelete) {
|
||||
await this.deleteKeyDirectly(k)
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ export default class CombinedSearcher implements GeocodingProvider {
|
|||
* @param providers
|
||||
*/
|
||||
constructor(...providers: ReadonlyArray<GeocodingProvider>) {
|
||||
this._providers = Utils.NoNull(providers)
|
||||
this._providers = Utils.noNull(providers)
|
||||
this._providersWithSuggest = this._providers.filter((pr) => pr.suggest !== undefined)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import GeocodingProvider, { GeocodeResult } from "./GeocodingProvider"
|
||||
import { Utils } from "../../Utils"
|
||||
import { ImmutableStore, Store } from "../UIEventSource"
|
||||
import CoordinateParser from "coordinate-parser"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
|
||||
/**
|
||||
* A simple search-class which interprets possible locations
|
||||
|
|
@ -71,13 +71,11 @@ export default class CoordinateSearch implements GeocodingProvider {
|
|||
* results[0] // => {lat: 51.047977, lon: 3.51184, "display_name": "lon: 3.51184, lat: 51.047977", "category": "coordinate","osm_id": "3.51184/51.047977", "source": "coordinate:latlon"}
|
||||
*/
|
||||
private directSearch(query: string): GeocodeResult[] {
|
||||
const matches = Utils.NoNull(CoordinateSearch.latLonRegexes.map((r) => query.match(r))).map(
|
||||
const matches = Lists.noNull(CoordinateSearch.latLonRegexes.map((r) => query.match(r))).map(
|
||||
(m) => CoordinateSearch.asResult(m[2], m[1], "latlon")
|
||||
)
|
||||
|
||||
const matchesLonLat = Utils.NoNull(
|
||||
CoordinateSearch.lonLatRegexes.map((r) => query.match(r))
|
||||
).map((m) => CoordinateSearch.asResult(m[1], m[2], "lonlat"))
|
||||
const matchesLonLat = Lists.noNull(CoordinateSearch.lonLatRegexes.map((r) => query.match(r))).map((m) => CoordinateSearch.asResult(m[1], m[2], "lonlat"))
|
||||
const init = matches.concat(matchesLonLat)
|
||||
if (init.length > 0) {
|
||||
return init
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import FilterConfig, { FilterConfigOption } from "../../Models/ThemeConfig/Filte
|
|||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
||||
import LayerState from "../State/LayerState"
|
||||
import ThemeConfig from "../../Models/ThemeConfig/ThemeConfig"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
|
||||
export type FilterSearchResult = {
|
||||
option: FilterConfigOption
|
||||
|
|
@ -64,7 +65,7 @@ export default class FilterSearch {
|
|||
].flatMap((term) => [term, ...(term?.split(" ") ?? [])])
|
||||
terms = terms.map((t) => Utils.simplifyStringForSearch(t))
|
||||
terms.push(option.emoji)
|
||||
Utils.NoNullInplace(terms)
|
||||
Lists.noNullInplace(terms)
|
||||
const distances = queries.flatMap((query) =>
|
||||
terms.map((entry) => {
|
||||
const d = Utils.levenshteinDistance(query, entry.slice(0, query.length))
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { Feature } from "geojson"
|
|||
import { GeoOperations } from "../GeoOperations"
|
||||
import { ImmutableStore, Store, Stores } from "../UIEventSource"
|
||||
import OpenStreetMapIdSearch from "./OpenStreetMapIdSearch"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
|
||||
type IntermediateResult = {
|
||||
feature: Feature
|
||||
|
|
@ -41,13 +42,13 @@ export default class LocalElementSearch implements GeocodingProvider {
|
|||
|
||||
for (const feature of features) {
|
||||
const props = feature.properties
|
||||
const searchTerms: string[] = Utils.NoNull([
|
||||
props.name,
|
||||
props.alt_name,
|
||||
props.local_name,
|
||||
props["addr:street"] && props["addr:number"]
|
||||
? props["addr:street"] + props["addr:number"]
|
||||
: undefined,
|
||||
const searchTerms: string[] = Lists.noNull([
|
||||
props.name,
|
||||
props.alt_name,
|
||||
props.local_name,
|
||||
props["addr:street"] && props["addr:number"]
|
||||
? props["addr:street"] + props["addr:number"]
|
||||
: undefined,
|
||||
])
|
||||
|
||||
let levehnsteinD: number
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { Store, UIEventSource } from "../UIEventSource"
|
||||
import GeocodingProvider, { GeocodeResult, GeocodingOptions } from "./GeocodingProvider"
|
||||
import { OsmId } from "../../Models/OsmFeature"
|
||||
import { Utils } from "../../Utils"
|
||||
import OsmObjectDownloader from "../Osm/OsmObjectDownloader"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
|
||||
export default class OpenStreetMapIdSearch implements GeocodingProvider {
|
||||
private static readonly regex =
|
||||
|
|
@ -76,7 +76,7 @@ export default class OpenStreetMapIdSearch implements GeocodingProvider {
|
|||
async search(query: string, _: GeocodingOptions): Promise<GeocodeResult[]> {
|
||||
if (!isNaN(Number(query))) {
|
||||
const n = Number(query)
|
||||
return Utils.NoNullInplace(
|
||||
return Lists.noNullInplace(
|
||||
await Promise.all([
|
||||
this.getInfoAbout(`node/${n}`).catch(() => undefined),
|
||||
this.getInfoAbout(`way/${n}`).catch(() => undefined),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import Locale from "../../UI/i18n/Locale"
|
||||
import { Utils } from "../../Utils"
|
||||
import ThemeSearch from "./ThemeSearch"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
|
||||
export default class SearchUtils {
|
||||
/** Applies special search terms, such as 'studio', 'osmcha', ...
|
||||
|
|
@ -66,7 +67,7 @@ export default class SearchUtils {
|
|||
} else {
|
||||
terms = (keywords[language] ?? []).concat(keywords["*"])
|
||||
}
|
||||
const termsAll = Utils.NoNullInplace(terms).flatMap((t) => t.split(" "))
|
||||
const termsAll = Lists.noNullInplace(terms).flatMap((t) => t.split(" "))
|
||||
|
||||
let distanceSummed = 0
|
||||
for (let i = 0; i < queryParts.length; i++) {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import Fuse, { IFuseOptions } from "fuse.js"
|
|||
import Constants from "../../Models/Constants"
|
||||
import Locale from "../../UI/i18n/Locale"
|
||||
import { Utils } from "../../Utils"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
|
||||
export class ThemeSearchIndex {
|
||||
private readonly themeIndex: Fuse<MinimalThemeInformation>
|
||||
|
|
@ -18,7 +19,7 @@ export class ThemeSearchIndex {
|
|||
themesToSearch?: MinimalThemeInformation[],
|
||||
layersToIgnore: string[] = []
|
||||
) {
|
||||
const themes = Utils.NoNull(themesToSearch ?? ThemeSearch.officialThemes?.themes)
|
||||
const themes = Utils.noNull(themesToSearch ?? ThemeSearch.officialThemes?.themes)
|
||||
if (!themes) {
|
||||
throw "No themes loaded. Did generate:layeroverview fail?"
|
||||
}
|
||||
|
|
@ -100,7 +101,7 @@ export class ThemeSearchIndex {
|
|||
const knownHidden: Store<string[]> = Stores.listStabilized(
|
||||
UserRelatedState.initDiscoveredHiddenThemes(state.osmConnection)
|
||||
.stabilized(1000)
|
||||
.map((list) => Utils.Dedup(list))
|
||||
.map((list) => Lists.dedup(list))
|
||||
)
|
||||
const otherThemes: MinimalThemeInformation[] = ThemeSearch.officialThemes.themes.filter(
|
||||
(th) => th.id !== state.theme.id
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@ import { Feature } from "geojson"
|
|||
import OpenLocationCodeSearch from "../Search/OpenLocationCodeSearch"
|
||||
import { BBox } from "../BBox"
|
||||
import { QueryParameters } from "../Web/QueryParameters"
|
||||
import { Utils } from "../../Utils"
|
||||
import { NominatimGeocoding } from "../Search/NominatimGeocoding"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
|
||||
export default class SearchState {
|
||||
public readonly feedback: UIEventSource<Translation> = new UIEventSource<Translation>(undefined)
|
||||
|
|
@ -83,7 +83,7 @@ export default class SearchState {
|
|||
}
|
||||
})))
|
||||
this.runningEngines = isRunningPerEngine.bindD(
|
||||
listOfSources => Stores.concat(listOfSources).mapD(list => Utils.NoNull(list)))
|
||||
listOfSources => Stores.concat(listOfSources).mapD(list => Lists.noNull(list)))
|
||||
|
||||
|
||||
this.failedEngines = suggestionsListWithSource
|
||||
|
|
@ -102,7 +102,7 @@ export default class SearchState {
|
|||
return []
|
||||
}
|
||||
}),
|
||||
))).map(list => Utils.NoNull(list?.flatMap(x => x) ?? []))
|
||||
))).map(list => Lists.noNull(list?.flatMap(x => x) ?? []))
|
||||
|
||||
this.suggestionsSearchRunning = this.runningEngines.map(running => running?.length > 0)
|
||||
this.suggestions = suggestionsList.bindD((suggestions) =>
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import Showdown from "showdown"
|
|||
import { LocalStorageSource } from "../Web/LocalStorageSource"
|
||||
import { GeocodeResult } from "../Search/GeocodingProvider"
|
||||
import Translations from "../../UI/i18n/Translations"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
|
||||
class RoundRobinStore<T> {
|
||||
private readonly _store: UIEventSource<T[]>
|
||||
|
|
@ -41,7 +42,7 @@ class RoundRobinStore<T> {
|
|||
private set() {
|
||||
const v = this._store.data
|
||||
const i = this._index.data
|
||||
const newList = Utils.NoNull(v.slice(i + 1, v.length).concat(v.slice(0, i + 1)))
|
||||
const newList = Lists.noNull(v.slice(i + 1, v.length).concat(v.slice(0, i + 1)))
|
||||
if (newList.length === 0) {
|
||||
this._index.set(0)
|
||||
}
|
||||
|
|
@ -89,7 +90,7 @@ export class OptionallySyncedHistory<T extends object | string> {
|
|||
defaultValue: "sync",
|
||||
})
|
||||
|
||||
this.syncedBackingStore = UIEventSource.concat(Utils.TimesT(maxHistory, (i) => {
|
||||
this.syncedBackingStore = UIEventSource.concat(Utils.timesT(maxHistory, (i) => {
|
||||
const pref = osmconnection.getPreference(key + "-hist-" + i + "-")
|
||||
return UIEventSource.asObject<T>(pref, undefined)
|
||||
}))
|
||||
|
|
@ -555,27 +556,25 @@ export default class UserRelatedState {
|
|||
|
||||
const untranslated = missing.untranslated.get(language) ?? []
|
||||
const hasMissingTheme = untranslated.some((k) => k.startsWith("themes:"))
|
||||
const missingLayers = Utils.Dedup(
|
||||
untranslated
|
||||
.filter((k) => k.startsWith("layers:"))
|
||||
.map((k) => k.slice("layers:".length).split(".")[0])
|
||||
)
|
||||
const missingLayers = Lists.dedup(untranslated
|
||||
.filter((k) => k.startsWith("layers:"))
|
||||
.map((k) => k.slice("layers:".length).split(".")[0]))
|
||||
|
||||
const zenLinks: { link: string; id: string }[] = Utils.NoNull([
|
||||
hasMissingTheme
|
||||
? {
|
||||
id: "theme:" + layout.id,
|
||||
link: Translations.hrefToWeblateZen(
|
||||
language,
|
||||
"themes",
|
||||
layout.id
|
||||
),
|
||||
}
|
||||
: undefined,
|
||||
...missingLayers.map((id) => ({
|
||||
id: "layer:" + id,
|
||||
link: Translations.hrefToWeblateZen(language, "layers", id),
|
||||
})),
|
||||
const zenLinks: { link: string; id: string }[] = Lists.noNull([
|
||||
hasMissingTheme
|
||||
? {
|
||||
id: "theme:" + layout.id,
|
||||
link: Translations.hrefToWeblateZen(
|
||||
language,
|
||||
"themes",
|
||||
layout.id
|
||||
),
|
||||
}
|
||||
: undefined,
|
||||
...missingLayers.map((id) => ({
|
||||
id: "layer:" + id,
|
||||
link: Translations.hrefToWeblateZen(language, "layers", id),
|
||||
})),
|
||||
])
|
||||
const untranslated_count = untranslated.length
|
||||
amendedPrefs.data["_translation_total"] = "" + total
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import key_counts from "../../assets/key_totals.json"
|
|||
|
||||
import { ConversionContext } from "../../Models/ThemeConfig/Conversion/ConversionContext"
|
||||
import { FlatTag, TagsFilterClosed, UploadableTag } from "./TagTypes"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
|
||||
type Tags = Record<string, string>
|
||||
|
||||
|
|
@ -383,7 +384,7 @@ export class TagUtils {
|
|||
const keyValues = TagUtils.SplitKeys(tagsFilters)
|
||||
const and: UploadableTag[] = []
|
||||
for (const key in keyValues) {
|
||||
const values = Utils.Dedup(keyValues[key]).filter((v) => v !== "")
|
||||
const values = Lists.dedup(keyValues[key]).filter((v) => v !== "")
|
||||
values.sort()
|
||||
and.push(new Tag(key, values.join(";")))
|
||||
}
|
||||
|
|
@ -742,7 +743,7 @@ export class TagUtils {
|
|||
if (level === undefined || level === null) {
|
||||
return []
|
||||
}
|
||||
let spec = Utils.NoNull([level])
|
||||
let spec = Lists.noNull([level])
|
||||
spec = [].concat(...spec.map((s) => s?.split(";")))
|
||||
spec = [].concat(
|
||||
...spec.map((s) => {
|
||||
|
|
@ -764,7 +765,7 @@ export class TagUtils {
|
|||
return values
|
||||
})
|
||||
)
|
||||
return Utils.NoNull(spec)
|
||||
return Lists.noNull(spec)
|
||||
}
|
||||
|
||||
private static ParseTagUnsafe(json: TagConfigJson, context: string = ""): TagsFilterClosed {
|
||||
|
|
@ -919,10 +920,10 @@ export class TagUtils {
|
|||
|
||||
public static GetPopularity(tag: TagsFilter): number | undefined {
|
||||
if (tag instanceof And) {
|
||||
return Math.min(...Utils.NoNull(tag.and.map((t) => TagUtils.GetPopularity(t)))) - 1
|
||||
return Math.min(...(Lists.noNull(tag.and.map((t) => TagUtils.GetPopularity(t))))) - 1
|
||||
}
|
||||
if (tag instanceof Or) {
|
||||
return Math.max(...Utils.NoNull(tag.or.map((t) => TagUtils.GetPopularity(t)))) + 1
|
||||
return Math.max(...(Lists.noNull(tag.or.map((t) => TagUtils.GetPopularity(t))))) + 1
|
||||
}
|
||||
if (tag instanceof Tag) {
|
||||
return TagUtils.GetCount(tag.key, tag.value)
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import { RegexTag } from "../Tags/RegexTag"
|
|||
import { TagConfigJson } from "../../Models/ThemeConfig/Json/TagConfigJson"
|
||||
import { TagUtils } from "../Tags/TagUtils"
|
||||
import Constants from "../../Models/Constants"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
|
||||
/**
|
||||
* Main name suggestion index file
|
||||
|
|
@ -103,7 +104,7 @@ export default class NameSuggestionIndex {
|
|||
}
|
||||
})
|
||||
)
|
||||
stats = Utils.NoNull(stats)
|
||||
stats = Lists.noNull(stats)
|
||||
if (stats.length === 1) {
|
||||
return stats[0]
|
||||
}
|
||||
|
|
@ -282,7 +283,7 @@ export default class NameSuggestionIndex {
|
|||
}
|
||||
const keys = Object.keys(this.nsiFile.nsi)
|
||||
const all = keys.map((k) => this.nsiFile.nsi[k].properties.path.split("/")[0])
|
||||
this._supportedTypes = Utils.Dedup(all).map((s) => {
|
||||
this._supportedTypes = Lists.dedup(all).map((s) => {
|
||||
if (s.endsWith("s")) {
|
||||
s = s.substring(0, s.length - 1)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { Utils } from "../../Utils"
|
|||
import { Store, UIEventSource } from "../UIEventSource"
|
||||
import { SimplifiedClaims, WBK } from "wikibase-sdk"
|
||||
import { ServerSourceInfo } from "../../Models/SourceOverview"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
|
||||
export class WikidataResponse {
|
||||
public readonly id: string
|
||||
|
|
@ -294,7 +295,7 @@ export default class Wikidata {
|
|||
}
|
||||
})
|
||||
)
|
||||
return Utils.NoNull(maybeResponses.map((r) => <WikidataResponse>r["success"]))
|
||||
return Lists.noNull(maybeResponses.map((r) => <WikidataResponse>r["success"]))
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue