Merge develop

This commit is contained in:
Pieter Vander Vennet 2025-02-05 11:41:27 +01:00
commit f25f5f156d
86 changed files with 1960 additions and 967 deletions

View file

@ -13,6 +13,7 @@ class SingleTileSaver {
private readonly _registeredIds = new Set<string>()
private readonly _featureProperties: FeaturePropertiesStore
private readonly _isDirty = new UIEventSource(false)
constructor(
storage: UIEventSource<Feature[]> & { flush: () => void },
featureProperties: FeaturePropertiesStore
@ -62,6 +63,7 @@ class SingleTileSaver {
export default class SaveFeatureSourceToLocalStorage {
public readonly storage: TileLocalStorage<Feature[]>
private readonly zoomlevel: number
constructor(
backend: string,
layername: string,
@ -75,8 +77,25 @@ export default class SaveFeatureSourceToLocalStorage {
this.storage = storage
const singleTileSavers: Map<number, SingleTileSaver> = new Map<number, SingleTileSaver>()
features.features.addCallbackAndRunD((features) => {
if (features.some(f => {
let totalPoints = 0
if (f.geometry.type === "MultiPolygon") {
totalPoints = f.geometry.coordinates.map(rings => rings.map(ring => ring.length).reduce((a, b) => a + b)).reduce((a, b) => a + b)
} else if (f.geometry.type === "Polygon" || f.geometry.type === "MultiLineString") {
totalPoints = f.geometry.coordinates.map(ring => ring.length).reduce((a, b) => a + b)
} else if (f.geometry.type === "LineString") {
totalPoints = f.geometry.coordinates.length
}
if (totalPoints > 1000) {
console.warn(`Not caching tiles, detected a big object (${totalPoints} points for ${f.properties.id})`)
return true
}
return false
})) {
// Has big objects
return
}
const sliced = GeoOperations.spreadIntoBboxes(features, zoomlevel)
sliced.forEach((features, tileIndex) => {
let tileSaver = singleTileSavers.get(tileIndex)
if (tileSaver === undefined) {

View file

@ -90,6 +90,9 @@ export default class AllImageProviders {
const allPrefixes = Utils.Dedup(prefixes ?? [].concat(...sources.map(s => s.defaultKeyPrefixes)))
for (const prefix of allPrefixes) {
for (const k in tags) {
if (!tags[k]) {
continue
}
if (k === prefix || k.startsWith(prefix + ":")) {
count++
continue

View file

@ -18,29 +18,29 @@ interface OsmUserInfo {
"contributor_terms": {
"agreed": boolean,
"pd": boolean
},
"img": {
}
"img"?: {
"href": string,
},
"roles": string[],
}
"roles": string[]
"changesets": {
"count": number
},
"traces": {
"count": number
},
}
traces: {
count: number
}
"blocks": {
"received": {
"count": number,
"active": number
}
},
"home": {
"lat": number,
"lon": number,
"zoom": number
},
"languages": string[],
}
home?: {
lat: number,
lon: number,
zoom: number
}
"languages": string[]
"messages": {
"received": {
"count": number,
@ -49,7 +49,22 @@ interface OsmUserInfo {
"sent": {
"count": number
}
}
id: number
display_name: string
account_created: string
description: string
contributor_terms: { agreed: boolean }
roles: []
changesets: { count: number }
traces: { count: number }
blocks: { received: { count: number; active: number } }
img?: { href: string }
home: { lat: number, lon: number }
languages?: string[]
messages: { received: { count: number, unread: number }, sent: { count: number } }
}
}
@ -231,12 +246,9 @@ export class OsmConnection {
return <UIEventSource<T>>this.preferencesHandler.getPreference(key, defaultValue, prefix)
}
public LogOut() {
this.auth.logout()
this.userDetails.data.csCount = 0
this.userDetails.data.name = ""
this.userDetails.ping()
this.userDetails.setData(undefined)
console.log("Logged out")
this.loadingStatus.setData("not-attempted")
}
@ -250,7 +262,7 @@ export class OsmConnection {
return this._oauth_config.url
}
public AttemptLogin() {
public async AttemptLogin() {
this.updateCapabilities()
if (this.loadingStatus.data !== "logged-in") {
// Stay 'logged-in' if we are already logged in; this simply means we are checking for messages
@ -271,7 +283,6 @@ export class OsmConnection {
this.loadUserInfo()
}
})
}
private async loadUserInfo() {
@ -294,17 +305,15 @@ export class OsmConnection {
description: user.description,
backend: this.Backend(),
home: user.home,
languages: user.languages,
languages: user.languages ?? [],
totalMessages: user.messages.received?.count ?? 0,
img: user.img?.href,
account_created: user.account_created,
tracesCount: user.traces.count,
tracesCount: user.traces?.count ?? 0,
unreadMessages: user.messages.received?.unread ?? 0,
}
console.log("Login completed, userinfo is ", userdetails)
this.userDetails.set(userdetails)
this.loadingStatus.setData("logged-in")
} catch (err) {
console.log("Could not login due to:", err)
this.loadingStatus.setData("error")
@ -314,7 +323,7 @@ export class OsmConnection {
this.auth.logout()
this.LogOut()
} else {
console.log("Other error. Status:", err.status)
console.log("Other error. Status:", err["status"])
this.apiIsOnline.setData("unreachable")
}
}
@ -362,7 +371,7 @@ export class OsmConnection {
method,
headers: header,
content,
path: `/api/0.6/${path}`,
path: `/api/0.6/${path}`
},
function(err, response) {
if (err !== null) {
@ -444,7 +453,7 @@ export class OsmConnection {
"notes.json",
content,
{
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"
},
true,
)
@ -489,7 +498,7 @@ export class OsmConnection {
file: gpx,
description: options.description,
tags: options.labels?.join(",") ?? "",
visibility: options.visibility,
visibility: options.visibility
}
if (!contents.description) {
@ -499,7 +508,7 @@ export class OsmConnection {
file:
"; filename=\"" +
(options.filename ?? "gpx_track_mapcomplete_" + new Date().toISOString()) +
"\"\r\nContent-Type: application/gpx+xml",
"\"\r\nContent-Type: application/gpx+xml"
}
const boundary = "987654"
@ -518,7 +527,7 @@ export class OsmConnection {
const response = await this.post("gpx/create", body, {
"Content-Type": "multipart/form-data; boundary=" + boundary,
"Content-Length": "" + body.length,
"Content-Length": "" + body.length
})
const parsed = JSON.parse(response)
console.log("Uploaded GPX track", parsed)
@ -539,7 +548,7 @@ export class OsmConnection {
{
method: "POST",
path: `/api/0.6/notes/${id}/comment?text=${encodeURIComponent(text)}`,
path: `/api/0.6/notes/${id}/comment?text=${encodeURIComponent(text)}`
},
function(err) {
if (err !== null) {
@ -556,7 +565,6 @@ export class OsmConnection {
* To be called by land.html
*/
public finishLogin(callback: (previousURL: string, oauth_token: string) => void) {
console.log(">>> authenticating")
this.auth.authenticate(() => {
// Fully authed at this point
console.log("Authentication successful!")
@ -593,7 +601,7 @@ export class OsmConnection {
*/
singlepage: !this._iframeMode && !AndroidPolyfill.inAndroid.data,
auto: autoLogin,
apiUrl: this._oauth_config.api_url ?? this._oauth_config.url,
apiUrl: this._oauth_config.api_url ?? this._oauth_config.url
})
if (AndroidPolyfill.inAndroid.data) {
this.loginAndroidPolyfill() // NO AWAIT!

View file

@ -1,12 +1,9 @@
import GeocodingProvider, {
SearchResult,
GeocodingOptions,
GeocodeResult,
} from "./GeocodingProvider"
import GeocodingProvider, { GeocodeResult, GeocodingOptions, SearchResult } from "./GeocodingProvider"
import { Utils } from "../../Utils"
import { Store, Stores } from "../UIEventSource"
export default class CombinedSearcher implements GeocodingProvider {
public readonly name = "CombinedSearcher"
private _providers: ReadonlyArray<GeocodingProvider>
private _providersWithSuggest: ReadonlyArray<GeocodingProvider>

View file

@ -2,10 +2,12 @@ import GeocodingProvider, { GeocodeResult } from "./GeocodingProvider"
import { Utils } from "../../Utils"
import { ImmutableStore, Store } from "../UIEventSource"
import CoordinateParser from "coordinate-parser"
/**
* A simple search-class which interprets possible locations
*/
export default class CoordinateSearch implements GeocodingProvider {
public readonly name = "CoordinateSearch"
private static readonly latLonRegexes: ReadonlyArray<RegExp> = [
/^ *(-?[0-9]+\.[0-9]+)[ ,;/\\]+(-?[0-9]+\.[0-9]+)/,
/^ *(-?[0-9]+,[0-9]+)[ ;/\\]+(-?[0-9]+,[0-9]+)/,

View file

@ -49,6 +49,13 @@ export interface GeocodingOptions {
}
export default interface GeocodingProvider {
readonly name: string
/**
* Performs search.
* Note: the result _must_ return an empty list in the case of no results.
* Undefined might be interpreted by clients as "still running"
*/
search(query: string, options?: GeocodingOptions): Promise<GeocodeResult[]>
/**

View file

@ -1,4 +1,4 @@
import GeocodingProvider, { SearchResult, GeocodingOptions } from "./GeocodingProvider"
import GeocodingProvider, { GeocodingOptions, SearchResult } from "./GeocodingProvider"
import ThemeViewState from "../../Models/ThemeViewState"
import { Utils } from "../../Utils"
import { Feature } from "geojson"
@ -20,7 +20,7 @@ type IntermediateResult = {
export default class LocalElementSearch implements GeocodingProvider {
private readonly _state: ThemeViewState
private readonly _limit: number
public readonly name = "LocalElementSearch"
constructor(state: ThemeViewState, limit: number) {
this._state = state
this._limit = limit

View file

@ -8,6 +8,7 @@ import GeocodingProvider, { GeocodingOptions, SearchResult } from "./GeocodingPr
export class NominatimGeocoding implements GeocodingProvider {
private readonly _host
private readonly limit: number
public readonly name = "Nominatim"
constructor(limit: number = 3, host: string = Constants.nominatimEndpoint) {
this.limit = limit

View file

@ -8,7 +8,7 @@ export default class OpenLocationCodeSearch implements GeocodingProvider {
*/
public static readonly _isPlusCode =
/^([2-9CFGHJMPQRVWX]{2}|00){2,4}\+([2-9CFGHJMPQRVWX]{2,3})?$/
public readonly name = "OpenLocationCodeSearch"
/**
*
* OpenLocationCodeSearch.isPlusCode("9FFW84J9+XG") // => true
@ -26,7 +26,7 @@ export default class OpenLocationCodeSearch implements GeocodingProvider {
async search(query: string, options?: GeocodingOptions): Promise<GeocodeResult[]> {
if (!OpenLocationCodeSearch.isPlusCode(query)) {
return undefined
return [] // Must be an empty list and not "undefined", the latter is interpreted as 'still searching'
}
const { latitude, longitude } = pluscode_decode(query)

View file

@ -7,7 +7,7 @@ import OsmObjectDownloader from "../Osm/OsmObjectDownloader"
export default class OpenStreetMapIdSearch implements GeocodingProvider {
private static readonly regex =
/((https?:\/\/)?(www.)?(osm|openstreetmap).org\/)?(n|node|w|way|r|relation)[/ ]?([0-9]+)/
public readonly name = "OpenStreetMapId"
private static readonly types: Readonly<Record<string, "node" | "way" | "relation">> = {
n: "node",
w: "way",

View file

@ -5,7 +5,7 @@ import GeocodingProvider, {
GeocodingOptions,
GeocodingUtils,
ReverseGeocodingProvider,
ReverseGeocodingResult,
ReverseGeocodingResult
} from "./GeocodingProvider"
import { Utils } from "../../Utils"
import { Feature, FeatureCollection } from "geojson"
@ -15,6 +15,7 @@ import { Store, Stores } from "../UIEventSource"
export default class PhotonSearch implements GeocodingProvider, ReverseGeocodingProvider {
private readonly _endpoint: string
public readonly name = "photon"
private supportedLanguages = ["en", "de", "fr"]
private static readonly types = {
R: "relation",

View file

@ -5,7 +5,7 @@ import LayerConfig from "../Models/ThemeConfig/LayerConfig"
import { CountryCoder } from "latlon2country"
import Constants from "../Models/Constants"
import { TagUtils } from "./Tags/TagUtils"
import { Feature, LineString } from "geojson"
import { Feature, LineString, MultiPolygon, Polygon } from "geojson"
import { OsmTags } from "../Models/OsmFeature"
import { UIEventSource } from "./UIEventSource"
import ThemeConfig from "../Models/ThemeConfig/ThemeConfig"
@ -80,7 +80,7 @@ export class ReferencingWaysMetaTagger extends SimpleMetaTagger {
super({
keys: ["_referencing_ways"],
isLazy: true,
doc: "_referencing_ways contains - for a node - which ways use this node as point in their geometry. ",
doc: "_referencing_ways contains - for a node - which ways use this node as point in their geometry. "
})
}
@ -116,7 +116,7 @@ class CountryTagger extends SimpleMetaTagger {
super({
keys: ["_country"],
doc: "The country codes of the of the country/countries that the feature is located in (with latlon2country). Might contain _multiple_ countries, separated by a `;`",
includesDates: false,
includesDates: false
})
}
@ -213,9 +213,9 @@ class RewriteMetaInfoTags extends SimpleMetaTagger {
"_last_edit:changeset",
"_last_edit:timestamp",
"_version_number",
"_backend",
"_backend"
],
doc: "Information about the last edit of this object. This object will actually _rewrite_ some tags for features coming from overpass",
doc: "Information about the last edit of this object. This object will actually _rewrite_ some tags for features coming from overpass"
})
}
@ -244,6 +244,69 @@ class RewriteMetaInfoTags extends SimpleMetaTagger {
}
}
class NormalizePanoramax extends SimpleMetaTagger {
constructor() {
super(
{
keys: ["panoramax"],
doc: "Converts a `panoramax=hash1;hash2;hash3;...` into `panoramax=hash1`,`panoramax:0=hash1`...",
isLazy: false,
cleanupRetagger: true
})
}
private addValue(comesFromKey: string, tags: Record<string, string>, hashesToAdd: string[], postfix?: string) {
let basekey = "panoramax"
if (postfix) {
basekey = "panoramax:" + postfix
}
let index = -1
for (let i = 0; i < hashesToAdd.length; i++) {
let k = basekey
do {
if (index >= 0) {
k = `${basekey}:${index}`
}
index++
} while (k !== comesFromKey && tags[k])
tags[k] = hashesToAdd[i]
}
}
/**
* const tags = new UIEventSource({panoramax: "abc;def;ghi", "panoramax:2": "xyz;uvw", "panoramax:streetsign":"a;b;c"})
* const _ = undefined
* new NormalizePanoramax().applyMetaTagsOnFeature(_, _, tags, _)
* tags.data // => {"panoramax": "abc", "panoramax:0" : "def", "panoramax:1": "ghi", "panoramax:2":"xyz", "panoramax:3":"uvw", "panoramax:streetsign":"a", "panoramax:streetsign:0":"b","panoramax:streetsign:1": "c"}
*/
applyMetaTagsOnFeature(feature: Feature, layer: LayerConfig, tags: UIEventSource<Record<string, string>>): boolean {
const tgs = tags.data
let somethingChanged = false
for (const key in tgs) {
if (!(key === "panoramax" || key.startsWith("panoramax:"))) {
continue
}
const v = tgs[key]
if (v.indexOf(";") < 0) {
continue
}
const parts = v.split(";")
if (key === "panoramax" || key.match("panoramax:[0-9]+")) {
this.addValue(key, tgs, parts)
somethingChanged = true
} else {
const postfix = key.match(/panoramax:([^:]+)(:[0-9]+)?/)?.[1]
if (postfix) {
this.addValue(key, tgs, parts, postfix)
somethingChanged = true
}
}
}
return somethingChanged
}
}
export default class SimpleMetaTaggers {
/**
* A simple metatagger which rewrites various metatags as needed
@ -253,7 +316,7 @@ export default class SimpleMetaTaggers {
public static geometryType = new InlineMetaTagger(
{
keys: ["_geometry:type"],
doc: "Adds the geometry type as property. This is identical to the GoeJson geometry type and is one of `Point`,`LineString`, `Polygon` and exceptionally `MultiPolygon` or `MultiLineString`",
doc: "Adds the geometry type as property. This is identical to the GoeJson geometry type and is one of `Point`,`LineString`, `Polygon` and exceptionally `MultiPolygon` or `MultiLineString`"
},
(feature) => {
const changed = feature.properties["_geometry:type"] === feature.geometry.type
@ -262,6 +325,7 @@ export default class SimpleMetaTaggers {
}
)
public static referencingWays = new ReferencingWaysMetaTagger()
private static normalizePanoramax = new NormalizePanoramax()
private static readonly cardinalDirections = {
N: 0,
NNE: 22.5,
@ -278,12 +342,12 @@ export default class SimpleMetaTaggers {
W: 270,
WNW: 292.5,
NW: 315,
NNW: 337.5,
NNW: 337.5
}
private static latlon = new InlineMetaTagger(
{
keys: ["_lat", "_lon"],
doc: "The latitude and longitude of the point (or centerpoint in the case of a way/area)",
doc: "The latitude and longitude of the point (or centerpoint in the case of a way/area)"
},
(feature) => {
const centerPoint = GeoOperations.centerpoint(feature)
@ -298,7 +362,7 @@ export default class SimpleMetaTaggers {
{
doc: "The layer-id to which this feature belongs. Note that this might be return any applicable if `passAllFeatures` is defined.",
keys: ["_layer"],
includesDates: false,
includesDates: false
},
(feature, layer) => {
if (feature.properties._layer === layer.id) {
@ -314,11 +378,11 @@ export default class SimpleMetaTaggers {
"sidewalk:left",
"sidewalk:right",
"generic_key:left:property",
"generic_key:right:property",
"generic_key:right:property"
],
doc: "Rewrites tags from 'generic_key:both:property' as 'generic_key:left:property' and 'generic_key:right:property' (and similar for sidewalk tagging). Note that this rewritten tags _will be reuploaded on a change_. To prevent to much unrelated retagging, this is only enabled if the layer has at least some lineRenderings with offset defined",
includesDates: false,
cleanupRetagger: true,
cleanupRetagger: true
},
(feature, layer) => {
if (!layer.lineRendering.some((lr) => lr.leftRightSensitive)) {
@ -332,11 +396,15 @@ export default class SimpleMetaTaggers {
{
keys: ["_surface"],
doc: "The surface area of the feature in square meters. Not set on points and ways",
isLazy: true,
isLazy: true
},
(feature) => {
if (feature.geometry.type !== "Polygon" && feature.geometry.type !== "MultiPolygon") {
return
}
const f = <Feature<Polygon | MultiPolygon>>feature
Utils.AddLazyProperty(feature.properties, "_surface", () => {
return "" + GeoOperations.surfaceAreaInSqMeters(feature)
return "" + GeoOperations.surfaceAreaInSqMeters(f)
})
return true
@ -346,11 +414,15 @@ export default class SimpleMetaTaggers {
{
keys: ["_surface:ha"],
doc: "The surface area of the feature in hectare. Not set on points and ways",
isLazy: true,
isLazy: true
},
(feature) => {
if (feature.geometry.type !== "Polygon" && feature.geometry.type !== "MultiPolygon") {
return
}
const f = <Feature<Polygon | MultiPolygon>>feature
Utils.AddLazyProperty(feature.properties, "_surface:ha", () => {
const sqMeters = GeoOperations.surfaceAreaInSqMeters(feature)
const sqMeters = GeoOperations.surfaceAreaInSqMeters(f)
return "" + Math.floor(sqMeters / 1000) / 10
})
@ -360,7 +432,7 @@ export default class SimpleMetaTaggers {
private static levels = new InlineMetaTagger(
{
doc: "Extract the 'level'-tag into a normalized, ';'-separated value called '_level' (which also includes 'repeat_on'). The `level` tag (without underscore) will be normalized with only the value of `level`.",
keys: ["_level"],
keys: ["_level"]
},
(feature) => {
let somethingChanged = false
@ -395,7 +467,7 @@ export default class SimpleMetaTaggers {
private static canonicalize = new InlineMetaTagger(
{
doc: "If 'units' is defined in the layoutConfig, then this metatagger will rewrite the specified keys to have the canonical form (e.g. `1meter` will be rewritten to `1m`; `1` will be rewritten to `1m` as well)",
keys: ["Theme-defined keys"],
keys: ["Theme-defined keys"]
},
(feature, _, __, state) => {
const units = Utils.NoNull(
@ -452,7 +524,7 @@ export default class SimpleMetaTaggers {
private static lngth = new InlineMetaTagger(
{
keys: ["_length", "_length:km"],
doc: "The total length of a feature in meters (and in kilometers, rounded to one decimal for '_length:km'). For a surface, the length of the perimeter",
doc: "The total length of a feature in meters (and in kilometers, rounded to one decimal for '_length:km'). For a surface, the length of the perimeter"
},
(feature) => {
const l = GeoOperations.lengthInMeters(feature)
@ -468,7 +540,7 @@ export default class SimpleMetaTaggers {
keys: ["_isOpen"],
doc: "If 'opening_hours' is present, it will add the current state of the feature (being 'yes' or 'no')",
includesDates: true,
isLazy: true,
isLazy: true
},
(feature) => {
if (Utils.runningFromConsole) {
@ -507,8 +579,8 @@ export default class SimpleMetaTaggers {
lon: lon,
address: {
country_code: tags._country.toLowerCase(),
state: undefined,
},
state: undefined
}
},
<any>{ tag_key: "opening_hours" }
)
@ -520,14 +592,14 @@ export default class SimpleMetaTaggers {
delete tags._isOpen
tags["_isOpen"] = "parse_error"
}
},
}
})
}
)
private static directionSimplified = new InlineMetaTagger(
{
keys: ["_direction:numerical", "_direction:leftright"],
doc: "_direction:numerical is a normalized, numerical direction based on 'camera:direction' or on 'direction'; it is only present if a valid direction is found (e.g. 38.5 or NE). _direction:leftright is either 'left' or 'right', which is left-looking on the map or 'right-looking' on the map",
doc: "_direction:numerical is a normalized, numerical direction based on 'camera:direction' or on 'direction'; it is only present if a valid direction is found (e.g. 38.5 or NE). _direction:leftright is either 'left' or 'right', which is left-looking on the map or 'right-looking' on the map"
},
(feature) => {
const tags = feature.properties
@ -552,7 +624,7 @@ export default class SimpleMetaTaggers {
{
keys: ["_direction:centerpoint"],
isLazy: true,
doc: "_direction:centerpoint is the direction of the linestring (in degrees) if one were standing at the projected centerpoint.",
doc: "_direction:centerpoint is the direction of the linestring (in degrees) if one were standing at the projected centerpoint."
},
(feature: Feature) => {
if (feature.geometry.type !== "LineString") {
@ -575,7 +647,7 @@ export default class SimpleMetaTaggers {
delete feature.properties["_direction:centerpoint"]
feature.properties["_direction:centerpoint"] = bearing
return bearing
},
}
})
return true
@ -585,7 +657,7 @@ export default class SimpleMetaTaggers {
{
keys: ["_now:date", "_now:datetime"],
doc: "Adds the time that the data got loaded - pretty much the time of downloading from overpass. The format is YYYY-MM-DD hh:mm, aka 'sortable' aka ISO-8601-but-not-entirely",
includesDates: true,
includesDates: true
},
(feature) => {
const now = new Date()
@ -609,7 +681,7 @@ export default class SimpleMetaTaggers {
keys: ["_last_edit:passed_time"],
doc: "Gives the number of seconds since the last edit. Note that this will _not_ update, but rather be the number of seconds elapsed at the moment this tag is read first",
isLazy: true,
includesDates: true,
includesDates: true
},
(feature) => {
Utils.AddLazyProperty(feature.properties, "_last_edit:passed_time", () => {
@ -628,7 +700,7 @@ export default class SimpleMetaTaggers {
{
keys: ["_currency"],
doc: "Adds the currency valid for the object, based on country or explicit tagging. Can be a single currency or a semicolon-separated list of currencies. Empty if no currency is found.",
isLazy: true,
isLazy: true
},
(feature: Feature, layer: LayerConfig, tagsStore: UIEventSource<OsmTags>) => {
if (tagsStore === undefined) {
@ -670,6 +742,7 @@ export default class SimpleMetaTaggers {
}
)
public static metatags: SimpleMetaTagger[] = [
SimpleMetaTaggers.latlon,
SimpleMetaTaggers.layerInfo,
@ -689,6 +762,7 @@ export default class SimpleMetaTaggers {
SimpleMetaTaggers.referencingWays,
SimpleMetaTaggers.timeSinceLastEdit,
SimpleMetaTaggers.currency,
SimpleMetaTaggers.normalizePanoramax
]
/**
@ -770,8 +844,8 @@ export default class SimpleMetaTaggers {
[
"Metatags are extra tags available, in order to display more data or to give better questions.",
"They are calculated automatically on every feature when the data arrives in the webbrowser. This document gives an overview of the available metatags.",
"**Hint:** when using metatags, add the [query parameter](URL_Parameters.md) `debug=true` to the URL. This will include a box in the popup for features which shows all the properties of the object",
].join("\n"),
"**Hint:** when using metatags, add the [query parameter](URL_Parameters.md) `debug=true` to the URL. This will include a box in the popup for features which shows all the properties of the object"
].join("\n")
]
subElements.push("## Metatags calculated by MapComplete")

View file

@ -60,7 +60,7 @@ export default class SearchState {
return new ImmutableStore(true)
}
return Stores.concat(suggestions).map((suggestions) =>
suggestions.some((list) => list === undefined)
suggestions.some((list, i) => list === undefined)
)
})
this.suggestions = suggestionsList.bindD((suggestions) =>

View file

@ -350,10 +350,10 @@ export default class UserRelatedState {
* List of all hidden themes that have been seen before
* @param osmConnection
*/
public static initDiscoveredHiddenThemes(osmConnection: OsmConnection): Store<string[]> {
public static initDiscoveredHiddenThemes(osmConnection: OsmConnection): Store<undefined | string[]> {
const prefix = "mapcomplete-hidden-theme-"
const userPreferences = osmConnection.preferencesHandler.allPreferences
return userPreferences.map((preferences) =>
return userPreferences.mapD((preferences) =>
Object.keys(preferences)
.filter((key) => key.startsWith(prefix))
.map((key) => key.substring(prefix.length, key.length - "-enabled".length))
@ -497,7 +497,7 @@ export default class UserRelatedState {
amendedPrefs.ping()
})
osmConnection.userDetails.addCallback((userDetails) => {
osmConnection.userDetails.addCallbackD((userDetails) => {
for (const k in userDetails) {
amendedPrefs.data["_" + k] = "" + userDetails[k]
}