More work on GRB theme, add 'apply_action' button

This commit is contained in:
pietervdvn 2021-10-29 18:16:51 +02:00
parent a456842773
commit 6c39f563b6
6 changed files with 275 additions and 66 deletions

View file

@ -183,7 +183,7 @@ Some advanced functions are available on **feat** as well:
### overlapWith ### overlapWith
Gives a list of features from the specified layer which this feature (partly) overlaps with. If the current feature is a point, all features that embed the point are given. The returned value is `{ feat: GeoJSONFeature, overlap: number}[]` where `overlap` is the overlapping surface are (in m²) for areas, the overlapping length (in meter) if the current feature is a line or `undefined` if the current feature is a point. Gives a list of features from the specified layer which this feature (partly) overlaps with. If the current feature is a point, all features that embed the point are given. The returned value is `{ feat: GeoJSONFeature, overlap: number}[]` where `overlap` is the overlapping surface are (in m²) for areas, the overlapping length (in meter) if the current feature is a line or `undefined` if the current feature is a point.
The resulting list is sorted in descending order by overlap. The feature with the most overlap will thus be the first in the list
For example to get all objects which overlap or embed from a layer, use `_contained_climbing_routes_properties=feat.overlapWith('climbing_route')` For example to get all objects which overlap or embed from a layer, use `_contained_climbing_routes_properties=feat.overlapWith('climbing_route')`
0. ...layerIds - one or more layer ids of the layer from which every feature is checked for overlap) 0. ...layerIds - one or more layer ids of the layer from which every feature is checked for overlap)

View file

