forked from MapComplete/MapComplete
Merge branch 'master' into develop
This commit is contained in:
commit
d71bdecc0c
74 changed files with 2792 additions and 392 deletions
|
@ -14,7 +14,6 @@ import {
|
|||
} from "geojson"
|
||||
import { Tiles } from "../Models/TileRange"
|
||||
import { Utils } from "../Utils"
|
||||
|
||||
;("use strict")
|
||||
|
||||
export class GeoOperations {
|
||||
|
|
|
@ -66,13 +66,13 @@ interface OsmUserInfo {
|
|||
}
|
||||
|
||||
interface UserBlock {
|
||||
"id": number,
|
||||
"created_at": string,
|
||||
"updated_at": string,
|
||||
"ends_at": string,
|
||||
"needs_view": boolean,
|
||||
"user": { "uid": number, "user": string },
|
||||
"creator": { "uid": number, "user": string }
|
||||
id: number
|
||||
created_at: string
|
||||
updated_at: string
|
||||
ends_at: string
|
||||
needs_view: boolean
|
||||
user: { uid: number; user: string }
|
||||
creator: { uid: number; user: string }
|
||||
}
|
||||
|
||||
export default interface UserDetails {
|
||||
|
@ -87,7 +87,7 @@ export default interface UserDetails {
|
|||
account_created: string
|
||||
tracesCount: number
|
||||
description?: string
|
||||
languages: string[],
|
||||
languages: string[]
|
||||
active_blocks: number
|
||||
}
|
||||
export type OsmServiceState = "online" | "readonly" | "offline" | "unknown" | "unreachable"
|
||||
|
@ -320,7 +320,7 @@ export class OsmConnection {
|
|||
account_created: user.account_created,
|
||||
tracesCount: user.traces?.count ?? 0,
|
||||
unreadMessages: user.messages.received?.unread ?? 0,
|
||||
active_blocks: user.blocks.received.active ?? 0
|
||||
active_blocks: user.blocks.received.active ?? 0,
|
||||
}
|
||||
this.userDetails.set(userdetails)
|
||||
this.loadingStatus.setData("logged-in")
|
||||
|
|
|
@ -497,7 +497,10 @@ export default class SimpleMetaTaggers {
|
|||
continue
|
||||
}
|
||||
const value = feature.properties[key]
|
||||
const denom: [string, Denomination] = unit.findDenomination(value, () => feature.properties["_country"])
|
||||
const denom: [string, Denomination] = unit.findDenomination(
|
||||
value,
|
||||
() => feature.properties["_country"]
|
||||
)
|
||||
if (denom === undefined) {
|
||||
// no valid value found
|
||||
break
|
||||
|
@ -515,7 +518,12 @@ export default class SimpleMetaTaggers {
|
|||
if (canonical === value) {
|
||||
break
|
||||
}
|
||||
console.log("Rewritten ", key, ` from '${value}' into '${canonical}' due to denomination`, denomination)
|
||||
console.log(
|
||||
"Rewritten ",
|
||||
key,
|
||||
` from '${value}' into '${canonical}' due to denomination`,
|
||||
denomination
|
||||
)
|
||||
if (canonical === undefined && !unit.eraseInvalid) {
|
||||
break
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@ export default class SearchState {
|
|||
return new ImmutableStore(true)
|
||||
}
|
||||
return Stores.concat(suggestions).map((suggestions) =>
|
||||
suggestions.some(list => list === undefined)
|
||||
suggestions.some((list) => list === undefined)
|
||||
)
|
||||
})
|
||||
this.suggestions = suggestionsList.bindD((suggestions) =>
|
||||
|
@ -101,7 +101,7 @@ export default class SearchState {
|
|||
|
||||
this.showSearchDrawer = new UIEventSource(false)
|
||||
|
||||
this.searchIsFocused.addCallbackAndRunD(sugg => {
|
||||
this.searchIsFocused.addCallbackAndRunD((sugg) => {
|
||||
if (sugg) {
|
||||
this.showSearchDrawer.set(true)
|
||||
}
|
||||
|
@ -192,7 +192,7 @@ export default class SearchState {
|
|||
state.mapProperties.bounds.set(
|
||||
new BBox([
|
||||
[lon0, lat0],
|
||||
[lon1, lat1]
|
||||
[lon1, lat1],
|
||||
]).pad(0.01)
|
||||
)
|
||||
} else {
|
||||
|
|
|
@ -1,14 +1,42 @@
|
|||
import { Utils } from "../../Utils"
|
||||
/** This code is autogenerated - do not edit. Edit ./assets/layers/usersettings/usersettings.json instead */
|
||||
export class ThemeMetaTagging {
|
||||
public static readonly themeName = "usersettings"
|
||||
public static readonly themeName = "usersettings"
|
||||
|
||||
public metaTaggging_for_usersettings(feat: {properties: Record<string, string>}) {
|
||||
Utils.AddLazyProperty(feat.properties, '_mastodon_candidate_md', () => feat.properties._description.match(/\[[^\]]*\]\((.*(mastodon|en.osm.town).*)\).*/)?.at(1) )
|
||||
Utils.AddLazyProperty(feat.properties, '_d', () => feat.properties._description?.replace(/</g,'<')?.replace(/>/g,'>') ?? '' )
|
||||
Utils.AddLazyProperty(feat.properties, '_mastodon_candidate_a', () => (feat => {const e = document.createElement('div');e.innerHTML = feat.properties._d;return Array.from(e.getElementsByTagName("a")).filter(a => a.href.match(/mastodon|en.osm.town/) !== null)[0]?.href }) (feat) )
|
||||
Utils.AddLazyProperty(feat.properties, '_mastodon_link', () => (feat => {const e = document.createElement('div');e.innerHTML = feat.properties._d;return Array.from(e.getElementsByTagName("a")).filter(a => a.getAttribute("rel")?.indexOf('me') >= 0)[0]?.href})(feat) )
|
||||
Utils.AddLazyProperty(feat.properties, '_mastodon_candidate', () => feat.properties._mastodon_candidate_md ?? feat.properties._mastodon_candidate_a )
|
||||
feat.properties['__current_backgroun'] = 'initial_value'
|
||||
}
|
||||
}
|
||||
public metaTaggging_for_usersettings(feat: { properties: Record<string, string> }) {
|
||||
Utils.AddLazyProperty(feat.properties, "_mastodon_candidate_md", () =>
|
||||
feat.properties._description
|
||||
.match(/\[[^\]]*\]\((.*(mastodon|en.osm.town).*)\).*/)
|
||||
?.at(1)
|
||||
)
|
||||
Utils.AddLazyProperty(
|
||||
feat.properties,
|
||||
"_d",
|
||||
() => feat.properties._description?.replace(/</g, "<")?.replace(/>/g, ">") ?? ""
|
||||
)
|
||||
Utils.AddLazyProperty(feat.properties, "_mastodon_candidate_a", () =>
|
||||
((feat) => {
|
||||
const e = document.createElement("div")
|
||||
e.innerHTML = feat.properties._d
|
||||
return Array.from(e.getElementsByTagName("a")).filter(
|
||||
(a) => a.href.match(/mastodon|en.osm.town/) !== null
|
||||
)[0]?.href
|
||||
})(feat)
|
||||
)
|
||||
Utils.AddLazyProperty(feat.properties, "_mastodon_link", () =>
|
||||
((feat) => {
|
||||
const e = document.createElement("div")
|
||||
e.innerHTML = feat.properties._d
|
||||
return Array.from(e.getElementsByTagName("a")).filter(
|
||||
(a) => a.getAttribute("rel")?.indexOf("me") >= 0
|
||||
)[0]?.href
|
||||
})(feat)
|
||||
)
|
||||
Utils.AddLazyProperty(
|
||||
feat.properties,
|
||||
"_mastodon_candidate",
|
||||
() => feat.properties._mastodon_candidate_md ?? feat.properties._mastodon_candidate_a
|
||||
)
|
||||
feat.properties["__current_backgroun"] = "initial_value"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,6 @@ export class TagTypes {
|
|||
return <any>and.and
|
||||
}
|
||||
|
||||
|
||||
static safeOr(or: Or & OptimizedTag): ((FlatTag | (And & OptimizedTag)) & OptimizedTag)[] {
|
||||
return <any>or.or
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ export class TagUtils {
|
|||
["<=", (a, b) => a <= b],
|
||||
[">=", (a, b) => a >= b],
|
||||
["<", (a, b) => a < b],
|
||||
[">", (a, b) => a > b]
|
||||
[">", (a, b) => a > b],
|
||||
]
|
||||
public static modeDocumentation: Record<
|
||||
string,
|
||||
|
@ -48,7 +48,7 @@ export class TagUtils {
|
|||
"### Removing a key\n" +
|
||||
"\n" +
|
||||
"If a key should be deleted in the OpenStreetMap-database, specify `key=` as well. This can be used e.g. to remove a\n" +
|
||||
"fixme or value from another mapping if another field is filled out."
|
||||
"fixme or value from another mapping if another field is filled out.",
|
||||
},
|
||||
"!=": {
|
||||
name: "strict not equals",
|
||||
|
@ -62,7 +62,7 @@ export class TagUtils {
|
|||
"### If key is present\n" +
|
||||
"\n" +
|
||||
"This implies that, to check if a key is present, `key!=` can be used. This will only match if the key is present and not\n" +
|
||||
"empty."
|
||||
"empty.",
|
||||
},
|
||||
"~": {
|
||||
name: "Value matches regex",
|
||||
|
@ -73,12 +73,12 @@ export class TagUtils {
|
|||
"The regex is put within braces as to prevent runaway values.\n" +
|
||||
"\nUse `key~*` to indicate that any value is allowed. This is effectively the check that the attribute is present (defined _and_ not empty)." +
|
||||
"\n" +
|
||||
"Regexes will match the newline character with `.` too - the `s`-flag is enabled by default."
|
||||
"Regexes will match the newline character with `.` too - the `s`-flag is enabled by default.",
|
||||
},
|
||||
"~i~": {
|
||||
name: "Value matches case-invariant regex",
|
||||
overpassSupport: true,
|
||||
docs: "A tag can also be tested against a regex with `key~i~regex`, where the case of the value will be ignored. The regex is still matched against the _entire_ value"
|
||||
docs: "A tag can also be tested against a regex with `key~i~regex`, where the case of the value will be ignored. The regex is still matched against the _entire_ value",
|
||||
},
|
||||
"!~": {
|
||||
name: "Value should _not_ match regex",
|
||||
|
@ -87,27 +87,27 @@ export class TagUtils {
|
|||
"A tag can also be tested against a regex with `key!~regex`. This filter will match if the value does *not* match the regex. " +
|
||||
"\n If the\n" +
|
||||
"value is allowed to appear anywhere as substring, use `key~.*regex.*`.\n" +
|
||||
"The regex is put within braces as to prevent runaway values.\n"
|
||||
"The regex is put within braces as to prevent runaway values.\n",
|
||||
},
|
||||
"!~i~": {
|
||||
name: "Value does *not* match case-invariant regex",
|
||||
overpassSupport: true,
|
||||
docs: "A tag can also be tested against a regex with `key~i~regex`, where the case of the value will be ignored. The regex is still matched against the _entire_ value. This filter returns true if the value does *not* match"
|
||||
docs: "A tag can also be tested against a regex with `key~i~regex`, where the case of the value will be ignored. The regex is still matched against the _entire_ value. This filter returns true if the value does *not* match",
|
||||
},
|
||||
"~~": {
|
||||
name: "Key and value should match given regex",
|
||||
overpassSupport: true,
|
||||
docs: "Both the `key` and `value` part of this specification are interpreted as regexes, both the key and value musth completely match their respective regexes"
|
||||
docs: "Both the `key` and `value` part of this specification are interpreted as regexes, both the key and value musth completely match their respective regexes",
|
||||
},
|
||||
"~i~~": {
|
||||
name: "Key and value should match a given regex; value is case-invariant",
|
||||
overpassSupport: true,
|
||||
docs: "Similar to ~~, except that the value is case-invariant"
|
||||
docs: "Similar to ~~, except that the value is case-invariant",
|
||||
},
|
||||
"!~i~~": {
|
||||
name: "Key and value should match a given regex; value is case-invariant",
|
||||
overpassSupport: true,
|
||||
docs: "Similar to !~~, except that the value is case-invariant"
|
||||
docs: "Similar to !~~, except that the value is case-invariant",
|
||||
},
|
||||
":=": {
|
||||
name: "Substitute `... {some_key} ...` and match `key`",
|
||||
|
@ -133,24 +133,24 @@ export class TagUtils {
|
|||
"\n" +
|
||||
"```json\n" +
|
||||
"{\n" +
|
||||
" \"mappings\": [\n" +
|
||||
' "mappings": [\n' +
|
||||
" {\n" +
|
||||
" \"if\":\"key:={some_other_key}\",\n" +
|
||||
" \"then\": \"...\",\n" +
|
||||
" \"hideInAnswer\": \"some_other_key=\"\n" +
|
||||
' "if":"key:={some_other_key}",\n' +
|
||||
' "then": "...",\n' +
|
||||
' "hideInAnswer": "some_other_key="\n' +
|
||||
" }\n" +
|
||||
" ]\n" +
|
||||
"}\n" +
|
||||
"```\n" +
|
||||
"\n" +
|
||||
"One can use `key!:=prefix-{other_key}-postfix` as well, to match if `key` is _not_ the same\n" +
|
||||
"as `prefix-{other_key}-postfix` (with `other_key` substituted by the value)"
|
||||
"as `prefix-{other_key}-postfix` (with `other_key` substituted by the value)",
|
||||
},
|
||||
"!:=": {
|
||||
name: "Substitute `{some_key}` should not match `key`",
|
||||
overpassSupport: false,
|
||||
docs: "See `:=`, except that this filter is inverted"
|
||||
}
|
||||
docs: "See `:=`, except that this filter is inverted",
|
||||
},
|
||||
}
|
||||
private static keyCounts: { keys: any; tags: any } = key_counts
|
||||
public static readonly numberAndDateComparisonDocs =
|
||||
|
@ -175,10 +175,10 @@ export class TagUtils {
|
|||
"\n" +
|
||||
"```json\n" +
|
||||
"{\n" +
|
||||
" \"osmTags\": {\n" +
|
||||
" \"or\": [\n" +
|
||||
" \"amenity=school\",\n" +
|
||||
" \"amenity=kindergarten\"\n" +
|
||||
' "osmTags": {\n' +
|
||||
' "or": [\n' +
|
||||
' "amenity=school",\n' +
|
||||
' "amenity=kindergarten"\n' +
|
||||
" ]\n" +
|
||||
" }\n" +
|
||||
"}\n" +
|
||||
|
@ -194,7 +194,7 @@ export class TagUtils {
|
|||
"If the schema-files note a type [`TagConfigJson`](https://github.com/pietervdvn/MapComplete/blob/develop/src/Models/ThemeConfig/Json/TagConfigJson.ts), you can use one of these values.\n" +
|
||||
"\n" +
|
||||
"In some cases, not every type of tags-filter can be used. For example, _rendering_ an option with a regex is\n" +
|
||||
"fine (`\"if\": \"brand~[Bb]randname\", \"then\":\" The brand is Brandname\"`); but this regex can not be used to write a value\n" +
|
||||
'fine (`"if": "brand~[Bb]randname", "then":" The brand is Brandname"`); but this regex can not be used to write a value\n' +
|
||||
"into the database. The theme loader will however refuse to work with such inconsistencies and notify you of this while\n" +
|
||||
"you are building your theme.\n" +
|
||||
"\n" +
|
||||
|
@ -205,18 +205,18 @@ export class TagUtils {
|
|||
"\n" +
|
||||
"```json\n" +
|
||||
"{\n" +
|
||||
" \"and\": [\n" +
|
||||
" \"key=value\",\n" +
|
||||
' "and": [\n' +
|
||||
' "key=value",\n' +
|
||||
" {\n" +
|
||||
" \"or\": [\n" +
|
||||
" \"other_key=value\",\n" +
|
||||
" \"other_key=some_other_value\"\n" +
|
||||
' "or": [\n' +
|
||||
' "other_key=value",\n' +
|
||||
' "other_key=some_other_value"\n' +
|
||||
" ]\n" +
|
||||
" },\n" +
|
||||
" \"key_which_should_be_missing=\",\n" +
|
||||
" \"key_which_should_have_a_value~*\",\n" +
|
||||
" \"key~.*some_regex_a*_b+_[a-z]?\",\n" +
|
||||
" \"height<1\"\n" +
|
||||
' "key_which_should_be_missing=",\n' +
|
||||
' "key_which_should_have_a_value~*",\n' +
|
||||
' "key~.*some_regex_a*_b+_[a-z]?",\n' +
|
||||
' "height<1"\n' +
|
||||
" ]\n" +
|
||||
"}\n" +
|
||||
"```\n" +
|
||||
|
@ -986,10 +986,10 @@ export class TagUtils {
|
|||
return ["", "## `" + mode + "` " + doc.name, "", doc.docs, "", ""].join("\n")
|
||||
}),
|
||||
"## " +
|
||||
TagUtils.comparators.map((comparator) => "`" + comparator[0] + "`").join(" ") +
|
||||
" Logical comparators",
|
||||
TagUtils.comparators.map((comparator) => "`" + comparator[0] + "`").join(" ") +
|
||||
" Logical comparators",
|
||||
TagUtils.numberAndDateComparisonDocs,
|
||||
TagUtils.logicalOperator
|
||||
TagUtils.logicalOperator,
|
||||
].join("\n")
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ export class MenuState {
|
|||
"favourites",
|
||||
"usersettings",
|
||||
"share",
|
||||
"menu"
|
||||
"menu",
|
||||
] as const
|
||||
|
||||
/**
|
||||
|
@ -140,7 +140,6 @@ export class MenuState {
|
|||
this.isClosingAll = true
|
||||
const ps = this.pageStates
|
||||
try {
|
||||
|
||||
if (ps.menu.data) {
|
||||
ps.menu.set(false)
|
||||
return true
|
||||
|
@ -177,6 +176,5 @@ export class MenuState {
|
|||
return
|
||||
}
|
||||
MenuState.previewedImage.setData(img)
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,14 @@
|
|||
import { Concat, Conversion, DesugaringContext, DesugaringStep, Each, Fuse, On, Pass, SetDefault } from "./Conversion"
|
||||
import {
|
||||
Concat,
|
||||
Conversion,
|
||||
DesugaringContext,
|
||||
DesugaringStep,
|
||||
Each,
|
||||
Fuse,
|
||||
On,
|
||||
Pass,
|
||||
SetDefault,
|
||||
} from "./Conversion"
|
||||
import { ThemeConfigJson } from "../Json/ThemeConfigJson"
|
||||
import { PrepareLayer, RewriteSpecial } from "./PrepareLayer"
|
||||
import { LayerConfigJson } from "../Json/LayerConfigJson"
|
||||
|
@ -30,7 +40,7 @@ class SubstituteLayer extends Conversion<string | LayerConfigJson, LayerConfigJs
|
|||
const knownLayers = Array.from(state.sharedLayers.keys())
|
||||
const withDistance: [string, number][] = knownLayers.map((lname) => [
|
||||
lname,
|
||||
Utils.levenshteinDistance(name, lname)
|
||||
Utils.levenshteinDistance(name, lname),
|
||||
])
|
||||
withDistance.sort((a, b) => a[1] - b[1])
|
||||
const ids = withDistance.map((n) => n[0])
|
||||
|
@ -120,9 +130,9 @@ class SubstituteLayer extends Conversion<string | LayerConfigJson, LayerConfigJs
|
|||
usedLabels.add(labels[forbiddenLabel])
|
||||
context.info(
|
||||
"Dropping tagRendering " +
|
||||
tr["id"] +
|
||||
" as it has a forbidden label: " +
|
||||
labels[forbiddenLabel]
|
||||
tr["id"] +
|
||||
" as it has a forbidden label: " +
|
||||
labels[forbiddenLabel]
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
@ -140,10 +150,10 @@ class SubstituteLayer extends Conversion<string | LayerConfigJson, LayerConfigJs
|
|||
usedLabels.add(tr["group"])
|
||||
context.info(
|
||||
"Dropping tagRendering " +
|
||||
tr["id"] +
|
||||
" as its group `" +
|
||||
tr["group"] +
|
||||
"` is a forbidden label"
|
||||
tr["id"] +
|
||||
" as its group `" +
|
||||
tr["group"] +
|
||||
"` is a forbidden label"
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
@ -154,8 +164,8 @@ class SubstituteLayer extends Conversion<string | LayerConfigJson, LayerConfigJs
|
|||
if (unused.length > 0) {
|
||||
context.err(
|
||||
"This theme specifies that certain tagrenderings have to be removed based on forbidden layers. One or more of these layers did not match any tagRenderings and caused no deletions: " +
|
||||
unused.join(", ") +
|
||||
"\n This means that this label can be removed or that the original tagRendering that should be deleted does not have this label anymore"
|
||||
unused.join(", ") +
|
||||
"\n This means that this label can be removed or that the original tagRendering that should be deleted does not have this label anymore"
|
||||
)
|
||||
}
|
||||
found.tagRenderings = filtered
|
||||
|
@ -195,10 +205,10 @@ export class AddDefaultLayers extends DesugaringStep<ThemeConfigJson> {
|
|||
if (alreadyLoaded.has(v.id)) {
|
||||
context.warn(
|
||||
"Layout " +
|
||||
context +
|
||||
" already has a layer with name " +
|
||||
v.id +
|
||||
"; skipping inclusion of this builtin layer"
|
||||
context +
|
||||
" already has a layer with name " +
|
||||
v.id +
|
||||
"; skipping inclusion of this builtin layer"
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
@ -342,10 +352,10 @@ class AddDependencyLayersToTheme extends DesugaringStep<ThemeConfigJson> {
|
|||
.enters("layer dependency")
|
||||
.err(
|
||||
"Layer " +
|
||||
dependency.neededLayer +
|
||||
" is loaded because " +
|
||||
dependency.reason +
|
||||
"; so it must specify a `snapName`. This is used in the sentence `move this point to snap it to {snapName}`"
|
||||
dependency.neededLayer +
|
||||
" is loaded because " +
|
||||
dependency.reason +
|
||||
"; so it must specify a `snapName`. This is used in the sentence `move this point to snap it to {snapName}`"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -370,12 +380,12 @@ class AddDependencyLayersToTheme extends DesugaringStep<ThemeConfigJson> {
|
|||
if (dep === undefined) {
|
||||
const message = [
|
||||
"Loading a dependency failed: layer " +
|
||||
unmetDependency.neededLayer +
|
||||
" is not found, neither as layer of " +
|
||||
themeId +
|
||||
" nor as builtin layer.",
|
||||
unmetDependency.neededLayer +
|
||||
" is not found, neither as layer of " +
|
||||
themeId +
|
||||
" nor as builtin layer.",
|
||||
reason,
|
||||
"Loaded layers are: " + alreadyLoaded.map((l) => l.id).join(",")
|
||||
"Loaded layers are: " + alreadyLoaded.map((l) => l.id).join(","),
|
||||
]
|
||||
throw message.join("\n\t")
|
||||
}
|
||||
|
@ -385,7 +395,7 @@ class AddDependencyLayersToTheme extends DesugaringStep<ThemeConfigJson> {
|
|||
dep.description = reason
|
||||
dependenciesToAdd.unshift({
|
||||
config: dep,
|
||||
reason
|
||||
reason,
|
||||
})
|
||||
loadedLayerIds.add(dep.id)
|
||||
unmetDependencies = unmetDependencies.filter(
|
||||
|
@ -430,7 +440,7 @@ class AddDependencyLayersToTheme extends DesugaringStep<ThemeConfigJson> {
|
|||
|
||||
return {
|
||||
...theme,
|
||||
layers: layers
|
||||
layers: layers,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -500,10 +510,10 @@ class WarnForUnsubstitutedLayersInTheme extends DesugaringStep<ThemeConfigJson>
|
|||
|
||||
context.warn(
|
||||
"The theme " +
|
||||
json.id +
|
||||
" has an inline layer: " +
|
||||
layer["id"] +
|
||||
". This is discouraged."
|
||||
json.id +
|
||||
" has an inline layer: " +
|
||||
layer["id"] +
|
||||
". This is discouraged."
|
||||
)
|
||||
}
|
||||
return json
|
||||
|
@ -545,12 +555,12 @@ class PostvalidateTheme extends DesugaringStep<ThemeConfigJson> {
|
|||
if (minZoomAll < layer.minzoom) {
|
||||
context.err(
|
||||
"There are multiple layers based on " +
|
||||
basedOn +
|
||||
". The layer with id " +
|
||||
layer.id +
|
||||
" has a minzoom of " +
|
||||
layer.minzoom +
|
||||
", and has a name set. Another similar layer has a lower minzoom. As such, the layer selection might show 'zoom in to see features' even though some of the features are already visible. Set `\"name\": null` for this layer and eventually remove the 'name':null for the other layer."
|
||||
basedOn +
|
||||
". The layer with id " +
|
||||
layer.id +
|
||||
" has a minzoom of " +
|
||||
layer.minzoom +
|
||||
", and has a name set. Another similar layer has a lower minzoom. As such, the layer selection might show 'zoom in to see features' even though some of the features are already visible. Set `\"name\": null` for this layer and eventually remove the 'name':null for the other layer."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -576,11 +586,11 @@ class PostvalidateTheme extends DesugaringStep<ThemeConfigJson> {
|
|||
.enters("layers", config.id, "filter", "sameAs")
|
||||
.err(
|
||||
"The layer " +
|
||||
config.id +
|
||||
" follows the filter state of layer " +
|
||||
sameAs +
|
||||
", but no layer with this name was found.\n\tDid you perhaps mean one of: " +
|
||||
closeLayers.slice(0, 3).join(", ")
|
||||
config.id +
|
||||
" follows the filter state of layer " +
|
||||
sameAs +
|
||||
", but no layer with this name was found.\n\tDid you perhaps mean one of: " +
|
||||
closeLayers.slice(0, 3).join(", ")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -608,12 +618,16 @@ export class PrepareTheme extends Fuse<ThemeConfigJson> {
|
|||
new SetDefault("socialImage", "assets/SocialImage.png", true),
|
||||
// We expand all tagrenderings first...
|
||||
new On("layers", new Each(new PrepareLayer(state))),
|
||||
new On("popup", new Each(
|
||||
new Fuse("Prepare popups",
|
||||
new On("body", new Each(new RewriteSpecial())),
|
||||
new On("title", new RewriteSpecial())
|
||||
new On(
|
||||
"popup",
|
||||
new Each(
|
||||
new Fuse(
|
||||
"Prepare popups",
|
||||
new On("body", new Each(new RewriteSpecial())),
|
||||
new On("title", new RewriteSpecial())
|
||||
)
|
||||
)
|
||||
)),
|
||||
),
|
||||
|
||||
// Then we apply the override all. We must first expand everything in case that we override something in an expanded tag
|
||||
// Note that it'll cheat with tagRenderings+
|
||||
|
|
|
@ -483,11 +483,11 @@ export interface ThemeConfigJson {
|
|||
*/
|
||||
dismissible?: boolean
|
||||
condition?: TagConfigJson
|
||||
title: TagRenderingConfigJson,
|
||||
body: TagRenderingConfigJson[],
|
||||
title: TagRenderingConfigJson
|
||||
body: TagRenderingConfigJson[]
|
||||
/**
|
||||
* id of the popup, mostly to keep the translations in check
|
||||
*/
|
||||
id: string,
|
||||
id: string
|
||||
}[]
|
||||
}
|
||||
|
|
|
@ -5,7 +5,10 @@ import { TagUtils } from "../../Logic/Tags/TagUtils"
|
|||
import { And } from "../../Logic/Tags/And"
|
||||
import { Utils } from "../../Utils"
|
||||
import { Tag } from "../../Logic/Tags/Tag"
|
||||
import { MappingConfigJson, QuestionableTagRenderingConfigJson } from "./Json/QuestionableTagRenderingConfigJson"
|
||||
import {
|
||||
MappingConfigJson,
|
||||
QuestionableTagRenderingConfigJson,
|
||||
} from "./Json/QuestionableTagRenderingConfigJson"
|
||||
import Validators, { ValidatorType } from "../../UI/InputElement/Validators"
|
||||
import { TagRenderingConfigJson } from "./Json/TagRenderingConfigJson"
|
||||
import { RegexTag } from "../../Logic/Tags/RegexTag"
|
||||
|
@ -223,7 +226,7 @@ export default class TagRenderingConfig {
|
|||
inline: json.freeform.inline ?? false,
|
||||
default: json.freeform.default,
|
||||
postfixDistinguished: json.freeform.postfixDistinguished?.trim(),
|
||||
args: json.freeform.helperArgs
|
||||
args: json.freeform.helperArgs,
|
||||
}
|
||||
if (json.freeform["extraTags"] !== undefined) {
|
||||
throw `Freeform.extraTags is defined. This should probably be 'freeform.addExtraTag' (at ${context})`
|
||||
|
@ -447,7 +450,7 @@ export default class TagRenderingConfig {
|
|||
iconClass,
|
||||
addExtraTags,
|
||||
searchTerms: mapping.searchTerms,
|
||||
priorityIf: prioritySearch
|
||||
priorityIf: prioritySearch,
|
||||
}
|
||||
if (isQuestionable) {
|
||||
if (hideInAnswer !== true && mp.if !== undefined && !mp.if.isUsableAsAnswer()) {
|
||||
|
@ -554,7 +557,7 @@ export default class TagRenderingConfig {
|
|||
then: new TypedTranslation<object>(
|
||||
this.render.replace("{" + this.freeform.key + "}", leftover).translations,
|
||||
this.render.context
|
||||
)
|
||||
),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -607,7 +610,7 @@ export default class TagRenderingConfig {
|
|||
return {
|
||||
then: this.render.PartialSubs({ [this.freeform.key]: v.trim() }),
|
||||
icon: this.renderIcon,
|
||||
iconClass: this.renderIconClass
|
||||
iconClass: this.renderIconClass,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -662,7 +665,7 @@ export default class TagRenderingConfig {
|
|||
key: commonKey,
|
||||
values: Utils.NoNull(
|
||||
values.map((arr) => arr.filter((item) => item.k === commonKey)[0]?.v)
|
||||
)
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -677,7 +680,7 @@ export default class TagRenderingConfig {
|
|||
return {
|
||||
key,
|
||||
type: this.freeform.type,
|
||||
values
|
||||
values,
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Could not create FreeformValues for tagrendering", this.id)
|
||||
|
@ -753,7 +756,7 @@ export default class TagRenderingConfig {
|
|||
const allValues = v.split(";").map((s) => s.trim())
|
||||
const perPostfix: Record<string, string> = {}
|
||||
for (const value of allValues) {
|
||||
const [v, postfix] = value.split("/").map(s => s.trim())
|
||||
const [v, postfix] = value.split("/").map((s) => s.trim())
|
||||
perPostfix[postfix ?? pf] = v.trim()
|
||||
}
|
||||
if (freeformValue === "" || freeformValue === undefined) {
|
||||
|
@ -790,7 +793,7 @@ export default class TagRenderingConfig {
|
|||
// Either no mappings, or this is a radio-button selected freeform value
|
||||
const tag = [
|
||||
new Tag(this.freeform.key, freeformValue),
|
||||
...(this.freeform.addExtraTags ?? [])
|
||||
...(this.freeform.addExtraTags ?? []),
|
||||
]
|
||||
const newProperties = new And(tag).applyOn(currentProperties)
|
||||
if (this.invalidValues?.matchesProperties(newProperties)) {
|
||||
|
@ -814,7 +817,7 @@ export default class TagRenderingConfig {
|
|||
selectedMappings.push(
|
||||
new And([
|
||||
new Tag(this.freeform.key, freeformValue),
|
||||
...(this.freeform.addExtraTags ?? [])
|
||||
...(this.freeform.addExtraTags ?? []),
|
||||
])
|
||||
)
|
||||
}
|
||||
|
@ -847,12 +850,12 @@ export default class TagRenderingConfig {
|
|||
if (useFreeform) {
|
||||
return [
|
||||
new Tag(this.freeform.key, freeformValue),
|
||||
...(this.freeform.addExtraTags ?? [])
|
||||
...(this.freeform.addExtraTags ?? []),
|
||||
]
|
||||
} else if (singleSelectedMapping !== undefined) {
|
||||
return [
|
||||
this.mappings[singleSelectedMapping].if,
|
||||
...(this.mappings[singleSelectedMapping].addExtraTags ?? [])
|
||||
...(this.mappings[singleSelectedMapping].addExtraTags ?? []),
|
||||
]
|
||||
} else {
|
||||
console.error("TagRenderingConfig.ConstructSpecification has a weird fallback for", {
|
||||
|
@ -860,7 +863,7 @@ export default class TagRenderingConfig {
|
|||
singleSelectedMapping,
|
||||
multiSelectedMapping,
|
||||
currentProperties,
|
||||
useFreeform
|
||||
useFreeform,
|
||||
})
|
||||
return undefined
|
||||
}
|
||||
|
@ -889,11 +892,11 @@ export default class TagRenderingConfig {
|
|||
}
|
||||
const msgs: string[] = [
|
||||
icon +
|
||||
" " +
|
||||
"*" +
|
||||
m.then.textFor(lang) +
|
||||
"* is shown if with " +
|
||||
m.if.asHumanString(true, false, {})
|
||||
" " +
|
||||
"*" +
|
||||
m.then.textFor(lang) +
|
||||
"* is shown if with " +
|
||||
m.if.asHumanString(true, false, {}),
|
||||
]
|
||||
|
||||
if (m.hideInAnswer === true) {
|
||||
|
@ -902,7 +905,7 @@ export default class TagRenderingConfig {
|
|||
if (m.ifnot !== undefined) {
|
||||
msgs.push(
|
||||
"Unselecting this answer will add " +
|
||||
m.ifnot.asHumanString(true, false, {})
|
||||
m.ifnot.asHumanString(true, false, {})
|
||||
)
|
||||
}
|
||||
return msgs.join(". ")
|
||||
|
@ -926,7 +929,7 @@ export default class TagRenderingConfig {
|
|||
if (this.labels?.length > 0) {
|
||||
labels = [
|
||||
"This tagrendering has labels ",
|
||||
...this.labels.map((label) => "`" + label + "`")
|
||||
...this.labels.map((label) => "`" + label + "`"),
|
||||
].join("\n")
|
||||
}
|
||||
|
||||
|
@ -939,7 +942,7 @@ export default class TagRenderingConfig {
|
|||
freeform,
|
||||
mappings,
|
||||
condition,
|
||||
labels
|
||||
labels,
|
||||
].join("\n")
|
||||
}
|
||||
|
||||
|
@ -983,7 +986,7 @@ export default class TagRenderingConfig {
|
|||
if (part.indexOf("/") < 0) {
|
||||
continue
|
||||
}
|
||||
const [v, denom] = part.split("/").map(s => s.trim())
|
||||
const [v, denom] = part.split("/").map((s) => s.trim())
|
||||
if (denom === distinguish) {
|
||||
return v
|
||||
}
|
||||
|
@ -1043,19 +1046,21 @@ export default class TagRenderingConfig {
|
|||
/**
|
||||
* Gives all the tags that should be applied to "reset" the freeform key to an "unknown" state
|
||||
*/
|
||||
public markUnknown(layer: LayerConfig, currentProperties: Record<string, string>): UploadableTag[] {
|
||||
public markUnknown(
|
||||
layer: LayerConfig,
|
||||
currentProperties: Record<string, string>
|
||||
): UploadableTag[] {
|
||||
if (this.freeform?.postfixDistinguished) {
|
||||
const v = currentProperties[this.freeform.key] ?? ""
|
||||
const allValues = v.split(";").filter(
|
||||
part => part.split("/")[1]?.trim() !== this.freeform.postfixDistinguished
|
||||
)
|
||||
const allValues = v
|
||||
.split(";")
|
||||
.filter((part) => part.split("/")[1]?.trim() !== this.freeform.postfixDistinguished)
|
||||
return [new Tag(this.freeform.key, allValues.join(";"))]
|
||||
}
|
||||
|
||||
const keys = this.removeToSetUnknown(layer, currentProperties)
|
||||
|
||||
|
||||
return keys?.map(k => new Tag(k, ""))
|
||||
return keys?.map((k) => new Tag(k, ""))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1095,7 +1100,7 @@ export class TagRenderingConfigUtils {
|
|||
clone.mappings?.map((m) => {
|
||||
const mapping = {
|
||||
...m,
|
||||
priorityIf: m.priorityIf ?? TagUtils.Tag("id~*")
|
||||
priorityIf: m.priorityIf ?? TagUtils.Tag("id~*"),
|
||||
}
|
||||
if (m.if.usedKeys().indexOf("nobrand") < 0) {
|
||||
// Erase 'nobrand=yes', unless this option explicitly sets it
|
||||
|
|
|
@ -98,10 +98,10 @@ export default class ThemeConfig implements ThemeInformation {
|
|||
public readonly enableCache: boolean
|
||||
|
||||
public readonly popups: Readonly<{
|
||||
id: string,
|
||||
dismissible?: boolean,
|
||||
condition: TagsFilter,
|
||||
title: TagRenderingConfig,
|
||||
id: string
|
||||
dismissible?: boolean
|
||||
condition: TagsFilter
|
||||
title: TagRenderingConfig
|
||||
body: TagRenderingConfig[]
|
||||
}>[]
|
||||
|
||||
|
@ -205,7 +205,7 @@ export default class ThemeConfig implements ThemeInformation {
|
|||
icon: "./assets/svg/pop-out.svg",
|
||||
href: "https://{basepath}/{theme}.html?lat={lat}&lon={lon}&z={zoom}&language={language}",
|
||||
newTab: true,
|
||||
requirements: ["iframe", "no-welcome-message"]
|
||||
requirements: ["iframe", "no-welcome-message"],
|
||||
},
|
||||
context + ".extraLink"
|
||||
)
|
||||
|
@ -213,7 +213,7 @@ export default class ThemeConfig implements ThemeInformation {
|
|||
this.popups = (json.popup ?? []).map((p, i) => {
|
||||
const ctx = context + ".popup." + i
|
||||
if (!p.id) {
|
||||
throw (ctx + ": an id is required")
|
||||
throw ctx + ": an id is required"
|
||||
}
|
||||
const body: TagRenderingConfigJson[] = Array.isArray(p.body) ? p.body : [p.body]
|
||||
return {
|
||||
|
@ -221,7 +221,7 @@ export default class ThemeConfig implements ThemeInformation {
|
|||
dismissible: p.dismissible ?? false,
|
||||
condition: TagUtils.Tag(p.condition),
|
||||
title: new TagRenderingConfig(p.title, ctx + ".title"),
|
||||
body: body.map((body, i) => new TagRenderingConfig(body, ctx + ".body." + i))
|
||||
body: body.map((body, i) => new TagRenderingConfig(body, ctx + ".body." + i)),
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -378,7 +378,7 @@ export default class ThemeConfig implements ThemeInformation {
|
|||
// The 'favourite'-layer contains pretty much all images as it bundles all layers, so we exclude it
|
||||
const jsonNoFavourites = {
|
||||
...json,
|
||||
layers: json.layers.filter((l) => l["id"] !== "favourite")
|
||||
layers: json.layers.filter((l) => l["id"] !== "favourite"),
|
||||
}
|
||||
const usedImages = jsonNoFavourites._usedImages
|
||||
usedImages.sort()
|
||||
|
|
|
@ -24,11 +24,13 @@
|
|||
let dialogClass =
|
||||
"fixed top-0 start-0 end-0 h-modal inset-0 w-full p-4 flex border-none " + zIndex
|
||||
|
||||
export let backdropClass = "fixed inset-0 z-40 bg-gray-900 bg-opacity-50 dark:bg-opacity-80 frozen-glass"
|
||||
export let backdropClass =
|
||||
"fixed inset-0 z-40 bg-gray-900 bg-opacity-50 dark:bg-opacity-80 frozen-glass"
|
||||
if (fullscreen) {
|
||||
dialogClass += " h-full-child"
|
||||
}
|
||||
let bodyClass = bodyPadding + " h-full space-y-4 flex-1 overflow-y-auto overscroll-contain background-normal"
|
||||
let bodyClass =
|
||||
bodyPadding + " h-full space-y-4 flex-1 overflow-y-auto overscroll-contain background-normal"
|
||||
|
||||
let headerClass = "flex justify-between items-center p-2 px-4 md:px-5 rounded-t-lg"
|
||||
if (!$$slots.header) {
|
||||
|
@ -42,7 +44,7 @@
|
|||
|
||||
<Modal
|
||||
open={_shown}
|
||||
on:close={() =>shown.set(false)}
|
||||
on:close={() => shown.set(false)}
|
||||
outsideclose={dismissable}
|
||||
size="xl"
|
||||
{dismissable}
|
||||
|
|
|
@ -88,8 +88,7 @@
|
|||
image={providedImage}
|
||||
imgClass="max-h-64 w-auto sm:h-32 md:h-64"
|
||||
attributionFormat="minimal"
|
||||
>
|
||||
</AttributedImage>
|
||||
/>
|
||||
<LoginToggle {state} silentFail={true}>
|
||||
{#if linkable}
|
||||
<label>
|
||||
|
|
|
@ -24,16 +24,20 @@
|
|||
|
||||
let enableLogin = state.featureSwitches.featureSwitchEnableLogin
|
||||
export let shown = new UIEventSource(false)
|
||||
onDestroy(MenuState.nearbyImagesFeature.addCallback(something => {
|
||||
if (something !== feature) {
|
||||
shown.set(false)
|
||||
}
|
||||
}))
|
||||
onDestroy(shown.addCallbackAndRun(isShown => {
|
||||
if (isShown) {
|
||||
MenuState.nearbyImagesFeature.set(feature)
|
||||
}
|
||||
}))
|
||||
onDestroy(
|
||||
MenuState.nearbyImagesFeature.addCallback((something) => {
|
||||
if (something !== feature) {
|
||||
shown.set(false)
|
||||
}
|
||||
})
|
||||
)
|
||||
onDestroy(
|
||||
shown.addCallbackAndRun((isShown) => {
|
||||
if (isShown) {
|
||||
MenuState.nearbyImagesFeature.set(feature)
|
||||
}
|
||||
})
|
||||
)
|
||||
</script>
|
||||
|
||||
{#if enableLogin.data}
|
||||
|
|
|
@ -56,13 +56,13 @@
|
|||
let searchIsRunning = new UIEventSource(false)
|
||||
let maplibremap: MapLibreAdaptor = new MapLibreAdaptor(map, {
|
||||
zoom,
|
||||
location: new UIEventSource<{ lon: number; lat: number }>({ lat: lat.data, lon: lon.data })
|
||||
location: new UIEventSource<{ lon: number; lat: number }>({ lat: lat.data, lon: lon.data }),
|
||||
})
|
||||
maplibremap.location.stabilized(500).addCallbackAndRunD((l) => {
|
||||
lat.set(l.lat)
|
||||
lon.set(l.lon)
|
||||
})
|
||||
let searchSuggestions = searchvalue.bindD(search => {
|
||||
let searchSuggestions = searchvalue.bindD((search) => {
|
||||
searchIsRunning.set(true)
|
||||
try {
|
||||
return UIEventSource.FromPromise(geocoder.search(search))
|
||||
|
@ -79,8 +79,8 @@
|
|||
showSearchDrawer,
|
||||
applyGeocodeResult(geocodeResult: GeocodeResult) {
|
||||
maplibremap.location.set({ lon: geocodeResult.lon, lat: geocodeResult.lat })
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
let allLayers = HistoryUtils.personalTheme.layers
|
||||
let layersNoFixme = allLayers.filter((l) => l.id !== "fixme")
|
||||
|
@ -94,7 +94,7 @@
|
|||
Utils.waitFor(200).then(() => {
|
||||
selectedElement.set(f)
|
||||
})
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
let osmConnection = new OsmConnection()
|
||||
|
@ -126,7 +126,7 @@
|
|||
inspectedData.push({
|
||||
label: undefined,
|
||||
visitedTime: new Date().toISOString(),
|
||||
name: user
|
||||
name: user,
|
||||
})
|
||||
}
|
||||
inspectedContributors.ping()
|
||||
|
@ -136,7 +136,7 @@
|
|||
featuresStore.set([])
|
||||
const overpass = new Overpass(
|
||||
undefined,
|
||||
user.split(";").map((user) => "nw(user_touched:\"" + user + "\");"),
|
||||
user.split(";").map((user) => 'nw(user_touched:"' + user + '");'),
|
||||
Constants.defaultOverpassUrls[0]
|
||||
)
|
||||
if (!maplibremap.bounds.data) {
|
||||
|
@ -172,7 +172,12 @@
|
|||
<h1 class="m-0 mx-2 flex-shrink-0">
|
||||
<Tr t={t.title} />
|
||||
</h1>
|
||||
<ValidatedInput placeholder={t.previouslySpied.username} type="string" value={username} on:submit={() => load()} />
|
||||
<ValidatedInput
|
||||
placeholder={t.previouslySpied.username}
|
||||
type="string"
|
||||
value={username}
|
||||
on:submit={() => load()}
|
||||
/>
|
||||
{#if loadingData}
|
||||
<Loading />
|
||||
{:else}
|
||||
|
@ -242,12 +247,14 @@
|
|||
</Drawer>
|
||||
{/if}
|
||||
|
||||
|
||||
<div class="relative m-1 flex-grow overflow-hidden rounded-xl">
|
||||
<MaplibreMap {map} mapProperties={maplibremap} autorecovery={true} />
|
||||
<div class="absolute right-0 top-0 w-1/4 p-4">
|
||||
<Searchbar isFocused={searchIsFocussed} value={searchvalue}
|
||||
on:focus={() => state.searchState.showSearchDrawer.set(true)} />
|
||||
<Searchbar
|
||||
isFocused={searchIsFocussed}
|
||||
value={searchvalue}
|
||||
on:focus={() => state.searchState.showSearchDrawer.set(true)}
|
||||
/>
|
||||
{#if $searchSuggestions?.length > 0 || $searchIsFocussed}
|
||||
<GeocodeResults {state} />
|
||||
{/if}
|
||||
|
|
|
@ -24,13 +24,13 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
|
|||
"dragRotate",
|
||||
"dragPan",
|
||||
"keyboard",
|
||||
"touchZoomRotate"
|
||||
"touchZoomRotate",
|
||||
]
|
||||
private static maplibre_zoom_handlers = [
|
||||
"scrollZoom",
|
||||
"boxZoom",
|
||||
"doubleClickZoom",
|
||||
"touchZoomRotate"
|
||||
"touchZoomRotate",
|
||||
]
|
||||
readonly location: UIEventSource<{ lon: number; lat: number }>
|
||||
private readonly isFlying = new UIEventSource(false)
|
||||
|
@ -44,14 +44,14 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
|
|||
readonly lastClickLocation: Store<
|
||||
| undefined
|
||||
| {
|
||||
lon: number
|
||||
lat: number
|
||||
mode: "left" | "right" | "middle"
|
||||
/**
|
||||
* The nearest feature from a MapComplete layer
|
||||
*/
|
||||
nearestFeature?: Feature
|
||||
}
|
||||
lon: number
|
||||
lat: number
|
||||
mode: "left" | "right" | "middle"
|
||||
/**
|
||||
* The nearest feature from a MapComplete layer
|
||||
*/
|
||||
nearestFeature?: Feature
|
||||
}
|
||||
>
|
||||
readonly minzoom: UIEventSource<number>
|
||||
readonly maxzoom: UIEventSource<number>
|
||||
|
@ -141,7 +141,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
|
|||
const features = map
|
||||
.queryRenderedFeatures([
|
||||
[point.x - buffer, point.y - buffer],
|
||||
[point.x + buffer, point.y + buffer]
|
||||
[point.x + buffer, point.y + buffer],
|
||||
])
|
||||
.filter((f) => f.source.startsWith("mapcomplete_"))
|
||||
if (features.length === 1) {
|
||||
|
@ -281,9 +281,9 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
|
|||
return {
|
||||
map: mlmap,
|
||||
ui: new SvelteUIElement(MaplibreMap, {
|
||||
map: mlmap
|
||||
map: mlmap,
|
||||
}),
|
||||
mapproperties: new MapLibreAdaptor(mlmap)
|
||||
mapproperties: new MapLibreAdaptor(mlmap),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -347,7 +347,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
|
|||
) {
|
||||
const event = {
|
||||
date: new Date(),
|
||||
key: key
|
||||
key: key,
|
||||
}
|
||||
|
||||
for (let i = 0; i < this._onKeyNavigation.length; i++) {
|
||||
|
@ -536,7 +536,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
|
|||
const bounds = map.getBounds()
|
||||
const bbox = new BBox([
|
||||
[bounds.getEast(), bounds.getNorth()],
|
||||
[bounds.getWest(), bounds.getSouth()]
|
||||
[bounds.getWest(), bounds.getSouth()],
|
||||
])
|
||||
if (this.bounds.data === undefined || !isSetup) {
|
||||
this.bounds.setData(bbox)
|
||||
|
@ -732,14 +732,14 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
|
|||
type: "raster-dem",
|
||||
url:
|
||||
"https://api.maptiler.com/tiles/terrain-rgb/tiles.json?key=" +
|
||||
Constants.maptilerApiKey
|
||||
Constants.maptilerApiKey,
|
||||
})
|
||||
try {
|
||||
while (!map?.isStyleLoaded()) {
|
||||
await Utils.waitFor(250)
|
||||
}
|
||||
map.setTerrain({
|
||||
source: id
|
||||
source: id,
|
||||
})
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
|
@ -764,7 +764,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
|
|||
if (this.scaleControl === undefined) {
|
||||
this.scaleControl = new ScaleControl({
|
||||
maxWidth: 100,
|
||||
unit: "metric"
|
||||
unit: "metric",
|
||||
})
|
||||
}
|
||||
if (!map.hasControl(this.scaleControl)) {
|
||||
|
@ -777,7 +777,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
|
|||
window.requestAnimationFrame(() => {
|
||||
this._maplibreMap.data?.flyTo({
|
||||
zoom,
|
||||
center: [lon, lat]
|
||||
center: [lon, lat],
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -593,7 +593,10 @@ export default class ShowDataLayer {
|
|||
try {
|
||||
map.resize()
|
||||
} catch (e) {
|
||||
console.error("Could not resize the map in preparation of zoomToCurrentFeatures; the error is:", e)
|
||||
console.error(
|
||||
"Could not resize the map in preparation of zoomToCurrentFeatures; the error is:",
|
||||
e
|
||||
)
|
||||
}
|
||||
map.fitBounds(bbox.toLngLat(), {
|
||||
padding: { top: 10, bottom: 10, left: 10, right: 10 },
|
||||
|
|
|
@ -69,7 +69,9 @@
|
|||
/**
|
||||
* The tags to apply to mark this answer as "unknown"
|
||||
*/
|
||||
let onMarkUnknown: Store<UploadableTag[] | undefined> = tags.mapD((tags) => config.markUnknown(layer, tags))
|
||||
let onMarkUnknown: Store<UploadableTag[] | undefined> = tags.mapD((tags) =>
|
||||
config.markUnknown(layer, tags)
|
||||
)
|
||||
let unknownModal = new UIEventSource(false)
|
||||
|
||||
let searchTerm: UIEventSource<string> = new UIEventSource("")
|
||||
|
@ -120,7 +122,7 @@
|
|||
seenFreeforms.push(newProps[confg.freeform.key])
|
||||
}
|
||||
return matches
|
||||
})
|
||||
}),
|
||||
]
|
||||
|
||||
if (tgs !== undefined && confg.freeform) {
|
||||
|
@ -229,7 +231,7 @@
|
|||
freeform: $freeformInput,
|
||||
selectedMapping,
|
||||
checkedMappings,
|
||||
currentTags: tags.data
|
||||
currentTags: tags.data,
|
||||
},
|
||||
" --> ",
|
||||
selectedTags
|
||||
|
@ -290,7 +292,7 @@
|
|||
dispatch("saved", { config, applied: selectedTagsJoined })
|
||||
const change = new ChangeTagAction(tags.data.id, selectedTagsJoined, tags.data, {
|
||||
theme: tags.data["_orig_theme"] ?? state.theme?.id,
|
||||
changeType: "answer"
|
||||
changeType: "answer",
|
||||
})
|
||||
freeformInput.set(undefined)
|
||||
selectedMapping = undefined
|
||||
|
@ -334,7 +336,7 @@
|
|||
const tagsToSet: UploadableTag[] = onMarkUnknown.data
|
||||
const change = new ChangeTagAction(tags.data.id, new And(tagsToSet), tags.data, {
|
||||
theme: tags.data["_orig_theme"] ?? state.theme.id,
|
||||
changeType: "answer"
|
||||
changeType: "answer",
|
||||
})
|
||||
freeformInput.set(undefined)
|
||||
selectedMapping = undefined
|
||||
|
@ -581,7 +583,7 @@
|
|||
>
|
||||
<div class="subtle">
|
||||
<Tr t={Translations.t.unknown.removedKeys} />
|
||||
<TagHint tags={$onMarkUnknown}></TagHint>
|
||||
<TagHint tags={$onMarkUnknown} />
|
||||
</div>
|
||||
</If>
|
||||
<div class="flex w-full justify-end" slot="footer">
|
||||
|
@ -613,13 +615,8 @@
|
|||
<!-- TagRenderingQuestion-buttons -->
|
||||
<slot name="cancel" />
|
||||
<slot name="save-button" {selectedTags}>
|
||||
|
||||
<!-- Save-button / delete button -->
|
||||
{#if config.freeform?.key &&
|
||||
!checkedMappings?.some((m) => m) &&
|
||||
!$freeformInput && !$freeformInputUnvalidated
|
||||
&& $tags[config.freeform.key]
|
||||
&& $isKnown}
|
||||
{#if config.freeform?.key && !checkedMappings?.some((m) => m) && !$freeformInput && !$freeformInputUnvalidated && $tags[config.freeform.key] && $isKnown}
|
||||
<button
|
||||
class="primary flex"
|
||||
on:click|stopPropagation|preventDefault={() => onSave()}
|
||||
|
@ -652,7 +649,10 @@
|
|||
</div>
|
||||
{/if}
|
||||
{#if $featureSwitchIsTesting || $featureSwitchIsDebugging}
|
||||
<button class="small as-link" on:click={() => console.log("Configuration is ", config)}>
|
||||
<button
|
||||
class="small as-link"
|
||||
on:click={() => console.log("Configuration is ", config)}
|
||||
>
|
||||
{config.id}
|
||||
</button>
|
||||
{/if}
|
||||
|
|
|
@ -16,9 +16,9 @@
|
|||
|
||||
export let entry: GeocodeResult
|
||||
export let state: {
|
||||
mapProperties: MapProperties,
|
||||
theme?: ThemeConfig,
|
||||
featureProperties?: FeaturePropertiesStore,
|
||||
mapProperties: MapProperties
|
||||
theme?: ThemeConfig
|
||||
featureProperties?: FeaturePropertiesStore
|
||||
searchState: Partial<SearchState>
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,8 @@
|
|||
searchTerm: UIEventSource<string>
|
||||
suggestions: Store<GeocodeResult[]>
|
||||
suggestionsSearchRunning: Store<boolean>
|
||||
}, mapProperties: MapProperties
|
||||
}
|
||||
mapProperties: MapProperties
|
||||
}
|
||||
|
||||
let searchTerm = state.searchState.searchTerm
|
||||
|
|
|
@ -57,7 +57,6 @@
|
|||
{/if}
|
||||
<GeocodeResults {state}>
|
||||
<svelte:fragment slot="if-no-results">
|
||||
|
||||
{#if $recentlySeen?.length > 0}
|
||||
<SidebarUnit>
|
||||
<div class="flex justify-between">
|
||||
|
@ -67,8 +66,8 @@
|
|||
<DotMenu>
|
||||
<button
|
||||
on:click={() => {
|
||||
state.userRelatedState.recentlyVisitedSearch.clear()
|
||||
}}
|
||||
state.userRelatedState.recentlyVisitedSearch.clear()
|
||||
}}
|
||||
>
|
||||
<TrashIcon />
|
||||
<Tr t={Translations.t.general.search.deleteSearchHistory} />
|
||||
|
@ -85,7 +84,6 @@
|
|||
</SidebarUnit>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
|
||||
</GeocodeResults>
|
||||
|
||||
{#if $allowOtherThemes}
|
||||
|
|
|
@ -111,13 +111,16 @@ export class SettingsVisualisations {
|
|||
},
|
||||
{
|
||||
funcName: "login_button",
|
||||
args: [{
|
||||
name: "force",
|
||||
doc: "Always show this button, even if logged in"
|
||||
}, {
|
||||
name: "message",
|
||||
doc: "Message to display on the button"
|
||||
}],
|
||||
args: [
|
||||
{
|
||||
name: "force",
|
||||
doc: "Always show this button, even if logged in",
|
||||
},
|
||||
{
|
||||
name: "message",
|
||||
doc: "Message to display on the button",
|
||||
},
|
||||
],
|
||||
docs: "Show a login button",
|
||||
needsUrls: [],
|
||||
group: "settings",
|
||||
|
@ -130,7 +133,7 @@ export class SettingsVisualisations {
|
|||
return new SvelteUIElement(LoginButton, {
|
||||
osmConnection: state.osmConnection,
|
||||
msg,
|
||||
forceShow: force === "yes" || force === "true"
|
||||
forceShow: force === "yes" || force === "true",
|
||||
})
|
||||
},
|
||||
},
|
||||
|
|
|
@ -151,7 +151,7 @@
|
|||
const bottomRight = mlmap.unproject([rect.right, rect.bottom])
|
||||
const bbox = new BBox([
|
||||
[topLeft.lng, topLeft.lat],
|
||||
[bottomRight.lng, bottomRight.lat]
|
||||
[bottomRight.lng, bottomRight.lat],
|
||||
])
|
||||
state.visualFeedbackViewportBounds.setData(bbox)
|
||||
}
|
||||
|
@ -506,16 +506,31 @@
|
|||
{#each theme.popups as popup}
|
||||
{#if popup.condition.matchesProperties($metatags)}
|
||||
<Popup shown={new UIEventSource(true)} dismissable={popup.dismissible}>
|
||||
<TagRenderingAnswer slot="header" config={popup.title} {state}
|
||||
tags={metatags}
|
||||
layer={undefined}
|
||||
selectedElement={({type: "Feature", properties: $metatags, geometry: {type: "Point", coordinates: [0,0]}})} />
|
||||
<TagRenderingAnswer
|
||||
slot="header"
|
||||
config={popup.title}
|
||||
{state}
|
||||
tags={metatags}
|
||||
layer={undefined}
|
||||
selectedElement={{
|
||||
type: "Feature",
|
||||
properties: $metatags,
|
||||
geometry: { type: "Point", coordinates: [0, 0] },
|
||||
}}
|
||||
/>
|
||||
<div class="flex flex-col">
|
||||
{#each popup.body as body}
|
||||
<TagRenderingAnswer config={body} {state}
|
||||
tags={metatags}
|
||||
layer={undefined}
|
||||
selectedElement={({type: "Feature", properties: $metatags, geometry: {type: "Point", coordinates: [0,0]}})} />
|
||||
<TagRenderingAnswer
|
||||
config={body}
|
||||
{state}
|
||||
tags={metatags}
|
||||
layer={undefined}
|
||||
selectedElement={{
|
||||
type: "Feature",
|
||||
properties: $metatags,
|
||||
geometry: { type: "Point", coordinates: [0, 0] },
|
||||
}}
|
||||
/>
|
||||
{/each}
|
||||
<span class="subtle">{popup.id}</span>
|
||||
</div>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"contributors": [
|
||||
{
|
||||
"commits": 9310,
|
||||
"commits": 9369,
|
||||
"contributor": "Pieter Vander Vennet"
|
||||
},
|
||||
{
|
||||
|
@ -48,6 +48,10 @@
|
|||
"commits": 29,
|
||||
"contributor": "riQQ"
|
||||
},
|
||||
{
|
||||
"commits": 26,
|
||||
"contributor": "Osmwithspace"
|
||||
},
|
||||
{
|
||||
"commits": 26,
|
||||
"contributor": "Joost"
|
||||
|
@ -60,10 +64,6 @@
|
|||
"commits": 24,
|
||||
"contributor": "Ward"
|
||||
},
|
||||
{
|
||||
"commits": 21,
|
||||
"contributor": "Osmwithspace"
|
||||
},
|
||||
{
|
||||
"commits": 21,
|
||||
"contributor": "wjtje"
|
||||
|
@ -148,6 +148,10 @@
|
|||
"commits": 8,
|
||||
"contributor": "Mateusz Konieczny"
|
||||
},
|
||||
{
|
||||
"commits": 7,
|
||||
"contributor": "Bastian Greshake Tzovaras"
|
||||
},
|
||||
{
|
||||
"commits": 7,
|
||||
"contributor": "OliNau"
|
||||
|
@ -280,6 +284,10 @@
|
|||
"commits": 2,
|
||||
"contributor": "Stanislas Gueniffey"
|
||||
},
|
||||
{
|
||||
"commits": 1,
|
||||
"contributor": "Bastian-Greshake-Tzovaras"
|
||||
},
|
||||
{
|
||||
"commits": 1,
|
||||
"contributor": "Weblate Admin"
|
||||
|
|
|
@ -181,9 +181,9 @@
|
|||
"ar"
|
||||
],
|
||||
"ER": [
|
||||
"ti",
|
||||
"en",
|
||||
"ar",
|
||||
"ti"
|
||||
"ar"
|
||||
],
|
||||
"ES": [
|
||||
"es",
|
||||
|
|
|
@ -11432,6 +11432,10 @@
|
|||
"if": "value=waste_disposal",
|
||||
"then": "waste_disposal - Waste Disposal Bin, medium to large bin for disposal of (household) waste"
|
||||
},
|
||||
{
|
||||
"if": "value=wayside_shrine",
|
||||
"then": "wayside_shrine - Shrines are religious places that are dedicated to specific deities, saints and other figures of religious importance. Typically, the contain religious depictions and people frequently leave offerings at those places. Wayside shrines are small shrines that can be found next to a road or pathway and are frequented by travellers passing by."
|
||||
},
|
||||
{
|
||||
"if": "value=windturbine",
|
||||
"then": "windturbine - Modern windmills generating electricity"
|
||||
|
|
|
@ -1311,6 +1311,10 @@
|
|||
"if": "value=waste_disposal",
|
||||
"then": "<b>waste_disposal</b> (builtin) - Waste Disposal Bin, medium to large bin for disposal of (household) waste"
|
||||
},
|
||||
{
|
||||
"if": "value=wayside_shrine",
|
||||
"then": "<b>wayside_shrine</b> (builtin) - Shrines are religious places that are dedicated to specific deities, saints and other figures of religious importance. Typically, the contain religious depictions and people frequently leave offerings at those places. Wayside shrines are small shrines that can be found next to a road or pathway and are frequented by travellers passing by."
|
||||
},
|
||||
{
|
||||
"if": "value=windturbine",
|
||||
"then": "<b>windturbine</b> (builtin) - Modern windmills generating electricity"
|
||||
|
@ -14059,6 +14063,10 @@
|
|||
"if": "value=waste_disposal",
|
||||
"then": "waste_disposal - Waste Disposal Bin, medium to large bin for disposal of (household) waste"
|
||||
},
|
||||
{
|
||||
"if": "value=wayside_shrine",
|
||||
"then": "wayside_shrine - Shrines are religious places that are dedicated to specific deities, saints and other figures of religious importance. Typically, the contain religious depictions and people frequently leave offerings at those places. Wayside shrines are small shrines that can be found next to a road or pathway and are frequented by travellers passing by."
|
||||
},
|
||||
{
|
||||
"if": "value=windturbine",
|
||||
"then": "windturbine - Modern windmills generating electricity"
|
||||
|
@ -35843,6 +35851,10 @@
|
|||
"if": "value=waste_disposal",
|
||||
"then": "waste_disposal - Waste Disposal Bin, medium to large bin for disposal of (household) waste"
|
||||
},
|
||||
{
|
||||
"if": "value=wayside_shrine",
|
||||
"then": "wayside_shrine - Shrines are religious places that are dedicated to specific deities, saints and other figures of religious importance. Typically, the contain religious depictions and people frequently leave offerings at those places. Wayside shrines are small shrines that can be found next to a road or pathway and are frequented by travellers passing by."
|
||||
},
|
||||
{
|
||||
"if": "value=windturbine",
|
||||
"then": "windturbine - Modern windmills generating electricity"
|
||||
|
|
|
@ -17,16 +17,16 @@
|
|||
"contributor": "Anonymous"
|
||||
},
|
||||
{
|
||||
"commits": 128,
|
||||
"commits": 130,
|
||||
"contributor": "mcliquid"
|
||||
},
|
||||
{
|
||||
"commits": 95,
|
||||
"contributor": "Allan Nordhøy"
|
||||
"contributor": "mike140"
|
||||
},
|
||||
{
|
||||
"commits": 92,
|
||||
"contributor": "mike140"
|
||||
"commits": 95,
|
||||
"contributor": "Allan Nordhøy"
|
||||
},
|
||||
{
|
||||
"commits": 89,
|
||||
|
@ -41,12 +41,12 @@
|
|||
"contributor": "danieldegroot2"
|
||||
},
|
||||
{
|
||||
"commits": 58,
|
||||
"contributor": "Jiří Podhorecký"
|
||||
"commits": 59,
|
||||
"contributor": "Supaplex"
|
||||
},
|
||||
{
|
||||
"commits": 57,
|
||||
"contributor": "Supaplex"
|
||||
"commits": 58,
|
||||
"contributor": "Jiří Podhorecký"
|
||||
},
|
||||
{
|
||||
"commits": 53,
|
||||
|
@ -105,12 +105,12 @@
|
|||
"contributor": "el_libre como el chaval"
|
||||
},
|
||||
{
|
||||
"commits": 16,
|
||||
"contributor": "macpac"
|
||||
"commits": 17,
|
||||
"contributor": "Pau Nofuentes"
|
||||
},
|
||||
{
|
||||
"commits": 15,
|
||||
"contributor": "Pau Nofuentes"
|
||||
"commits": 16,
|
||||
"contributor": "macpac"
|
||||
},
|
||||
{
|
||||
"commits": 15,
|
||||
|
@ -252,6 +252,10 @@
|
|||
"commits": 7,
|
||||
"contributor": "Niels Elgaard Larsen"
|
||||
},
|
||||
{
|
||||
"commits": 6,
|
||||
"contributor": "Lukáš Jelínek"
|
||||
},
|
||||
{
|
||||
"commits": 6,
|
||||
"contributor": "Juele juele"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue