Themes: add proper 'image along way' possibility
This commit is contained in:
parent
29f8e08509
commit
584fb3cb57
17 changed files with 259 additions and 70 deletions
|
@ -134,6 +134,27 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"opening_hours",
|
"opening_hours",
|
||||||
|
{
|
||||||
|
"id": "oneway",
|
||||||
|
"question": {
|
||||||
|
"en": "In what direction can this aerialway be taken?"
|
||||||
|
},
|
||||||
|
"mappings": [
|
||||||
|
{
|
||||||
|
"if": "oneway=yes",
|
||||||
|
"alsoShowIf": "oneway=",
|
||||||
|
"then": {
|
||||||
|
"en": "This aerialway can only be taken to the top"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"if": "oneway=no",
|
||||||
|
"then": {
|
||||||
|
"en": "This aerialway can be taken in both directions"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "length",
|
"id": "length",
|
||||||
"render": {
|
"render": {
|
||||||
|
@ -144,7 +165,20 @@
|
||||||
"lineRendering": [
|
"lineRendering": [
|
||||||
{
|
{
|
||||||
"width": "4",
|
"width": "4",
|
||||||
"color": "black"
|
"color": "black",
|
||||||
|
"imageAlongWay": [ {
|
||||||
|
"if": "oneway=no",
|
||||||
|
"then": "./assets/png/twoway.png"
|
||||||
|
},{
|
||||||
|
"if": {
|
||||||
|
"or": [
|
||||||
|
"oneway=yes",
|
||||||
|
"oneway="
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"then": "./assets/png/oneway.png"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"id": "aerialway",
|
"id": "aerialway",
|
||||||
|
|
|
@ -96,7 +96,8 @@
|
||||||
"then": "gray"
|
"then": "gray"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
},
|
||||||
|
"imageAlongWay": "./assets/png/oneway.png"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"id": "ski_piste",
|
"id": "ski_piste",
|
||||||
|
|
|
@ -14,5 +14,21 @@
|
||||||
"Pieter Vander Vennet"
|
"Pieter Vander Vennet"
|
||||||
],
|
],
|
||||||
"sources": []
|
"sources": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "twoway.png",
|
||||||
|
"license": "CC0-1.0",
|
||||||
|
"authors": [
|
||||||
|
"Pieter Vander Vennet"
|
||||||
|
],
|
||||||
|
"sources": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "twoway.svg",
|
||||||
|
"license": "CC0-1.0",
|
||||||
|
"authors": [
|
||||||
|
"Pieter Vander Vennet"
|
||||||
|
],
|
||||||
|
"sources": []
|
||||||
}
|
}
|
||||||
]
|
]
|
BIN
assets/png/twoway.png
Normal file
BIN
assets/png/twoway.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.4 KiB |
2
assets/png/twoway.png.license
Normal file
2
assets/png/twoway.png.license
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
SPDX-FileCopyrightText: Pieter Vander Vennet
|
||||||
|
SPDX-License-Identifier: CC0
|
58
assets/png/twoway.svg
Normal file
58
assets/png/twoway.svg
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
width="400"
|
||||||
|
height="400"
|
||||||
|
viewBox="0 0 400 400"
|
||||||
|
version="1.1"
|
||||||
|
id="svg1"
|
||||||
|
sodipodi:docname="twoway.svg"
|
||||||
|
inkscape:version="1.3.2 (1:1.3.2+202311252150+091e20ef0f)"
|
||||||
|
inkscape:export-filename="twoway.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<defs
|
||||||
|
id="defs1" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview1"
|
||||||
|
pagecolor="#efe1c6"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1"
|
||||||
|
inkscape:zoom="2.1376401"
|
||||||
|
inkscape:cx="153.67414"
|
||||||
|
inkscape:cy="183.14589"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="995"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg1"
|
||||||
|
showgrid="false"
|
||||||
|
showguides="true">
|
||||||
|
<sodipodi:guide
|
||||||
|
position="-44.441532,199.7792"
|
||||||
|
orientation="0,-1"
|
||||||
|
id="guide1"
|
||||||
|
inkscape:locked="false" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<g
|
||||||
|
id="surface1"
|
||||||
|
transform="matrix(0,0.46822921,-0.46822921,0,397.53821,111.31934)"
|
||||||
|
style="stroke:none">
|
||||||
|
<path
|
||||||
|
id="path1"
|
||||||
|
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:20;stroke-linecap:round;stroke-linejoin:bevel;stroke-opacity:1"
|
||||||
|
d="M 0.35313437,468.46201 187.80431,842.77256 375.40782,467.93858 230.57188,530.22569 V 312.546 L 375.40782,374.83506 187.80431,-8.76875e-4 0.35313437,374.30967 c 1e-8,0 42.56450563,-17.52151 93.90429663,-39.77539 L 144.63048,312.69444 V 530.07725 L 94.257431,508.2374 C 42.917692,485.98356 0.35313438,468.46201 0.35313437,468.46201 Z" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g2"
|
||||||
|
transform="matrix(0,1,1,0,4.4769698,8.5883084)"
|
||||||
|
style="stroke:none" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2 KiB |
2
assets/png/twoway.svg.license
Normal file
2
assets/png/twoway.svg.license
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
SPDX-FileCopyrightText: Pieter Vander Vennet
|
||||||
|
SPDX-License-Identifier: CC0
|
|
@ -7,7 +7,7 @@
|
||||||
"en": "Everything you need to go skiing"
|
"en": "Everything you need to go skiing"
|
||||||
},
|
},
|
||||||
"icon": "./assets/layers/aerialway/chair_lift.svg",
|
"icon": "./assets/layers/aerialway/chair_lift.svg",
|
||||||
"enableTerrain": true,
|
"enableTerrain": false,
|
||||||
"layers": [
|
"layers": [
|
||||||
"ski_piste",
|
"ski_piste",
|
||||||
"aerialway",
|
"aerialway",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "mapcomplete",
|
"name": "mapcomplete",
|
||||||
"version": "0.37.3",
|
"version": "0.37.4",
|
||||||
"repository": "https://github.com/pietervdvn/MapComplete",
|
"repository": "https://github.com/pietervdvn/MapComplete",
|
||||||
"description": "A small website to edit OSM easily",
|
"description": "A small website to edit OSM easily",
|
||||||
"bugs": "https://github.com/pietervdvn/MapComplete/issues",
|
"bugs": "https://github.com/pietervdvn/MapComplete/issues",
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { TagUtils } from "./TagUtils"
|
||||||
import { Tag } from "./Tag"
|
import { Tag } from "./Tag"
|
||||||
import { RegexTag } from "./RegexTag"
|
import { RegexTag } from "./RegexTag"
|
||||||
import { TagConfigJson } from "../../Models/ThemeConfig/Json/TagConfigJson"
|
import { TagConfigJson } from "../../Models/ThemeConfig/Json/TagConfigJson"
|
||||||
|
import { ExpressionSpecification } from "maplibre-gl"
|
||||||
|
|
||||||
export class And extends TagsFilter {
|
export class And extends TagsFilter {
|
||||||
public and: TagsFilter[]
|
public and: TagsFilter[]
|
||||||
|
@ -429,4 +430,8 @@ export class And extends TagsFilter {
|
||||||
f(this)
|
f(this)
|
||||||
this.and.forEach((sub) => sub.visit(f))
|
this.and.forEach((sub) => sub.visit(f))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
asMapboxExpression(): ExpressionSpecification {
|
||||||
|
return ["all", ...this.and.map(t => t.asMapboxExpression())]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { TagsFilter } from "./TagsFilter"
|
||||||
import { TagUtils } from "./TagUtils"
|
import { TagUtils } from "./TagUtils"
|
||||||
import { And } from "./And"
|
import { And } from "./And"
|
||||||
import { TagConfigJson } from "../../Models/ThemeConfig/Json/TagConfigJson"
|
import { TagConfigJson } from "../../Models/ThemeConfig/Json/TagConfigJson"
|
||||||
|
import { ExpressionSpecification } from "maplibre-gl"
|
||||||
|
|
||||||
export class Or extends TagsFilter {
|
export class Or extends TagsFilter {
|
||||||
public or: TagsFilter[]
|
public or: TagsFilter[]
|
||||||
|
@ -288,4 +289,8 @@ export class Or extends TagsFilter {
|
||||||
f(this)
|
f(this)
|
||||||
this.or.forEach((t) => t.visit(f))
|
this.or.forEach((t) => t.visit(f))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
asMapboxExpression(): ExpressionSpecification {
|
||||||
|
return ["any", ...this.or.map(t => t.asMapboxExpression())]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { Tag } from "./Tag"
|
import { Tag } from "./Tag"
|
||||||
import { TagsFilter } from "./TagsFilter"
|
import { TagsFilter } from "./TagsFilter"
|
||||||
import { TagConfigJson } from "../../Models/ThemeConfig/Json/TagConfigJson"
|
import { TagConfigJson } from "../../Models/ThemeConfig/Json/TagConfigJson"
|
||||||
|
import { ExpressionSpecification } from "maplibre-gl"
|
||||||
|
|
||||||
export class RegexTag extends TagsFilter {
|
export class RegexTag extends TagsFilter {
|
||||||
public readonly key: RegExp | string
|
public readonly key: RegExp | string
|
||||||
|
@ -357,4 +358,11 @@ export class RegexTag extends TagsFilter {
|
||||||
visit(f: (TagsFilter) => void) {
|
visit(f: (TagsFilter) => void) {
|
||||||
f(this)
|
f(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
asMapboxExpression(): ExpressionSpecification {
|
||||||
|
if(typeof this.key=== "string" && typeof this.value === "string" ) {
|
||||||
|
return [this.invert ? "!=" : "==", ["get",this.key], this.value]
|
||||||
|
}
|
||||||
|
throw "TODO"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
import { Utils } from "../../Utils"
|
import { Utils } from "../../Utils"
|
||||||
import { TagsFilter } from "./TagsFilter"
|
import { TagsFilter } from "./TagsFilter"
|
||||||
import { TagConfigJson } from "../../Models/ThemeConfig/Json/TagConfigJson"
|
import { TagConfigJson } from "../../Models/ThemeConfig/Json/TagConfigJson"
|
||||||
|
import { ExpressionSpecification } from "maplibre-gl"
|
||||||
|
|
||||||
export class Tag extends TagsFilter {
|
export class Tag extends TagsFilter {
|
||||||
public key: string
|
public key: string
|
||||||
public value: string
|
public value: string
|
||||||
|
|
||||||
constructor(key: string, value: string) {
|
constructor(key: string, value: string) {
|
||||||
super()
|
super()
|
||||||
this.key = key
|
this.key = key
|
||||||
|
@ -63,7 +65,7 @@ export class Tag extends TagsFilter {
|
||||||
asOverpass(): string[] {
|
asOverpass(): string[] {
|
||||||
if (this.value === "") {
|
if (this.value === "") {
|
||||||
// NOT having this key
|
// NOT having this key
|
||||||
return ['[!"' + this.key + '"]']
|
return ["[!\"" + this.key + "\"]"]
|
||||||
}
|
}
|
||||||
return [`["${this.key}"="${this.value}"]`]
|
return [`["${this.key}"="${this.value}"]`]
|
||||||
}
|
}
|
||||||
|
@ -81,7 +83,7 @@ export class Tag extends TagsFilter {
|
||||||
asHumanString(
|
asHumanString(
|
||||||
linkToWiki?: boolean,
|
linkToWiki?: boolean,
|
||||||
shorten?: boolean,
|
shorten?: boolean,
|
||||||
currentProperties?: Record<string, string>
|
currentProperties?: Record<string, string>,
|
||||||
) {
|
) {
|
||||||
let v = this.value
|
let v = this.value
|
||||||
if (typeof v !== "string") {
|
if (typeof v !== "string") {
|
||||||
|
@ -165,4 +167,16 @@ export class Tag extends TagsFilter {
|
||||||
visit(f: (tagsFilter: TagsFilter) => void) {
|
visit(f: (tagsFilter: TagsFilter) => void) {
|
||||||
f(this)
|
f(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
asMapboxExpression(): ExpressionSpecification {
|
||||||
|
if (this.value === "") {
|
||||||
|
return [
|
||||||
|
"any",
|
||||||
|
["!", ["has", this.key]],
|
||||||
|
["==", ["get", this.key], ""],
|
||||||
|
]
|
||||||
|
|
||||||
|
}
|
||||||
|
return ["==", ["get", this.key], this.value]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { TagConfigJson } from "../../Models/ThemeConfig/Json/TagConfigJson"
|
import { TagConfigJson } from "../../Models/ThemeConfig/Json/TagConfigJson"
|
||||||
|
import { ExpressionSpecification } from "maplibre-gl"
|
||||||
|
|
||||||
export abstract class TagsFilter {
|
export abstract class TagsFilter {
|
||||||
abstract asOverpass(): string[]
|
abstract asOverpass(): string[]
|
||||||
|
@ -63,4 +64,6 @@ export abstract class TagsFilter {
|
||||||
* Walks the entire tree, every tagsFilter will be passed into the function once
|
* Walks the entire tree, every tagsFilter will be passed into the function once
|
||||||
*/
|
*/
|
||||||
abstract visit(f: (tagsFilter: TagsFilter) => void)
|
abstract visit(f: (tagsFilter: TagsFilter) => void)
|
||||||
|
|
||||||
|
abstract asMapboxExpression(): ExpressionSpecification
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
import { MinimalTagRenderingConfigJson } from "./TagRenderingConfigJson"
|
import { MinimalTagRenderingConfigJson } from "./TagRenderingConfigJson"
|
||||||
|
import { MappingConfigJson } from "./QuestionableTagRenderingConfigJson"
|
||||||
|
import { TagsFilter } from "../../../Logic/Tags/TagsFilter"
|
||||||
|
import { TagConfigJson } from "./TagConfigJson"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The LineRenderingConfig gives all details onto how to render a single line of a feature.
|
* The LineRenderingConfig gives all details onto how to render a single line of a feature.
|
||||||
|
@ -74,4 +77,12 @@ export default interface LineRenderingConfigJson {
|
||||||
* type: int
|
* type: int
|
||||||
*/
|
*/
|
||||||
offset?: number | MinimalTagRenderingConfigJson
|
offset?: number | MinimalTagRenderingConfigJson
|
||||||
|
/**
|
||||||
|
* question: What PNG-image should be shown along the way?
|
||||||
|
*
|
||||||
|
* ifunset: no image is shown along the way
|
||||||
|
* suggestions: [{if: "./assets/png/oneway.png", then: "Show a oneway error"}]
|
||||||
|
* type: image
|
||||||
|
*/
|
||||||
|
imageAlongWay?: {if: TagConfigJson, then: string}[] | string
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import WithContextLoader from "./WithContextLoader"
|
import WithContextLoader from "./WithContextLoader"
|
||||||
import TagRenderingConfig from "./TagRenderingConfig"
|
import TagRenderingConfig from "./TagRenderingConfig"
|
||||||
import { Utils } from "../../Utils"
|
|
||||||
import LineRenderingConfigJson from "./Json/LineRenderingConfigJson"
|
import LineRenderingConfigJson from "./Json/LineRenderingConfigJson"
|
||||||
|
import { TagUtils } from "../../Logic/Tags/TagUtils"
|
||||||
|
import { TagsFilter } from "../../Logic/Tags/TagsFilter"
|
||||||
|
|
||||||
export default class LineRenderingConfig extends WithContextLoader {
|
export default class LineRenderingConfig extends WithContextLoader {
|
||||||
public readonly color: TagRenderingConfig
|
public readonly color: TagRenderingConfig
|
||||||
|
@ -12,6 +13,7 @@ export default class LineRenderingConfig extends WithContextLoader {
|
||||||
public readonly fill: TagRenderingConfig
|
public readonly fill: TagRenderingConfig
|
||||||
public readonly fillColor: TagRenderingConfig
|
public readonly fillColor: TagRenderingConfig
|
||||||
public readonly leftRightSensitive: boolean
|
public readonly leftRightSensitive: boolean
|
||||||
|
public readonly imageAlongWay: { if?: TagsFilter, then: string }[]
|
||||||
|
|
||||||
constructor(json: LineRenderingConfigJson, context: string) {
|
constructor(json: LineRenderingConfigJson, context: string) {
|
||||||
super(json, context)
|
super(json, context)
|
||||||
|
@ -21,6 +23,28 @@ export default class LineRenderingConfig extends WithContextLoader {
|
||||||
this.lineCap = this.tr("lineCap", "round")
|
this.lineCap = this.tr("lineCap", "round")
|
||||||
this.fill = this.tr("fill", undefined)
|
this.fill = this.tr("fill", undefined)
|
||||||
this.fillColor = this.tr("fillColor", undefined)
|
this.fillColor = this.tr("fillColor", undefined)
|
||||||
|
this.imageAlongWay = []
|
||||||
|
if (json.imageAlongWay) {
|
||||||
|
if (typeof json.imageAlongWay === "string") {
|
||||||
|
this.imageAlongWay.push({
|
||||||
|
then: json.imageAlongWay,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
for (let i = 0; i < json.imageAlongWay.length; i++) {
|
||||||
|
const imgAlong = json.imageAlongWay[i]
|
||||||
|
const ctx = context + ".imageAlongWay[" + i + "]"
|
||||||
|
if(!imgAlong.then.endsWith(".png")){
|
||||||
|
throw "An imageAlongWay should always be a PNG image"
|
||||||
|
}
|
||||||
|
this.imageAlongWay.push(
|
||||||
|
{
|
||||||
|
if: TagUtils.Tag(imgAlong.if, ctx),
|
||||||
|
then: imgAlong.then,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof json.offset === "string") {
|
if (typeof json.offset === "string") {
|
||||||
json.offset = parseFloat(json.offset)
|
json.offset = parseFloat(json.offset)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { ImmutableStore, Store, UIEventSource } from "../../Logic/UIEventSource"
|
import { ImmutableStore, Store, UIEventSource } from "../../Logic/UIEventSource"
|
||||||
import type { Map as MlMap } from "maplibre-gl"
|
import type { AddLayerObject, Map as MlMap } from "maplibre-gl"
|
||||||
import { GeoJSONSource, Marker } from "maplibre-gl"
|
import { GeoJSONSource, Marker } from "maplibre-gl"
|
||||||
import { ShowDataLayerOptions } from "./ShowDataLayerOptions"
|
import { ShowDataLayerOptions } from "./ShowDataLayerOptions"
|
||||||
import { GeoOperations } from "../../Logic/GeoOperations"
|
import { GeoOperations } from "../../Logic/GeoOperations"
|
||||||
|
@ -15,6 +15,7 @@ import * as range_layer from "../../../assets/layers/range/range.json"
|
||||||
import PerLayerFeatureSourceSplitter from "../../Logic/FeatureSource/PerLayerFeatureSourceSplitter"
|
import PerLayerFeatureSourceSplitter from "../../Logic/FeatureSource/PerLayerFeatureSourceSplitter"
|
||||||
import FilteredLayer from "../../Models/FilteredLayer"
|
import FilteredLayer from "../../Models/FilteredLayer"
|
||||||
import SimpleFeatureSource from "../../Logic/FeatureSource/Sources/SimpleFeatureSource"
|
import SimpleFeatureSource from "../../Logic/FeatureSource/Sources/SimpleFeatureSource"
|
||||||
|
import { TagsFilter } from "../../Logic/Tags/TagsFilter"
|
||||||
|
|
||||||
class PointRenderingLayer {
|
class PointRenderingLayer {
|
||||||
private readonly _config: PointRenderingConfig
|
private readonly _config: PointRenderingConfig
|
||||||
|
@ -36,7 +37,7 @@ class PointRenderingLayer {
|
||||||
visibility?: Store<boolean>,
|
visibility?: Store<boolean>,
|
||||||
fetchStore?: (id: string) => Store<Record<string, string>>,
|
fetchStore?: (id: string) => Store<Record<string, string>>,
|
||||||
onClick?: (feature: Feature) => void,
|
onClick?: (feature: Feature) => void,
|
||||||
selectedElement?: Store<{ properties: { id?: string } }>
|
selectedElement?: Store<{ properties: { id?: string } }>,
|
||||||
) {
|
) {
|
||||||
this._visibility = visibility
|
this._visibility = visibility
|
||||||
this._config = config
|
this._config = config
|
||||||
|
@ -89,7 +90,7 @@ class PointRenderingLayer {
|
||||||
" while rendering",
|
" while rendering",
|
||||||
location,
|
location,
|
||||||
"of",
|
"of",
|
||||||
this._config
|
this._config,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
const id = feature.properties.id + "-" + location
|
const id = feature.properties.id + "-" + location
|
||||||
|
@ -97,7 +98,7 @@ class PointRenderingLayer {
|
||||||
|
|
||||||
const loc = GeoOperations.featureToCoordinateWithRenderingType(
|
const loc = GeoOperations.featureToCoordinateWithRenderingType(
|
||||||
<any>feature,
|
<any>feature,
|
||||||
location
|
location,
|
||||||
)
|
)
|
||||||
if (loc === undefined) {
|
if (loc === undefined) {
|
||||||
continue
|
continue
|
||||||
|
@ -153,7 +154,7 @@ class PointRenderingLayer {
|
||||||
|
|
||||||
if (this._onClick) {
|
if (this._onClick) {
|
||||||
const self = this
|
const self = this
|
||||||
el.addEventListener("click", function (ev) {
|
el.addEventListener("click", function(ev) {
|
||||||
ev.preventDefault()
|
ev.preventDefault()
|
||||||
self._onClick(feature)
|
self._onClick(feature)
|
||||||
// Workaround to signal the MapLibreAdaptor to ignore this click
|
// Workaround to signal the MapLibreAdaptor to ignore this click
|
||||||
|
@ -221,7 +222,7 @@ class LineRenderingLayer {
|
||||||
config: LineRenderingConfig,
|
config: LineRenderingConfig,
|
||||||
visibility?: Store<boolean>,
|
visibility?: Store<boolean>,
|
||||||
fetchStore?: (id: string) => Store<Record<string, string>>,
|
fetchStore?: (id: string) => Store<Record<string, string>>,
|
||||||
onClick?: (feature: Feature) => void
|
onClick?: (feature: Feature) => void,
|
||||||
) {
|
) {
|
||||||
this._layername = layername
|
this._layername = layername
|
||||||
this._map = map
|
this._map = map
|
||||||
|
@ -235,53 +236,60 @@ class LineRenderingLayer {
|
||||||
map.on("styledata", () => self.update(features.features))
|
map.on("styledata", () => self.update(features.features))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async addSymbolLayer(sourceId: string, url: string = "./assets/png/oneway.png") {
|
|
||||||
const map = this._map
|
|
||||||
const imgId = url.replaceAll(/[/.-]/g, "_")
|
|
||||||
|
|
||||||
if (map.getImage(imgId) === undefined) {
|
|
||||||
await new Promise<void>((resolve, reject) => {
|
|
||||||
map.loadImage(url, (err, image) => {
|
|
||||||
if (err) {
|
|
||||||
console.error("Could not add symbol layer to line due to", err)
|
|
||||||
reject(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
map.addImage(imgId, image)
|
|
||||||
resolve()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
map.addLayer({
|
|
||||||
"id": "symbol-layer_" + this._layername + "-" + imgId,
|
|
||||||
'type': 'symbol',
|
|
||||||
'source': sourceId,
|
|
||||||
'layout': {
|
|
||||||
'symbol-placement': 'line',
|
|
||||||
'symbol-spacing': 10,
|
|
||||||
'icon-allow-overlap': true,
|
|
||||||
'icon-rotation-alignment':'map',
|
|
||||||
'icon-pitch-alignment':'map',
|
|
||||||
'icon-image': imgId,
|
|
||||||
'icon-size': 0.055,
|
|
||||||
'visibility': 'visible'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public destruct(): void {
|
public destruct(): void {
|
||||||
this._map.removeLayer(this._layername + "_polygon")
|
this._map.removeLayer(this._layername + "_polygon")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async addSymbolLayer(sourceId: string, imageAlongWay: { if?: TagsFilter, then: string }[]) {
|
||||||
|
const map = this._map
|
||||||
|
await Promise.allSettled(imageAlongWay.map(async (img, i) => {
|
||||||
|
const imgId = img.then.replaceAll(/[/.-]/g, "_")
|
||||||
|
if (map.getImage(imgId) === undefined) {
|
||||||
|
await new Promise<void>((resolve, reject) => {
|
||||||
|
map.loadImage(img.then, (err, image) => {
|
||||||
|
if (err) {
|
||||||
|
console.error("Could not add symbol layer to line due to", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
map.addImage(imgId, image)
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const spec: AddLayerObject = {
|
||||||
|
"id": "symbol-layer_" + this._layername + "-" + i,
|
||||||
|
"type": "symbol",
|
||||||
|
"source": sourceId,
|
||||||
|
"layout": {
|
||||||
|
"symbol-placement": "line",
|
||||||
|
"symbol-spacing": 10,
|
||||||
|
"icon-allow-overlap": true,
|
||||||
|
"icon-rotation-alignment": "map",
|
||||||
|
"icon-pitch-alignment": "map",
|
||||||
|
"icon-image": imgId,
|
||||||
|
"icon-size": 0.055,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const filter = img.if?.asMapboxExpression()
|
||||||
|
console.log(">>>", this._layername, imgId, img.if, "-->", filter)
|
||||||
|
if (filter) {
|
||||||
|
spec.filter = filter
|
||||||
|
}
|
||||||
|
map.addLayer(spec)
|
||||||
|
}))
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate the feature-state for maplibre
|
* Calculate the feature-state for maplibre
|
||||||
* @param properties
|
* @param properties
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
private calculatePropsFor(
|
private calculatePropsFor(
|
||||||
properties: Record<string, string>
|
properties: Record<string, string>,
|
||||||
): Partial<Record<(typeof LineRenderingLayer.lineConfigKeys)[number], string>> {
|
): Partial<Record<(typeof LineRenderingLayer.lineConfigKeys)[number], string>> {
|
||||||
const config = this._config
|
const config = this._config
|
||||||
|
|
||||||
|
@ -357,11 +365,8 @@ class LineRenderingLayer {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
if(this._layername.startsWith("mapcomplete_ski_piste") || this._layername.startsWith("mapcomplete_aerialway")){
|
if (this._config.imageAlongWay) {
|
||||||
// TODO FIXME properly enable this so that more layers can use this if appropriate
|
this.addSymbolLayer(this._layername, this._config.imageAlongWay)
|
||||||
this.addSymbolLayer(this._layername)
|
|
||||||
}else{
|
|
||||||
console.log("No oneway arrow for", this._layername)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -372,7 +377,7 @@ class LineRenderingLayer {
|
||||||
}
|
}
|
||||||
map.setFeatureState(
|
map.setFeatureState(
|
||||||
{ source: this._layername, id: feature.properties.id },
|
{ source: this._layername, id: feature.properties.id },
|
||||||
this.calculatePropsFor(feature.properties)
|
this.calculatePropsFor(feature.properties),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -415,7 +420,7 @@ class LineRenderingLayer {
|
||||||
"Error while setting visibility of layers ",
|
"Error while setting visibility of layers ",
|
||||||
linelayer,
|
linelayer,
|
||||||
polylayer,
|
polylayer,
|
||||||
e
|
e,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -436,7 +441,7 @@ class LineRenderingLayer {
|
||||||
console.trace(
|
console.trace(
|
||||||
"Got a feature without ID; this causes rendering bugs:",
|
"Got a feature without ID; this causes rendering bugs:",
|
||||||
feature,
|
feature,
|
||||||
"from"
|
"from",
|
||||||
)
|
)
|
||||||
LineRenderingLayer.missingIdTriggered = true
|
LineRenderingLayer.missingIdTriggered = true
|
||||||
}
|
}
|
||||||
|
@ -448,7 +453,7 @@ class LineRenderingLayer {
|
||||||
if (this._fetchStore === undefined) {
|
if (this._fetchStore === undefined) {
|
||||||
map.setFeatureState(
|
map.setFeatureState(
|
||||||
{ source: this._layername, id },
|
{ source: this._layername, id },
|
||||||
this.calculatePropsFor(feature.properties)
|
this.calculatePropsFor(feature.properties),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
const tags = this._fetchStore(id)
|
const tags = this._fetchStore(id)
|
||||||
|
@ -465,7 +470,7 @@ class LineRenderingLayer {
|
||||||
}
|
}
|
||||||
map.setFeatureState(
|
map.setFeatureState(
|
||||||
{ source: this._layername, id },
|
{ source: this._layername, id },
|
||||||
this.calculatePropsFor(properties)
|
this.calculatePropsFor(properties),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -489,7 +494,7 @@ export default class ShowDataLayer {
|
||||||
layer: LayerConfig
|
layer: LayerConfig
|
||||||
drawMarkers?: true | boolean
|
drawMarkers?: true | boolean
|
||||||
drawLines?: true | boolean
|
drawLines?: true | boolean
|
||||||
}
|
},
|
||||||
) {
|
) {
|
||||||
this._options = options
|
this._options = options
|
||||||
const self = this
|
const self = this
|
||||||
|
@ -500,7 +505,7 @@ export default class ShowDataLayer {
|
||||||
mlmap: UIEventSource<MlMap>,
|
mlmap: UIEventSource<MlMap>,
|
||||||
features: FeatureSource,
|
features: FeatureSource,
|
||||||
layers: LayerConfig[],
|
layers: LayerConfig[],
|
||||||
options?: Partial<ShowDataLayerOptions>
|
options?: Partial<ShowDataLayerOptions>,
|
||||||
) {
|
) {
|
||||||
const perLayer: PerLayerFeatureSourceSplitter<FeatureSourceForLayer> =
|
const perLayer: PerLayerFeatureSourceSplitter<FeatureSourceForLayer> =
|
||||||
new PerLayerFeatureSourceSplitter(
|
new PerLayerFeatureSourceSplitter(
|
||||||
|
@ -508,7 +513,7 @@ export default class ShowDataLayer {
|
||||||
features,
|
features,
|
||||||
{
|
{
|
||||||
constructStore: (features, layer) => new SimpleFeatureSource(layer, features),
|
constructStore: (features, layer) => new SimpleFeatureSource(layer, features),
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
perLayer.forEach((fs) => {
|
perLayer.forEach((fs) => {
|
||||||
new ShowDataLayer(mlmap, {
|
new ShowDataLayer(mlmap, {
|
||||||
|
@ -522,7 +527,7 @@ export default class ShowDataLayer {
|
||||||
public static showRange(
|
public static showRange(
|
||||||
map: Store<MlMap>,
|
map: Store<MlMap>,
|
||||||
features: FeatureSource,
|
features: FeatureSource,
|
||||||
doShowLayer?: Store<boolean>
|
doShowLayer?: Store<boolean>,
|
||||||
): ShowDataLayer {
|
): ShowDataLayer {
|
||||||
return new ShowDataLayer(map, {
|
return new ShowDataLayer(map, {
|
||||||
layer: ShowDataLayer.rangeLayer,
|
layer: ShowDataLayer.rangeLayer,
|
||||||
|
@ -531,7 +536,8 @@ export default class ShowDataLayer {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
public destruct() {}
|
public destruct() {
|
||||||
|
}
|
||||||
|
|
||||||
private zoomToCurrentFeatures(map: MlMap) {
|
private zoomToCurrentFeatures(map: MlMap) {
|
||||||
if (this._options.zoomToFeatures) {
|
if (this._options.zoomToFeatures) {
|
||||||
|
@ -552,9 +558,9 @@ export default class ShowDataLayer {
|
||||||
(this._options.layer.title === undefined
|
(this._options.layer.title === undefined
|
||||||
? undefined
|
? undefined
|
||||||
: (feature: Feature) => {
|
: (feature: Feature) => {
|
||||||
selectedElement?.setData(feature)
|
selectedElement?.setData(feature)
|
||||||
selectedLayer?.setData(this._options.layer)
|
selectedLayer?.setData(this._options.layer)
|
||||||
})
|
})
|
||||||
if (this._options.drawLines !== false) {
|
if (this._options.drawLines !== false) {
|
||||||
for (let i = 0; i < this._options.layer.lineRendering.length; i++) {
|
for (let i = 0; i < this._options.layer.lineRendering.length; i++) {
|
||||||
const lineRenderingConfig = this._options.layer.lineRendering[i]
|
const lineRenderingConfig = this._options.layer.lineRendering[i]
|
||||||
|
@ -565,7 +571,7 @@ export default class ShowDataLayer {
|
||||||
lineRenderingConfig,
|
lineRenderingConfig,
|
||||||
doShowLayer,
|
doShowLayer,
|
||||||
fetchStore,
|
fetchStore,
|
||||||
onClick
|
onClick,
|
||||||
)
|
)
|
||||||
this.onDestroy.push(l.destruct)
|
this.onDestroy.push(l.destruct)
|
||||||
}
|
}
|
||||||
|
@ -580,7 +586,7 @@ export default class ShowDataLayer {
|
||||||
doShowLayer,
|
doShowLayer,
|
||||||
fetchStore,
|
fetchStore,
|
||||||
onClick,
|
onClick,
|
||||||
selectedElement
|
selectedElement,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue