Refactoring: split icons into proper layered icons, fix and rerun shops-thief
This commit is contained in:
parent
d6cd0516bb
commit
19a760178b
307 changed files with 26845 additions and 116541 deletions
|
@ -94,8 +94,8 @@ export default class CreateNoteImportLayer extends Conversion<LayerConfigJson, L
|
|||
maxCacheAge: 0,
|
||||
},
|
||||
/* We need to set 'pass_all_features'
|
||||
There are probably many note_import-layers, and we don't want the first one to gobble up all notes and then discard them...
|
||||
*/
|
||||
There are probably many note_import-layers, and we don't want the first one to gobble up all notes and then discard them...
|
||||
*/
|
||||
passAllFeatures: true,
|
||||
minzoom: Math.min(12, layerJson.minzoom - 2),
|
||||
title: {
|
||||
|
@ -181,15 +181,24 @@ export default class CreateNoteImportLayer extends Conversion<LayerConfigJson, L
|
|||
pointRendering: [
|
||||
{
|
||||
location: ["point"],
|
||||
icon: {
|
||||
render: "circle:white;help:black",
|
||||
mappings: [
|
||||
{
|
||||
if: { or: ["closed_at~*", "_imported=yes"] },
|
||||
then: "circle:white;checkmark:black",
|
||||
marker: [
|
||||
{
|
||||
icon: "circle",
|
||||
color: "#fff",
|
||||
},
|
||||
{
|
||||
icon: {
|
||||
render: "help",
|
||||
mappings: [
|
||||
{
|
||||
if: { or: ["closed_at~*", "_imported=yes"] },
|
||||
then: "checkmark",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
color: "#00",
|
||||
},
|
||||
],
|
||||
iconSize: "40,40",
|
||||
anchor: "center",
|
||||
},
|
||||
|
|
|
@ -81,7 +81,11 @@ export class UpdateLegacyLayer extends DesugaringStep<
|
|||
}
|
||||
}
|
||||
|
||||
if (config["mapRendering"] === undefined) {
|
||||
if (
|
||||
config["mapRendering"] === undefined &&
|
||||
config.pointRendering === undefined &&
|
||||
config.lineRendering === undefined
|
||||
) {
|
||||
config["mapRendering"] = []
|
||||
// This is a legacy format, lets create a pointRendering
|
||||
let location: ("point" | "centroid")[] = ["point"]
|
||||
|
@ -144,6 +148,7 @@ export class UpdateLegacyLayer extends DesugaringStep<
|
|||
}
|
||||
|
||||
if (config["mapRendering"]) {
|
||||
console.log("MapRendering is", config["mapRendering"], json.id)
|
||||
const pointRenderings: PointRenderingConfigJson[] = []
|
||||
const lineRenderings: LineRenderingConfigJson[] = []
|
||||
for (const mapRenderingElement of config["mapRendering"]) {
|
||||
|
@ -161,6 +166,21 @@ export class UpdateLegacyLayer extends DesugaringStep<
|
|||
|
||||
for (const rendering of config.pointRendering ?? []) {
|
||||
const pr = rendering
|
||||
if (pr["icon"]) {
|
||||
try {
|
||||
const icon = Utils.NoEmpty(pr["icon"].split(";"))
|
||||
pr.marker = icon.map((i) => {
|
||||
const [iconPath, color] = i.split(":")
|
||||
return { icon: iconPath, color }
|
||||
})
|
||||
delete pr["icon"]
|
||||
} catch (e) {
|
||||
console.error("Could not handle icon in", json.id)
|
||||
pr.marker = [{ icon: pr["icon"] }]
|
||||
delete pr["icon"]
|
||||
}
|
||||
}
|
||||
|
||||
let iconSize = pr.iconSize
|
||||
if (!iconSize) {
|
||||
continue
|
||||
|
@ -178,35 +198,36 @@ export class UpdateLegacyLayer extends DesugaringStep<
|
|||
}
|
||||
}
|
||||
|
||||
for (const rendering of config.pointRendering) {
|
||||
for (const key in rendering) {
|
||||
if (!rendering[key]) {
|
||||
continue
|
||||
}
|
||||
if (
|
||||
typeof rendering[key]["render"] === "string" &&
|
||||
Object.keys(rendering[key]).length === 1
|
||||
) {
|
||||
console.log("Rewrite: ", rendering[key])
|
||||
rendering[key] = rendering[key]["render"]
|
||||
if (config.pointRendering)
|
||||
for (const rendering of config.pointRendering) {
|
||||
for (const key in rendering) {
|
||||
if (!rendering[key]) {
|
||||
continue
|
||||
}
|
||||
if (
|
||||
typeof rendering[key]["render"] === "string" &&
|
||||
Object.keys(rendering[key]).length === 1
|
||||
) {
|
||||
console.log("Rewrite: ", rendering[key])
|
||||
rendering[key] = rendering[key]["render"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const rendering of config.lineRendering) {
|
||||
for (const key in rendering) {
|
||||
if (!rendering[key]) {
|
||||
continue
|
||||
}
|
||||
if (
|
||||
typeof rendering[key]["render"] === "string" &&
|
||||
Object.keys(rendering[key]).length === 1
|
||||
) {
|
||||
console.log("Rewrite: ", rendering[key])
|
||||
rendering[key] = rendering[key]["render"]
|
||||
if (config.lineRendering)
|
||||
for (const rendering of config.lineRendering) {
|
||||
for (const key in rendering) {
|
||||
if (!rendering[key]) {
|
||||
continue
|
||||
}
|
||||
if (
|
||||
typeof rendering[key]["render"] === "string" &&
|
||||
Object.keys(rendering[key]).length === 1
|
||||
) {
|
||||
console.log("Rewrite: ", rendering[key])
|
||||
rendering[key] = rendering[key]["render"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
result: config,
|
||||
|
|
|
@ -21,8 +21,7 @@ import { AddContextToTranslations } from "./AddContextToTranslations"
|
|||
import FilterConfigJson from "../Json/FilterConfigJson"
|
||||
import predifined_filters from "../../../../assets/layers/filters/filters.json"
|
||||
import { TagConfigJson } from "../Json/TagConfigJson"
|
||||
import PointRenderingConfigJson from "../Json/PointRenderingConfigJson"
|
||||
import LineRenderingConfigJson from "../Json/LineRenderingConfigJson"
|
||||
import PointRenderingConfigJson, { IconConfigJson } from "../Json/PointRenderingConfigJson"
|
||||
import ValidationUtils from "./ValidationUtils"
|
||||
import { RenderingSpecification } from "../../../UI/SpecialVisualization"
|
||||
import { QuestionableTagRenderingConfigJson } from "../Json/QuestionableTagRenderingConfigJson"
|
||||
|
@ -127,6 +126,7 @@ class ExpandTagRendering extends Conversion<
|
|||
> {
|
||||
private readonly _state: DesugaringContext
|
||||
private readonly _tagRenderingsByLabel: Map<string, TagRenderingConfigJson[]>
|
||||
// Only used for self-reference
|
||||
private readonly _self: LayerConfigJson
|
||||
private readonly _options: {
|
||||
/* If true, will copy the 'osmSource'-tags into the condition */
|
||||
|
@ -224,7 +224,7 @@ class ExpandTagRendering extends Conversion<
|
|||
|
||||
const spl = name.split(".")
|
||||
let layer = state.sharedLayers?.get(spl[0])
|
||||
if (spl[0] === this._self.id) {
|
||||
if (spl[0] === this._self?.id) {
|
||||
layer = this._self
|
||||
}
|
||||
|
||||
|
@ -352,7 +352,7 @@ class ExpandTagRendering extends Conversion<
|
|||
if (name.indexOf(".") > 0) {
|
||||
const [layerName] = name.split(".")
|
||||
let layer = state.sharedLayers.get(layerName)
|
||||
if (layerName === this._self.id) {
|
||||
if (layerName === this._self?.id) {
|
||||
layer = this._self
|
||||
}
|
||||
if (layer === undefined) {
|
||||
|
@ -1116,23 +1116,19 @@ export class RewriteSpecial extends DesugaringStep<TagRenderingConfigJson> {
|
|||
}
|
||||
}
|
||||
|
||||
class ExpandIconBadges extends DesugaringStep<PointRenderingConfigJson | LineRenderingConfigJson> {
|
||||
private _state: DesugaringContext
|
||||
private _layer: LayerConfigJson
|
||||
class ExpandIconBadges extends DesugaringStep<PointRenderingConfigJson> {
|
||||
private _expand: ExpandTagRendering
|
||||
|
||||
constructor(state: DesugaringContext, layer: LayerConfigJson) {
|
||||
super("Expands shorthand properties on iconBadges", ["iconBadges"], "ExpandIconBadges")
|
||||
this._state = state
|
||||
this._layer = layer
|
||||
this._expand = new ExpandTagRendering(state, layer)
|
||||
}
|
||||
|
||||
convert(
|
||||
json: PointRenderingConfigJson | LineRenderingConfigJson,
|
||||
json: PointRenderingConfigJson,
|
||||
context: string
|
||||
): {
|
||||
result: PointRenderingConfigJson | LineRenderingConfigJson
|
||||
result: PointRenderingConfigJson
|
||||
errors?: string[]
|
||||
warnings?: string[]
|
||||
information?: string[]
|
||||
|
@ -1140,7 +1136,7 @@ class ExpandIconBadges extends DesugaringStep<PointRenderingConfigJson | LineRen
|
|||
if (!json["iconBadges"]) {
|
||||
return { result: json }
|
||||
}
|
||||
const badgesJson = (<PointRenderingConfigJson>json).iconBadges
|
||||
const badgesJson = json.iconBadges
|
||||
|
||||
const iconBadges: { if: TagConfigJson; then: string | TagRenderingConfigJson }[] = []
|
||||
|
||||
|
@ -1176,7 +1172,7 @@ class ExpandIconBadges extends DesugaringStep<PointRenderingConfigJson | LineRen
|
|||
}
|
||||
}
|
||||
|
||||
class PreparePointRendering extends Fuse<PointRenderingConfigJson | LineRenderingConfigJson> {
|
||||
class PreparePointRendering extends Fuse<PointRenderingConfigJson> {
|
||||
constructor(state: DesugaringContext, layer: LayerConfigJson) {
|
||||
super(
|
||||
"Prepares point renderings by expanding 'icon' and 'iconBadges'",
|
||||
|
@ -1262,6 +1258,47 @@ export class AddMiniMap extends DesugaringStep<LayerConfigJson> {
|
|||
}
|
||||
}
|
||||
|
||||
class ExpandMarkerRenderings extends DesugaringStep<IconConfigJson> {
|
||||
private readonly _layer: LayerConfigJson
|
||||
private readonly _state: DesugaringContext
|
||||
|
||||
constructor(state: DesugaringContext, layer: LayerConfigJson) {
|
||||
super(
|
||||
"Expands tagRenderings in the icons, if needed",
|
||||
["icon", "color"],
|
||||
"ExpandMarkerRenderings"
|
||||
)
|
||||
this._layer = layer
|
||||
this._state = state
|
||||
}
|
||||
|
||||
convert(
|
||||
json: IconConfigJson,
|
||||
context: string
|
||||
): {
|
||||
result: IconConfigJson
|
||||
errors?: string[]
|
||||
warnings?: string[]
|
||||
information?: string[]
|
||||
} {
|
||||
const expander = new ExpandTagRendering(this._state, this._layer)
|
||||
const result: IconConfigJson = { icon: undefined, color: undefined }
|
||||
const errors: string[] = []
|
||||
const warnings: string[] = []
|
||||
if (json.icon && json.icon["builtin"]) {
|
||||
result.icon = expander.convertJoin(<any>json.icon, context, errors, warnings)[0]
|
||||
} else {
|
||||
result.icon = json.icon
|
||||
}
|
||||
if (json.color && json.color["builtin"]) {
|
||||
result.color = expander.convertJoin(<any>json.color, context, errors, warnings)[0]
|
||||
} else {
|
||||
result.color = json.color
|
||||
}
|
||||
return { result, errors, warnings }
|
||||
}
|
||||
}
|
||||
|
||||
export class PrepareLayer extends Fuse<LayerConfigJson> {
|
||||
constructor(state: DesugaringContext) {
|
||||
super(
|
||||
|
@ -1274,9 +1311,13 @@ export class PrepareLayer extends Fuse<LayerConfigJson> {
|
|||
new AddMiniMap(state),
|
||||
new AddEditingElements(state),
|
||||
new SetFullNodeDatabase(),
|
||||
new On("mapRendering", new Concat(new ExpandRewrite()).andThenF(Utils.Flatten)),
|
||||
new On<(PointRenderingConfigJson | LineRenderingConfigJson)[], LayerConfigJson>(
|
||||
"mapRendering",
|
||||
new On<PointRenderingConfigJson[], LayerConfigJson>(
|
||||
"pointRendering",
|
||||
(layer) =>
|
||||
new Each(new On("marker", new Each(new ExpandMarkerRenderings(state, layer))))
|
||||
),
|
||||
new On<PointRenderingConfigJson[], LayerConfigJson>(
|
||||
"pointRendering",
|
||||
(layer) => new Each(new PreparePointRendering(state, layer))
|
||||
),
|
||||
new SetDefault("titleIcons", ["icons.defaults"]),
|
||||
|
|
|
@ -115,6 +115,7 @@ export default class DeleteConfig {
|
|||
const config: QuestionableTagRenderingConfigJson = {
|
||||
question: t.whyDelete.translations,
|
||||
mappings,
|
||||
id: "why-delete",
|
||||
}
|
||||
return new TagRenderingConfig(config)
|
||||
}
|
||||
|
|
|
@ -9,10 +9,6 @@ import LineRenderingConfigJson from "./LineRenderingConfigJson"
|
|||
import { QuestionableTagRenderingConfigJson } from "./QuestionableTagRenderingConfigJson"
|
||||
import RewritableConfigJson from "./RewritableConfigJson"
|
||||
import { Translatable } from "./Translatable"
|
||||
import { Point } from "geojson"
|
||||
import PointRenderingConfig from "../PointRenderingConfig"
|
||||
|
||||
type MapRendering = {}
|
||||
|
||||
/**
|
||||
* Configuration for a single layer
|
||||
|
@ -49,6 +45,7 @@ export interface LayerConfigJson {
|
|||
/**
|
||||
*
|
||||
* Question: Where should the data be fetched from?
|
||||
* title: Data Source
|
||||
*
|
||||
* This determines where the data for the layer is fetched: from OSM or from an external geojson dataset.
|
||||
*
|
||||
|
@ -242,12 +239,12 @@ export interface LayerConfigJson {
|
|||
/**
|
||||
* Creates points to render on the map.
|
||||
* This can render points for point-objects, lineobjects or areaobjects; use 'location' to indicate where it should be rendered
|
||||
* group: maprendering
|
||||
* group: pointrendering
|
||||
*/
|
||||
pointRendering: PointRenderingConfigJson[]
|
||||
/**
|
||||
* Creates lines and areas to render on the map
|
||||
* group: maprendering
|
||||
* group: linerendering
|
||||
*/
|
||||
lineRendering?: LineRenderingConfigJson[]
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { TagRenderingConfigJson } from "./TagRenderingConfigJson"
|
||||
import { MinimalTagRenderingConfigJson } from "./TagRenderingConfigJson"
|
||||
|
||||
/**
|
||||
* The LineRenderingConfig gives all details onto how to render a single line of a feature.
|
||||
|
@ -10,40 +10,69 @@ import { TagRenderingConfigJson } from "./TagRenderingConfigJson"
|
|||
*/
|
||||
export default interface LineRenderingConfigJson {
|
||||
/**
|
||||
* The color for way-elements and SVG-elements.
|
||||
* question: What color should lines be drawn in?
|
||||
*
|
||||
* For an area, this will be the colour of the outside line.
|
||||
* If the value starts with "--", the style of the body element will be queried for the corresponding variable instead
|
||||
*
|
||||
* types: dynamic value ; string
|
||||
* title: Line Colour
|
||||
* inline: The line colour always is <b>{value}</b>
|
||||
* ifunset: Round ending
|
||||
* type: color
|
||||
*
|
||||
*/
|
||||
color?: string | TagRenderingConfigJson
|
||||
color?: MinimalTagRenderingConfigJson | string
|
||||
/**
|
||||
* question: How wide should the line be?
|
||||
* The stroke-width for way-elements
|
||||
*
|
||||
* types: dynamic value ; string
|
||||
* title: Line width
|
||||
* inline: The line width is <b>{value} pixels</b>
|
||||
* type: pnat
|
||||
* ifunset: Use the default-linewidth of 7 pixels
|
||||
*/
|
||||
width?: string | number | TagRenderingConfigJson
|
||||
width?: MinimalTagRenderingConfigJson | number | string
|
||||
|
||||
/**
|
||||
* A dasharray, e.g. "5 6"
|
||||
* The dasharray defines 'pixels of line, pixels of gap, pixels of line, pixels of gap',
|
||||
* Default value: "" (empty string == full line)
|
||||
* question: Should a dasharray be used to render the lines?
|
||||
* The dasharray defines 'pixels of line, pixels of gap, pixels of line, pixels of gap, ...'. For example, `5 6` will be 5 pixels of line followed by a 6 pixel gap.
|
||||
* Cannot be a dynamic property due to a mapbox limitation
|
||||
* ifunset: Ways are rendered with a full line
|
||||
*/
|
||||
dashArray?: string | TagRenderingConfigJson
|
||||
dashArray?: string
|
||||
|
||||
/**
|
||||
* The form at the end of a line
|
||||
*/
|
||||
lineCap?: "round" | "square" | "butt" | string | TagRenderingConfigJson
|
||||
* question: What form should the line-ending have?
|
||||
* suggestions: return [{if:"value=round",then:"Round endings"}, {if: "value=square", then: "square endings"}, {if: "value=butt", then: "no ending (square ending at the end, without padding)"}]
|
||||
* types: dynamic value ; string
|
||||
* title: Line Cap
|
||||
* ifunset: Use the default value (round ending)
|
||||
**/
|
||||
lineCap?: "round" | "square" | "butt" | string | MinimalTagRenderingConfigJson
|
||||
|
||||
/**
|
||||
* The color to fill a polygon with.
|
||||
* If undefined, this will be slightly more opaque version of the stroke line.
|
||||
* Use '#00000000' to make the fill invisible
|
||||
* question: What colour should be used as fill colour for polygons?
|
||||
* ifunset: The polygon fill colour will be a more transparent version of the stroke colour
|
||||
* suggestions: return [{if: "value=#00000000", then: "Use a transparent fill (only render the outline)"}]
|
||||
* inline: The fill colour is <b>{value}</b>
|
||||
* types: dynamic value ; string
|
||||
* type: color
|
||||
*/
|
||||
fillColor?: string | TagRenderingConfigJson
|
||||
fillColor?: string | MinimalTagRenderingConfigJson
|
||||
|
||||
/**
|
||||
* question: Should the lines be moved (offsetted) with a number of pixels against the geographical lines?
|
||||
* The number of pixels this line should be moved.
|
||||
* Use a positive numbe to move to the right, a negative to move to the left (left/right as defined by the drawing direction of the line).
|
||||
* Use a positive number to move to the right in the drawing direction or a negative to move to the left (left/right as defined by the drawing direction of the line).
|
||||
*
|
||||
* IMPORTANT: MapComplete will already normalize 'key:both:property' and 'key:both' into the corresponding 'key:left' and 'key:right' tagging (same for 'sidewalk=left/right/both' which is rewritten to 'sidewalk:left' and 'sidewalk:right')
|
||||
* This simplifies programming. Refer to the CalculatedTags.md-documentation for more details
|
||||
* ifunset: don't offset lines on the map
|
||||
* inline: Pixel offset by <b>{value}</b> pixels
|
||||
* types: dynamic value ; number
|
||||
* type: int
|
||||
*/
|
||||
offset?: number | TagRenderingConfigJson
|
||||
offset?: number | MinimalTagRenderingConfigJson
|
||||
}
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
import { TagRenderingConfigJson } from "./TagRenderingConfigJson"
|
||||
import { TagConfigJson } from "./TagConfigJson"
|
||||
|
||||
export interface IconConfigJson {
|
||||
icon: string | TagRenderingConfigJson | { builtin: string; override: any }
|
||||
color?: string | TagRenderingConfigJson | { builtin: string; override: any }
|
||||
}
|
||||
|
||||
/**
|
||||
* The PointRenderingConfig gives all details onto how to render a single point of a feature.
|
||||
*
|
||||
|
@ -11,28 +16,26 @@ import { TagConfigJson } from "./TagConfigJson"
|
|||
*/
|
||||
export default interface PointRenderingConfigJson {
|
||||
/**
|
||||
* All the locations that this point should be rendered at.
|
||||
* Possible values are:
|
||||
* - `point`: only renders points at their location
|
||||
* - `centroid`: show a symbol at the centerpoint of a (multi)Linestring and (multi)polygon. Points will _not_ be rendered with this
|
||||
* - `projected_centerpoint`: Only on (multi)linestrings: calculate the centerpoint and snap it to the way
|
||||
* - `start` and `end`: only on linestrings: add a point to the first/last coordinate of the LineString
|
||||
* question: At what location should this icon be shown?
|
||||
* multianswer: true
|
||||
* suggestions: return [{if: "value=point",then: "Show an icon for point (node) objects"},{if: "value=centroid",then: "Show an icon for line or polygon (way) objects at their centroid location"}, {if: "value=start",then: "Show an icon for line (way) objects at the start"},{if: "value=end",then: "Show an icon for line (way) object at the end"},{if: "value=projected_centerpoint",then: "Show an icon for line (way) object near the centroid location, but moved onto the line"}]
|
||||
*/
|
||||
location: ("point" | "centroid" | "start" | "end" | "projected_centerpoint" | string)[]
|
||||
|
||||
/**
|
||||
* The icon for an element.
|
||||
* Note that this also doubles as the icon for this layer (rendered with the overpass-tags) ánd the icon in the presets.
|
||||
*
|
||||
* The result of the icon is rendered as follows:
|
||||
* the resulting string is interpreted as a _list_ of items, separated by ";". The bottommost layer is the first layer.
|
||||
* As a result, on could use a generic pin, then overlay it with a specific icon.
|
||||
* To make things even more practical, one can use all SVG's from the folder "assets/svg" and _substitute the color_ in it.
|
||||
* E.g. to draw a red pin, use "pin:#f00", to have a green circle with your icon on top, use `circle:#0f0;<path to my icon.svg>`
|
||||
* question: What marker should be used to
|
||||
* The icon for an element.
|
||||
* Note that this also doubles as the icon for this layer (rendered with the overpass-tags) ánd the icon in the presets.
|
||||
*
|
||||
* The result of the icon is rendered as follows:
|
||||
* the resulting string is interpreted as a _list_ of items, separated by ";". The bottommost layer is the first layer.
|
||||
* As a result, on could use a generic pin, then overlay it with a specific icon.
|
||||
* To make things even more practical, one c an use all SVG's from the folder "assets/svg" and _substitute the color_ in it.
|
||||
* E.g. to draw a red pin, use "pin:#f00", to have a green circle with your icon on top, use `circle:#0f0;<path to my icon.svg>`
|
||||
|
||||
* Type: icon
|
||||
*/
|
||||
icon?: string | TagRenderingConfigJson
|
||||
* Type: icon
|
||||
*/
|
||||
marker?: IconConfigJson[]
|
||||
|
||||
/**
|
||||
* A list of extra badges to show next to the icon as small badge
|
||||
|
@ -59,8 +62,9 @@ export default interface PointRenderingConfigJson {
|
|||
* question: What is the anchorpoint of the icon?
|
||||
*
|
||||
* This matches the geographical point with a location on the icon.
|
||||
* For example, a feature attached to the ground can use 'bottom' as zooming in will give the appearance of being anchored to a fixed location.
|
||||
*
|
||||
* ifunset: Use MapComplete-default (<b>center</b>)
|
||||
* suggestions: return [{if: "value=center", then: "Place the <b>center</b> of the icon on the geographical location"},{if: "value=top", then: "Place the <b>top</b> of the icon on the geographical location"},{if: "value=bottom", then: "Place the <b>bottom</b> of the icon on the geographical location"},{if: "value=left", then: "Place the <b>left</b> of the icon on the geographical location"},{if: "value=right", then: "Place the <b>right</b> of the icon on the geographical location"}]
|
||||
*/
|
||||
anchor?: "center" | "top" | "bottom" | "left" | "right" | string | TagRenderingConfigJson
|
||||
|
||||
|
@ -70,31 +74,52 @@ export default interface PointRenderingConfigJson {
|
|||
*/
|
||||
rotation?: string | TagRenderingConfigJson
|
||||
/**
|
||||
* A HTML-fragment that is shown below the icon, for example:
|
||||
* <div style="background: white">{name}</div>
|
||||
* question: What label should be shown beneath the marker?
|
||||
* For example: <div style="background: white">{name}</div>
|
||||
*
|
||||
* If the icon is undefined, then the label is shown in the center of the feature.
|
||||
* Note that, if the wayhandling hides the icon then no label is shown as well.
|
||||
* types: Dynamic value | string
|
||||
* inline: Always show label <b>{value}</b> beneath the marker
|
||||
*/
|
||||
label?: string | TagRenderingConfigJson
|
||||
|
||||
/**
|
||||
* A snippet of css code which is applied onto the container of the entire marker
|
||||
* question: What CSS should be applied to the entire marker?
|
||||
* You can set the css-properties here, e.g. `background: red; font-size: 12px; `
|
||||
* This will be applied to the _container_ containing both the marker and the label
|
||||
* inline: Apply CSS-style <b>{value}</b> to the _entire marker_
|
||||
* types: Dynamic value ; string
|
||||
*/
|
||||
css?: string | TagRenderingConfigJson
|
||||
|
||||
/**
|
||||
* A snippet of css-classes which are applied onto the container of the entire marker. They can be space-separated
|
||||
* question: Which CSS-classes should be applied to the entire marker?
|
||||
* This will be applied to the _container_ containing both the marker and the label
|
||||
*
|
||||
* The classes should be separated by a space (` `)
|
||||
* You can use most Tailwind-css classes, see https://tailwindcss.com/ for more information
|
||||
* For example: `center bg-gray-500 mx-2 my-1 rounded-full`
|
||||
* inline: Apply CSS-classes <b>{value}</b> to the entire container
|
||||
* types: Dynamic value ; string
|
||||
*/
|
||||
cssClasses?: string | TagRenderingConfigJson
|
||||
|
||||
/**
|
||||
* Css that is applied onto the label
|
||||
* question: What CSS should be applied to the label?
|
||||
* You can set the css-properties here, e.g. `background: red; font-size: 12px; `
|
||||
* inline: Apply CSS-style <b>{value}</b> to the label
|
||||
* types: Dynamic value ; string
|
||||
*/
|
||||
labelCss?: string | TagRenderingConfigJson
|
||||
labelCss?: TagRenderingConfigJson | string
|
||||
|
||||
/**
|
||||
* Css classes that are applied onto the label; can be space-separated
|
||||
* question: Which CSS-classes should be applied to the label?
|
||||
*
|
||||
* The classes should be separated by a space (` `)
|
||||
* You can use most Tailwind-css classes, see https://tailwindcss.com/ for more information
|
||||
* For example: `center bg-gray-500 mx-2 my-1 rounded-full`
|
||||
* inline: Apply CSS-classes <b>{value}</b> to the label
|
||||
* types: Dynamic value ; string
|
||||
*/
|
||||
labelCssClasses?: string | TagRenderingConfigJson
|
||||
|
||||
|
@ -105,7 +130,9 @@ export default interface PointRenderingConfigJson {
|
|||
pitchAlignment?: "canvas" | "map" | TagRenderingConfigJson
|
||||
|
||||
/**
|
||||
* If the map is rotated, the icon will still point to the north if no rotation was applied
|
||||
* question: Should the icon be rotated or tilted if the map is rotated or tilted?
|
||||
* ifunset: Do not rotate or tilt icons. Always keep the icons straight
|
||||
* suggestions: return [{if: "value=canvas", then: "If the map is tilted, tilt the icon as well. This gives the impression of an icon that is glued to the ground."}, {if: "value=map", then: "If the map is rotated, rotate the icon as well. This gives the impression of an icon that floats perpendicular above the ground."}]
|
||||
*/
|
||||
rotationAlignment?: "map" | "canvas" | TagRenderingConfigJson
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { TagConfigJson } from "./TagConfigJson"
|
||||
import { TagRenderingConfigJson } from "./TagRenderingConfigJson"
|
||||
import { Translatable } from "./Translatable"
|
||||
import type { Translatable } from "./Translatable"
|
||||
|
||||
export interface MappingConfigJson {
|
||||
/**
|
||||
|
|
|
@ -1,6 +1,43 @@
|
|||
import { TagConfigJson } from "./TagConfigJson"
|
||||
import { Translatable } from "./Translatable"
|
||||
|
||||
/**
|
||||
* Mostly used for lineRendering and pointRendering
|
||||
*/
|
||||
export interface MinimalTagRenderingConfigJson {
|
||||
/**
|
||||
* question: What value should be rendered?
|
||||
*
|
||||
* This piece of text will be shown in the infobox.
|
||||
* Note that "&LBRACEkey&RBRACE"-parts are substituted by the corresponding values of the element.
|
||||
*
|
||||
* This value will be used if there is no mapping which matches (or there are no matches)
|
||||
* Note that this is a HTML-interpreted value, so you can add links as e.g. '<a href='{website}'>{website}</a>' or include images such as `This is of type A <br><img src='typeA-icon.svg' />`
|
||||
*/
|
||||
render?: string
|
||||
/**
|
||||
* Allows fixed-tag inputs, shown either as radiobuttons or as checkboxes
|
||||
*/
|
||||
mappings?: {
|
||||
/**
|
||||
* question: When should this single mapping match?
|
||||
*
|
||||
* If this condition is met, then the text under `then` will be shown.
|
||||
* If no value matches, and the user selects this mapping as an option, then these tags will be uploaded to OSM.
|
||||
*
|
||||
* For example: {'if': 'diet:vegetarion=yes', 'then':'A vegetarian option is offered here'}
|
||||
*
|
||||
* This can be an substituting-tag as well, e.g. {'if': 'addr:street:={_calculated_nearby_streetname}', 'then': '{_calculated_nearby_streetname}'}
|
||||
*/ if: TagConfigJson
|
||||
/**
|
||||
* question: What text should be shown?
|
||||
*
|
||||
* If the condition `if` is met, the text `then` will be rendered.
|
||||
* If not known yet, the user will be presented with `then` as an option
|
||||
*/ then: string
|
||||
}[]
|
||||
}
|
||||
|
||||
/**
|
||||
* A TagRenderingConfigJson is a single piece of code which converts one ore more tags into a HTML-snippet.
|
||||
* For an _editable_ tagRendering, use 'QuestionableTagRenderingConfigJson' instead, which extends this one
|
||||
|
|
|
@ -75,7 +75,6 @@ export default class LayerConfig extends WithContextLoader {
|
|||
const translationContext = "layers:" + json.id
|
||||
super(json, context)
|
||||
this.id = json.id
|
||||
|
||||
if (typeof json === "string") {
|
||||
throw `Not a valid layer: the layerConfig is a string. 'npm run generate:layeroverview' might be needed (at ${context})`
|
||||
}
|
||||
|
@ -295,7 +294,9 @@ export default class LayerConfig extends WithContextLoader {
|
|||
throw (
|
||||
"The layer " +
|
||||
this.id +
|
||||
" does not have any maprenderings defined and will thus not show up on the map at all. If this is intentional, set `pointRendering` and `lineRendering` to 'null' instead of '[]'"
|
||||
` does not have any maprenderings defined and will thus not show up on the map at all:
|
||||
\t ${this.lineRendering?.length} linerenderings and ${this.mapRendering?.length} pointRenderings.
|
||||
\t If this is intentional, set \`pointRendering\` and \`lineRendering\` to 'null' instead of '[]'`
|
||||
)
|
||||
} else if (
|
||||
!hasCenterRendering &&
|
||||
|
@ -403,7 +404,7 @@ export default class LayerConfig extends WithContextLoader {
|
|||
if (mapRendering === undefined) {
|
||||
return undefined
|
||||
}
|
||||
return mapRendering.GetBaseIcon(this.GetBaseTags(), { noFullWidth: true })
|
||||
return mapRendering.GetBaseIcon(this.GetBaseTags())
|
||||
}
|
||||
|
||||
public GetBaseTags(): Record<string, string> {
|
||||
|
@ -555,23 +556,12 @@ export default class LayerConfig extends WithContextLoader {
|
|||
|
||||
let iconImg: BaseUIElement = new FixedUiElement("")
|
||||
|
||||
if (Utils.runningFromConsole) {
|
||||
const icon = this.mapRendering
|
||||
.filter((mr) => mr.location.has("point"))
|
||||
.map((mr) => mr.icon?.render?.txt)
|
||||
.find((i) => i !== undefined)
|
||||
// This is for the documentation in a markdown-file, so we have to use raw HTML
|
||||
if (icon !== undefined) {
|
||||
iconImg = new FixedUiElement(
|
||||
`<img src='https://mapcomplete.org/${icon}' height="100px"> `
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if (!Utils.runningFromConsole) {
|
||||
iconImg = this.mapRendering
|
||||
.filter((mr) => mr.location.has("point"))
|
||||
.map(
|
||||
(mr) =>
|
||||
mr.RenderIcon(new ImmutableStore<OsmTags>({ id: "node/-1" }), false, {
|
||||
mr.RenderIcon(new ImmutableStore<OsmTags>({ id: "node/-1" }), {
|
||||
includeBadges: false,
|
||||
}).html
|
||||
)
|
||||
|
|
|
@ -11,6 +11,27 @@ import { FixedUiElement } from "../../UI/Base/FixedUiElement"
|
|||
import Img from "../../UI/Base/Img"
|
||||
import Combine from "../../UI/Base/Combine"
|
||||
import { VariableUiElement } from "../../UI/Base/VariableUIElement"
|
||||
import { TagRenderingConfigJson } from "./Json/TagRenderingConfigJson"
|
||||
import SvelteUIElement from "../../UI/Base/SvelteUIElement"
|
||||
import Marker from "../../UI/Map/Marker.svelte"
|
||||
|
||||
export class IconConfig extends WithContextLoader {
|
||||
public readonly icon: TagRenderingConfig
|
||||
public readonly color: TagRenderingConfig
|
||||
|
||||
public static readonly defaultIcon = new IconConfig({ icon: "pin", color: "#ff9939" })
|
||||
constructor(
|
||||
config: {
|
||||
icon: string | TagRenderingConfigJson
|
||||
color?: string | TagRenderingConfigJson
|
||||
},
|
||||
context?: string
|
||||
) {
|
||||
super(config, context)
|
||||
this.icon = this.tr("icon")
|
||||
this.color = this.tr("color")
|
||||
}
|
||||
}
|
||||
|
||||
export default class PointRenderingConfig extends WithContextLoader {
|
||||
static readonly allowed_location_codes: ReadonlySet<string> = new Set<string>([
|
||||
|
@ -24,7 +45,8 @@ export default class PointRenderingConfig extends WithContextLoader {
|
|||
"point" | "centroid" | "start" | "end" | "projected_centerpoint" | string
|
||||
>
|
||||
|
||||
public readonly icon?: TagRenderingConfig
|
||||
// public readonly icon?: TagRenderingConfig
|
||||
private readonly marker: IconConfig[]
|
||||
public readonly iconBadges: { if: TagsFilter; then: TagRenderingConfig }[]
|
||||
public readonly iconSize: TagRenderingConfig
|
||||
public readonly anchor: TagRenderingConfig
|
||||
|
@ -42,7 +64,7 @@ export default class PointRenderingConfig extends WithContextLoader {
|
|||
super(json, context)
|
||||
|
||||
if (json === undefined || json === null) {
|
||||
throw "Invalid PointRenderingConfig: undefined or null"
|
||||
throw `At ${context}: Invalid PointRenderingConfig: undefined or null`
|
||||
}
|
||||
|
||||
if (typeof json.location === "string") {
|
||||
|
@ -60,8 +82,8 @@ export default class PointRenderingConfig extends WithContextLoader {
|
|||
}
|
||||
})
|
||||
|
||||
if (json.icon === undefined && json.label === undefined) {
|
||||
throw `A point rendering should define at least an icon or a label`
|
||||
if (json.marker === undefined && json.label === undefined) {
|
||||
throw `${context}: A point rendering should define at least an icon or a marker`
|
||||
}
|
||||
|
||||
if (this.location.size == 0) {
|
||||
|
@ -71,7 +93,7 @@ export default class PointRenderingConfig extends WithContextLoader {
|
|||
".location)"
|
||||
)
|
||||
}
|
||||
this.icon = this.tr("icon", undefined)
|
||||
this.marker = (json.marker ?? []).map((m) => new IconConfig(m))
|
||||
if (json.css !== undefined) {
|
||||
this.cssDef = this.tr("css", undefined)
|
||||
}
|
||||
|
@ -85,13 +107,6 @@ export default class PointRenderingConfig extends WithContextLoader {
|
|||
}
|
||||
})
|
||||
|
||||
const iconPath = this.icon?.GetRenderValue({ id: "node/-1" })?.txt
|
||||
if (iconPath !== undefined && iconPath.startsWith(Utils.assets_path)) {
|
||||
const iconKey = iconPath.substr(Utils.assets_path.length)
|
||||
if (Svg.All[iconKey] === undefined) {
|
||||
throw context + ": builtin SVG asset not found: " + iconPath
|
||||
}
|
||||
}
|
||||
if (typeof json.iconSize === "string") {
|
||||
const s = json.iconSize
|
||||
if (["bottom", "top", "center"].some((e) => s.endsWith(e))) {
|
||||
|
@ -176,49 +191,11 @@ export default class PointRenderingConfig extends WithContextLoader {
|
|||
}
|
||||
}
|
||||
|
||||
public GetBaseIcon(
|
||||
tags?: Record<string, string>,
|
||||
options?: {
|
||||
noFullWidth?: boolean
|
||||
}
|
||||
): BaseUIElement {
|
||||
tags = tags ?? { id: "node/-1" }
|
||||
let defaultPin: BaseUIElement = undefined
|
||||
if (this.label === undefined) {
|
||||
defaultPin = Svg.teardrop_with_hole_green_svg()
|
||||
}
|
||||
if (this.icon === undefined) {
|
||||
return defaultPin
|
||||
}
|
||||
const rotation = Utils.SubstituteKeys(
|
||||
this.rotation?.GetRenderValue(tags)?.txt ?? "0deg",
|
||||
tags
|
||||
)
|
||||
const htmlDefs = Utils.SubstituteKeys(this.icon?.GetRenderValue(tags)?.txt, tags)
|
||||
if (htmlDefs === undefined) {
|
||||
// This layer doesn't want to show an icon right now
|
||||
return undefined
|
||||
}
|
||||
if (htmlDefs.startsWith("<") && htmlDefs.endsWith(">")) {
|
||||
// This is probably already prepared HTML
|
||||
return new FixedUiElement(Utils.SubstituteKeys(htmlDefs, tags))
|
||||
}
|
||||
return PointRenderingConfig.FromHtmlMulti(htmlDefs, rotation, false, defaultPin, options)
|
||||
public GetBaseIcon(tags?: Record<string, string>): BaseUIElement {
|
||||
return new SvelteUIElement(Marker, { icons: this.marker, tags })
|
||||
}
|
||||
|
||||
public GetSimpleIcon(tags: Store<Record<string, string>>): BaseUIElement {
|
||||
const self = this
|
||||
if (this.icon === undefined) {
|
||||
return undefined
|
||||
}
|
||||
return new VariableUiElement(tags.map((tags) => self.GetBaseIcon(tags))).SetClass(
|
||||
"w-full h-full block"
|
||||
)
|
||||
}
|
||||
|
||||
public RenderIcon(
|
||||
tags: Store<Record<string, string>>,
|
||||
clickable: boolean,
|
||||
options?: {
|
||||
noSize?: false | boolean
|
||||
includeBadges?: true | boolean
|
||||
|
@ -235,7 +212,7 @@ export default class PointRenderingConfig extends WithContextLoader {
|
|||
return n
|
||||
}
|
||||
|
||||
function render(tr: TagRenderingConfig, deflt?: string) {
|
||||
function render(tr: TagRenderingConfig, deflt?: string): string {
|
||||
if (tags === undefined) {
|
||||
return deflt
|
||||
}
|
||||
|
@ -267,7 +244,7 @@ export default class PointRenderingConfig extends WithContextLoader {
|
|||
anchorH = -iconH / 2
|
||||
}
|
||||
|
||||
const icon = this.GetSimpleIcon(tags)
|
||||
const icon = new SvelteUIElement(Marker, { config: this, tags }).SetClass("w-full h-full")
|
||||
let badges = undefined
|
||||
if (options?.includeBadges ?? true) {
|
||||
badges = this.GetBadges(tags)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue