forked from MapComplete/MapComplete
More work on GRB theme, add 'apply_action' button
This commit is contained in:
parent
a456842773
commit
6c39f563b6
6 changed files with 275 additions and 66 deletions
|
@ -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)
|
||||||
|
|
|
@ -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
|
|
@ -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");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -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"
|
||||||
],
|
],
|
||||||
|
|
|
@ -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.",
|
||||||
|
|
|
@ -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"]);
|
||||||
|
|
Loading…
Reference in a new issue