@ -23,6 +23,7 @@ General usage is `{func_name()}`, `{func_name(arg, someotherarg)}` or `{func_nam
- [canonical](#canonical) - [canonical](#canonical)
- [import_button](#import_button) - [import_button](#import_button)
- [multi_apply](#multi_apply) - [multi_apply](#multi_apply)
- [tag_apply](#tag_apply)
@ -191,6 +192,8 @@ key | _undefined_ | The key of the tag to give the canonical text for
This button will copy the data from an external dataset into OpenStreetMap. It is only functional in official themes but can be tested in unofficial themes. This button will copy the data from an external dataset into OpenStreetMap. It is only functional in official themes but can be tested in unofficial themes.
#### Importing a dataset into OpenStreetMap: requirements
If you want to import a dataset, make sure that: If you want to import a dataset, make sure that:
1. The dataset to import has a suitable license 1. The dataset to import has a suitable license
@ -199,17 +202,42 @@ If you want to import a dataset, make sure that:
There are also some technicalities in your theme to keep in mind: There are also some technicalities in your theme to keep in mind:
1. The new point will be added and will flow through the program as any other new point as if it came from OSM. 1. The new feature will be added and will flow through the program as any other new point as if it came from OSM.
This means that there should be a layer which will match the new tags and which will display it. This means that there should be a layer which will match the new tags and which will display it.
2. The original point from your geojson layer will gain the tag '_imported=yes'. 2. The original feature from your geojson layer will gain the tag '_imported=yes'.
This should be used to change the appearance or even to hide it (eg by changing the icon size to zero) This should be used to change the appearance or even to hide it (eg by changing the icon size to zero)
3. There should be a way for the theme to detect previously imported points, even after reloading. 3. There should be a way for the theme to detect previously imported points, even after reloading.
A reference number to the original dataset is an excellen way to do this A reference number to the original dataset is an excellent way to do this
4. When importing ways, the theme creator is also responsible of avoiding overlapping ways.
#### Disabled in unofficial themes
The import button can be tested in an unofficial theme by adding `test=true` or `backend=osm-test` as [URL-paramter](URL_Parameters.md).
The import button will show up then. If in testmode, you can read the changeset-XML directly in the web console.
In the case that MapComplete is pointed to the testing grounds, the edit will be made on https://master.apis.dev.openstreetmap.org
#### Specifying which tags to copy or add
The first argument of the import button takes a `;`-seperated list of tags to add.
These can either be a tag to add, such as `amenity=fast_food` or can use a substitution, e.g. `addr:housenumber=$number`.
This new point will then have the tags `amenity=fast_food` and `addr:housenumber` with the value that was saved in `number` in the original feature.
If a value to substitute is undefined, empty string will be used instead.
This supports multiple values, e.g. `ref=$source:geometry:type/$source:geometry:ref`
Remark that the syntax is slightly different then expected; it uses '$' to note a value to copy, followed by a name (matched with `[a-zA-Z0-9_:]*`). Sadly, delimiting with `{}` as these already mark the boundaries of the special rendering...
Note that these values can be prepare with javascript in the theme by using a [calculatedTag](calculatedTags.md#calculating-tags-with-javascript)
name | default | description name | default | description
------ | --------- | ------------- ------ | --------- | -------------
tags | _undefined_ | Tags to copy-specification. This contains one or more pairs (seperated by a `;`), e.g. `amenity=fast_food; addr:housenumber=$number`. This new point will then have the tags `amenity=fast_food` and `addr:housenumber` with the value that was saved in `number` in the original feature. (Hint: prepare these values, e.g. with calculatedTags) tags | _undefined_ | The tags to add onto the new object - see specification above
text | Import this data into OpenStreetMap | The text to show on the button text | Import this data into OpenStreetMap | The text to show on the button
icon | ./assets/svg/addSmall.svg | A nice icon to show in the button icon | ./assets/svg/addSmall.svg | A nice icon to show in the button
minzoom | 18 | How far the contributor must zoom in before being able to import the point minzoom | 18 | How far the contributor must zoom in before being able to import the point
@ -233,4 +261,33 @@ overwrite | _undefined_ | If set to 'true', the tags on the other objects will a
#### Example usage #### Example usage
{multi_apply(_features_with_the_same_name_within_100m, name:etymology:wikidata;name:etymology, Apply etymology information on all nearby objects with the same name)} Generated from UI/SpecialVisualisations.ts {multi_apply(_features_with_the_same_name_within_100m, name:etymology:wikidata;name:etymology, Apply etymology information on all nearby objects with the same name)}
### tag_apply
Shows a big button; clicking this button will apply certain tags onto the feature.
The first argument takes a specification of which tags to add.
These can either be a tag to add, such as `amenity=fast_food` or can use a substitution, e.g. `addr:housenumber=$number`.
This new point will then have the tags `amenity=fast_food` and `addr:housenumber` with the value that was saved in `number` in the original feature.
If a value to substitute is undefined, empty string will be used instead.
This supports multiple values, e.g. `ref=$source:geometry:type/$source:geometry:ref`
Remark that the syntax is slightly different then expected; it uses '$' to note a value to copy, followed by a name (matched with `[a-zA-Z0-9_:]*`). Sadly, delimiting with `{}` as these already mark the boundaries of the special rendering...
Note that these values can be prepare with javascript in the theme by using a [calculatedTag](calculatedTags.md#calculating-tags-with-javascript)
name | default | description
------ | --------- | -------------
tags_to_apply | _undefined_ | A specification of the tags to apply
message | _undefined_ | The text to show to the contributor
image | _undefined_ | An image to show to the contributor on the button
id_of_object_to_apply_this_one | _undefined_ | If specified, applies the the tags onto _another_ object. The id will be read from properties[id_of_object_to_apply_this_one] of the selected object. The tags are still calculated based on the tags of the _selected_ element
#### Example usage
`{tag_apply(survey_date:=$_now:date, Surveyed today!)}` Generated from UI/SpecialVisualisations.ts

View file

@ -34,6 +34,10 @@ import ShowDataLayer from "./ShowDataLayer/ShowDataLayer";
import Link from "./Base/Link"; import Link from "./Base/Link";
import List from "./Base/List"; import List from "./Base/List";
import {OsmConnection} from "../Logic/Osm/OsmConnection"; import {OsmConnection} from "../Logic/Osm/OsmConnection";
import {SubtleButton} from "./Base/SubtleButton";
import ChangeTagAction from "../Logic/Osm/Actions/ChangeTagAction";
import {And} from "../Logic/Tags/And";
import Toggle from "./Input/Toggle";
export interface SpecialVisualization { export interface SpecialVisualization {
funcName: string, funcName: string,
@ -45,6 +49,17 @@ export interface SpecialVisualization {
export default class SpecialVisualizations { export default class SpecialVisualizations {
private static tagsToApplyHelpText = `These can either be a tag to add, such as \`amenity=fast_food\` or can use a substitution, e.g. \`addr:housenumber=$number\`.
This new point will then have the tags \`amenity=fast_food\` and \`addr:housenumber\` with the value that was saved in \`number\` in the original feature.
If a value to substitute is undefined, empty string will be used instead.
This supports multiple values, e.g. \`ref=$source:geometry:type/$source:geometry:ref\`
Remark that the syntax is slightly different then expected; it uses '$' to note a value to copy, followed by a name (matched with \`[a-zA-Z0-9_:]*\`). Sadly, delimiting with \`{}\` as these already mark the boundaries of the special rendering...
Note that these values can be prepare with javascript in the theme by using a [calculatedTag](calculatedTags.md#calculating-tags-with-javascript)
`
public static specialVisualizations: SpecialVisualization[] = public static specialVisualizations: SpecialVisualization[] =
[ [
{ {
@ -222,7 +237,6 @@ export default class SpecialVisualizations {
return minimap; return minimap;
} }
}, },
{ {
funcName: "sided_minimap", funcName: "sided_minimap",
docs: "A small map showing _only one side_ the selected feature. *This features requires to have linerenderings with offset* as only linerenderings with a postive or negative offset will be shown. Note: in most cases, this map will be automatically introduced", docs: "A small map showing _only one side_ the selected feature. *This features requires to have linerenderings with offset* as only linerenderings with a postive or negative offset will be shown. Note: in most cases, this map will be automatically introduced",
@ -305,14 +319,14 @@ export default class SpecialVisualizations {
name: "key", name: "key",
defaultValue: "opening_hours", defaultValue: "opening_hours",
doc: "The tagkey from which the table is constructed." doc: "The tagkey from which the table is constructed."
},{ }, {
name: "prefix", name: "prefix",
defaultValue: "", defaultValue: "",
doc:"Remove this string from the start of the value before parsing. __Note: use `&LPARENs` to indicate `(` if needed__" doc: "Remove this string from the start of the value before parsing. __Note: use `&LPARENs` to indicate `(` if needed__"
},{ }, {
name: "postfix", name: "postfix",
defaultValue: "", defaultValue: "",
doc:"Remove this string from the end of the value before parsing. __Note: use `&RPARENs` to indicate `)` if needed__" doc: "Remove this string from the end of the value before parsing. __Note: use `&RPARENs` to indicate `)` if needed__"
}], }],
example: "A normal opening hours table can be invoked with `{opening_hours_table()}`. A table for e.g. conditional access with opening hours can be `{opening_hours_table(access:conditional, no @ &LPARENS, &RPARENS)}`", example: "A normal opening hours table can be invoked with `{opening_hours_table()}`. A table for e.g. conditional access with opening hours can be `{opening_hours_table(access:conditional, no @ &LPARENS, &RPARENS)}`",
constr: (state: State, tagSource: UIEventSource<any>, args) => { constr: (state: State, tagSource: UIEventSource<any>, args) => {
@ -529,57 +543,21 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
The first argument of the import button takes a \`;\`-seperated list of tags to add. The first argument of the import button takes a \`;\`-seperated list of tags to add.
These can either be a tag to add, such as \`amenity=fast_food\` or can use a substitution, e.g. \`addr:housenumber=$number\`. ${SpecialVisualizations.tagsToApplyHelpText}
This new point will then have the tags \`amenity=fast_food\` and \`addr:housenumber\` with the value that was saved in \`number\` in the original feature.
If a value to substitute is undefined, empty string will be used instead.
This supports multiple values, e.g. \`ref=$source:geometry:type/$source:geometry:ref\`
Remark that the syntax is slightly different then expected; it uses '$' to note a value to copy, followed by a name (matched with \`[a-zA-Z0-9_:]*\`). Sadly, delimiting with \`{}\` as these already mark the boundaries of the special rendering...
Note that these values can be prepare with javascript in the theme by using a [calculatedTag](calculatedTags.md#calculating-tags-with-javascript)
`, `,
constr: (state, tagSource, args) => { constr: (state, tagSource, args) => {
if (!state.layoutToUse.official && !(state.featureSwitchIsTesting.data || state.osmConnection._oauth_config.url === OsmConnection.oauth_configs["osm-test"].url)) { if (!state.layoutToUse.official && !(state.featureSwitchIsTesting.data || state.osmConnection._oauth_config.url === OsmConnection.oauth_configs["osm-test"].url)) {
return new Combine([new FixedUiElement("The import button is disabled for unofficial themes to prevent accidents.").SetClass("alert"), return new Combine([new FixedUiElement("The import button is disabled for unofficial themes to prevent accidents.").SetClass("alert"),
new FixedUiElement("To test, add <b>test=true</b> or <b>backend=osm-test</b> to the URL. The changeset will be printed in the console. Please open a PR to officialize this theme to actually enable the import button.")]) new FixedUiElement("To test, add <b>test=true</b> or <b>backend=osm-test</b> to the URL. The changeset will be printed in the console. Please open a PR to officialize this theme to actually enable the import button.")])
} }
const tgsSpec = args[0].split(";").map(spec => { const rewrittenTags = SpecialVisualizations.generateTagsToApply(args[0], tagSource)
const kv = spec.split("=").map(s => s.trim());
if (kv.length != 2) {
throw "Invalid key spec: multiple '=' found in " + spec
}
return kv
})
const rewrittenTags: UIEventSource<Tag[]> = tagSource.map(tags => {
const newTags: Tag [] = []
for (const [key, value] of tgsSpec) {
if (value.indexOf('$') >= 0) {
let parts = value.split("$")
// THe first of the split won't start with a '$', so no substitution needed
let actualValue = parts[0]
parts.shift()
for (const part of parts) {
const [_, varName, leftOver] = part.match(/([a-zA-Z0-9_:]*)(.*)/)
actualValue += (tags[varName] ?? "") + leftOver
}
newTags.push(new Tag(key, actualValue))
} else {
newTags.push(new Tag(key, value))
}
}
return newTags
})
const id = tagSource.data.id; const id = tagSource.data.id;
const feature = state.allElements.ContainingFeatures.get(id) const feature = state.allElements.ContainingFeatures.get(id)
const minzoom = Number(args[3]) const minzoom = Number(args[3])
const message = args[1] const message = args[1]
const image = args[2] const image = args[2]
return new ImportButton( return new ImportButton(
image, message, tagSource, rewrittenTags, feature, minzoom, state image, message, tagSource, rewrittenTags, feature, minzoom, state
) )
@ -636,12 +614,113 @@ Note that these values can be prepare with javascript in the theme by using a [c
); );
} }
},
{
funcName: "tag_apply",
docs: "Shows a big button; clicking this button will apply certain tags onto the feature.\n\nThe first argument takes a specification of which tags to add.\n" + SpecialVisualizations.tagsToApplyHelpText,
args: [
{
name: "tags_to_apply",
doc: "A specification of the tags to apply"
},
{
name: "message",
doc: "The text to show to the contributor"
},
{
name: "image",
doc: "An image to show to the contributor on the button"
},
{
name: "id_of_object_to_apply_this_one",
defaultValue: undefined,
doc: "If specified, applies the the tags onto _another_ object. The id will be read from properties[id_of_object_to_apply_this_one] of the selected object. The tags are still calculated based on the tags of the _selected_ element"
}
],
example: "`{tag_apply(survey_date:=$_now:date, Surveyed today!)}`",
constr: (state, tags, args) => {
const tagsToApply = SpecialVisualizations.generateTagsToApply(args[0], tags)
const msg = args[1]
let image = args[2]?.trim()
if (image === "" || image === "undefined") {
image = undefined
}
const targetIdKey = args[3]
const t = Translations.t.general.apply_button
const tagsExplanation = new VariableUiElement(tagsToApply.map(tagsToApply => {
const tagsStr = tagsToApply.map(t => t.asHumanString(false, true)).join("&");
let el: BaseUIElement = new FixedUiElement(tagsStr)
if(targetIdKey !== undefined){
const targetId = tags.data[targetIdKey] ?? tags.data.id
el = t.appliedOnAnotherObject.Subs({tags: tagsStr , id: targetId })
}
return el;
}
)).SetClass("subtle")
const applied = new UIEventSource(false)
const applyButton = new SubtleButton(image, new Combine([msg, tagsExplanation]).SetClass("flex flex-col"))
.onClick(() => {
const targetId = tags.data[ targetIdKey] ?? tags.data.id
const changeAction = new ChangeTagAction(targetId,
new And(tagsToApply.data),
tags.data, // We pass in the tags of the selected element, not the tags of the target element!
{
theme: state.layoutToUse.id,
changeType: "answer"
}
)
state.changes.applyAction(changeAction)
applied.setData(true)
})
return new Toggle(
new Toggle(
t.isApplied.SetClass("thanks"),
applyButton,
applied
)
, undefined, state.osmConnection.isLoggedIn)
}
} }
] ]
static HelpMessage: BaseUIElement = SpecialVisualizations.GenHelpMessage(); private static generateTagsToApply(spec: string, tagSource: UIEventSource<any>): UIEventSource<Tag[]> {
private static GenHelpMessage() { const tgsSpec = spec.split(";").map(spec => {
const kv = spec.split("=").map(s => s.trim());
if (kv.length != 2) {
throw "Invalid key spec: multiple '=' found in " + spec
}
return kv
})
return tagSource.map(tags => {
const newTags: Tag [] = []
for (const [key, value] of tgsSpec) {
if (value.indexOf('$') >= 0) {
let parts = value.split("$")
// THe first of the split won't start with a '$', so no substitution needed
let actualValue = parts[0]
parts.shift()
for (const part of parts) {
const [_, varName, leftOver] = part.match(/([a-zA-Z0-9_:]*)(.*)/)
actualValue += (tags[varName] ?? "") + leftOver
}
newTags.push(new Tag(key, actualValue))
} else {
newTags.push(new Tag(key, value))
}
}
return newTags
})
}
public static HelpMessage() {
const helpTexts = const helpTexts =
SpecialVisualizations.specialVisualizations.map(viz => new Combine( SpecialVisualizations.specialVisualizations.map(viz => new Combine(
@ -651,7 +730,7 @@ Note that these values can be prepare with javascript in the theme by using a [c
viz.args.length > 0 ? new Table(["name", "default", "description"], viz.args.length > 0 ? new Table(["name", "default", "description"],
viz.args.map(arg => { viz.args.map(arg => {
let defaultArg = arg.defaultValue ?? "_undefined_" let defaultArg = arg.defaultValue ?? "_undefined_"
if(defaultArg == ""){ if (defaultArg == "") {
defaultArg = "_empty string_" defaultArg = "_empty string_"
} }
return [arg.name, defaultArg, arg.doc]; return [arg.name, defaultArg, arg.doc];
@ -665,9 +744,9 @@ Note that these values can be prepare with javascript in the theme by using a [c
] ]
)); ));
const toc = new List( const toc = new List(
SpecialVisualizations.specialVisualizations.map(viz => new Link(viz.funcName, "#"+viz.funcName)) SpecialVisualizations.specialVisualizations.map(viz => new Link(viz.funcName, "#" + viz.funcName))
) )
return new Combine([ return new Combine([
@ -679,4 +758,5 @@ Note that these values can be prepare with javascript in the theme by using a [c
] ]
).SetClass("flex flex-col"); ).SetClass("flex flex-col");
} }
} }

View file

@ -7,7 +7,8 @@
"nl": "Grb Fixup" "nl": "Grb Fixup"
}, },
"description": { "description": {
"nl": "GRB Fixup" "nl": "GRB Fixup",
"en": "This theme is an attempt to help automating the GRB import.<br/>Note that this is very hacky and 'steals' the GRB data from an external site; in order to do this, you need to install and activate <a href='https://addons.mozilla.org/en-US/firefox/addon/cors-everywhere/'>this firefox extension</a> for it to work."
}, },
"language": [ "language": [
"nl" "nl"
@ -23,6 +24,9 @@
"clustering": { "clustering": {
"maxZoom": 15 "maxZoom": 15
}, },
"overrideAll": {
"minzoom": 18
},
"layers": [ "layers": [
{ {
"id": "OSM-buildings", "id": "OSM-buildings",
@ -31,7 +35,6 @@
"osmTags": "building~*", "osmTags": "building~*",
"maxCacheAge": 0 "maxCacheAge": 0
}, },
"minzoom": 16,
"mapRendering": [ "mapRendering": [
{ {
"width": { "width": {
@ -67,6 +70,55 @@
], ],
"title": "OSM-gebouw", "title": "OSM-gebouw",
"tagRenderings": [ "tagRenderings": [
{
"id": "building type",
"freeform": {
"key": "building"
},
"render": "The building type is <b>{building}</b>",
"mappings": [
{
"if": "building=house",
"then": "A normal house"
},
{
"if": "building=detached",
"then": "A house detached from other building"
},
{
"if": "building=semidetached_house",
"then": "A house sharing only one wall with another house"
},
{
"if": "building=apartments",
"then": "An apartment building - highrise for living"
},
{
"if": "building=office",
"then": "An office building - highrise for work"
},
{
"if": "building=apartments",
"then": "An apartment building"
},
{
"if": "building=shed",
"then": "A small shed, e.g. in a garden"
},
{
"if": "building=garage",
"then": "A single garage to park a car"
},
{
"if": "building=garages",
"then": "A building containing only garages; typically they are all identical"
},
{
"if": "building=yes",
"then": "A building - no specification"
}
]
},
"all_tags" "all_tags"
] ]
}, },
@ -99,7 +151,6 @@
}, },
"maxCacheAge": 0 "maxCacheAge": 0
}, },
"minzoom": 18,
"mapRendering": [ "mapRendering": [
{ {
"color": { "color": {
@ -124,7 +175,6 @@
"name": { "name": {
"nl": "Fixmes op gebouwen" "nl": "Fixmes op gebouwen"
}, },
"minzoom": 21,
"source": { "source": {
"maxCacheAge": 0, "maxCacheAge": 0,
"osmTags": { "osmTags": {
@ -314,7 +364,6 @@
"geoJsonZoomLevel": 18, "geoJsonZoomLevel": 18,
"maxCacheAge": 0 "maxCacheAge": 0
}, },
"minzoom": 16,
"name": "CRAB-addressen", "name": "CRAB-addressen",
"title": "CRAB-adres", "title": "CRAB-adres",
"mapRendering": [ "mapRendering": [
@ -372,7 +421,6 @@
"name": { "name": {
"nl": "Fixmes op gebouwen" "nl": "Fixmes op gebouwen"
}, },
"minzoom": 16,
"source": { "source": {
"maxCacheAge": 0, "maxCacheAge": 0,
"osmTags": { "osmTags": {
@ -563,7 +611,6 @@
}, },
"name": "GRB geometries", "name": "GRB geometries",
"title": "GRB outline", "title": "GRB outline",
"minzoom": 16,
"calculatedTags": [ "calculatedTags": [
"_overlaps_with=feat.overlapWith('OSM-buildings').filter(f => f.overlap > 1 && (feat.get('_surface') < 20 || f.overlap / feat.get('_surface')) > 0.9)[0] ?? null", "_overlaps_with=feat.overlapWith('OSM-buildings').filter(f => f.overlap > 1 && (feat.get('_surface') < 20 || f.overlap / feat.get('_surface')) > 0.9)[0] ?? null",
"_overlap_absolute=feat.get('_overlaps_with')?.overlap", "_overlap_absolute=feat.get('_overlaps_with')?.overlap",
@ -587,6 +634,26 @@
"render": "<div>The overlapping openstreetmap-building is a <b>{_osm_obj:building}</b> and covers <b>{_overlap_percentage}%</b> of the GRB building<div><h3>GRB geometry:</h3>{minimap(21, id):height:10rem;border-radius:1rem;overflow:hidden}<h3>OSM geometry:</h3>{minimap(21,_osm_obj:id):height:10rem;border-radius:1rem;overflow:hidden}", "render": "<div>The overlapping openstreetmap-building is a <b>{_osm_obj:building}</b> and covers <b>{_overlap_percentage}%</b> of the GRB building<div><h3>GRB geometry:</h3>{minimap(21, id):height:10rem;border-radius:1rem;overflow:hidden}<h3>OSM geometry:</h3>{minimap(21,_osm_obj:id):height:10rem;border-radius:1rem;overflow:hidden}",
"condition": "_overlaps_with!=null" "condition": "_overlaps_with!=null"
}, },
{
"id": "apply-id",
"render": "{tag_apply(source:geometry:date=$_grb_date; source:geometry:ref=$_grb_ref,Mark the OSM-building as imported,,_osm_obj:id)}",
"condition": {
"and": [
"_overlaps_with!=null"
]
}
},
{
"id": "apply-building-type",
"render": "{tag_apply(building=$building,Use the building type from GRB,,_osm_obj:id)}",
"condition": {
"and": [
"_overlaps_with!=null",
"_osm_obj:building=yes",
"building!=yes"
]
}
},
{ {
"id": "Import-button", "id": "Import-button",
"render": "{import_button(building=$building; source:geometry:date=$_grb_date; source:geometry:ref=$_grb_ref, Upload this building to OpenStreetMap)}", "render": "{import_button(building=$building; source:geometry:date=$_grb_date; source:geometry:ref=$_grb_ref, Upload this building to OpenStreetMap)}",
@ -594,7 +661,8 @@
{ {
"if": "_overlaps_with!=null", "if": "_overlaps_with!=null",
"then": "Cannot be imported directly, there is a nearly identical building geometry in OpenStreetMap" "then": "Cannot be imported directly, there is a nearly identical building geometry in OpenStreetMap"
}] }
]
}, },
"all_tags" "all_tags"
], ],

View file

@ -131,6 +131,10 @@
"previouslyHiddenTitle": "Previously visited hidden themes", "previouslyHiddenTitle": "Previously visited hidden themes",
"hiddenExplanation": "These themes are only visible if you know the link. You have discovered {hidden_discovered} out of {total_hidden} hidden themes" "hiddenExplanation": "These themes are only visible if you know the link. You have discovered {hidden_discovered} out of {total_hidden} hidden themes"
}, },
"apply_button": {
"isApplied": "The changes are applied",
"appliedOnAnotherObject": "The object {id} will receive {tags}"
},
"sharescreen": { "sharescreen": {
"intro": "<h3>Share this map</h3> Share this map by copying the link below and sending it to friends and family:", "intro": "<h3>Share this map</h3> Share this map by copying the link below and sending it to friends and family:",
"addToHomeScreen": "<h3>Add to your home screen</h3>You can easily add this website to your smartphone home screen for a native feel. Click the 'add to home screen' button in the URL bar to do this.", "addToHomeScreen": "<h3>Add to your home screen</h3>You can easily add this website to your smartphone home screen for a native feel. Click the 'add to home screen' button in the URL bar to do this.",

View file

@ -22,7 +22,7 @@ function WriteFile(filename, html: string | BaseUIElement, autogenSource: string
]).AsMarkdown()); ]).AsMarkdown());
} }
WriteFile("./Docs/SpecialRenderings.md", SpecialVisualizations.HelpMessage, ["UI/SpecialVisualisations.ts"]) WriteFile("./Docs/SpecialRenderings.md", SpecialVisualizations.HelpMessage(), ["UI/SpecialVisualisations.ts"])
WriteFile("./Docs/CalculatedTags.md", new Combine([SimpleMetaTagger.HelpText(), ExtraFunction.HelpText()]).SetClass("flex-col"), WriteFile("./Docs/CalculatedTags.md", new Combine([SimpleMetaTagger.HelpText(), ExtraFunction.HelpText()]).SetClass("flex-col"),
["SimpleMetaTagger", "ExtraFunction"]) ["SimpleMetaTagger", "ExtraFunction"])
WriteFile("./Docs/SpecialInputElements.md", ValidatedTextField.HelpText(), ["ValidatedTextField.ts"]); WriteFile("./Docs/SpecialInputElements.md", ValidatedTextField.HelpText(), ["ValidatedTextField.ts"]);