Merge branch 'develop'

This commit is contained in:
Pieter Vander Vennet 2022-07-31 13:31:34 +02:00
commit 4ffdf20bef
48 changed files with 51320 additions and 454 deletions

View file

@ -1,5 +1,6 @@
Metatags
==========

View file

@ -44,11 +44,23 @@ class StatsDownloader {
if (year === currentYear && month === currentMonth && day === today.getDate() ) {
break;
}
const path = `${this._targetDirectory}/stats.${year}-${month}-${(day < 10 ? "0" : "") + day}.json`
const path = `${this._targetDirectory}/stats.${year}-${month}-${(day < 10 ? "0" : "") + day}.json`
if(existsSync(path)){
console.log("Skipping ", path,": already exists")
continue
}
try{
await this.DownloadStatsForDay(year, month, day, path)
}catch(e){
console.error(e)
console.error("Could not download "+year+"-"+month+"-"+day+"... Trying again")
try{
await this.DownloadStatsForDay(year, month, day, path)
}catch(e){
console.error("Could not download "+year+"-"+month+"-"+day+", skipping for now")
}
}
}
}
}
@ -828,17 +840,21 @@ async function main(): Promise<void> {
if (process.argv.indexOf("--no-graphs") >= 0) {
return
}
await createMiscGraphs(allFeatures, emptyCS)
const grbOnly = allFeatures.filter(f => f.properties.metadata.theme === "grb")
allFeatures = allFeatures.filter(f => f.properties.metadata.theme !== "grb")
await createGraphs(allFeatures, "")
await createGraphs(allFeatures.filter(f => f.properties.date.startsWith("2020")), " in 2020")
await createGraphs(allFeatures.filter(f => f.properties.date.startsWith("2021")), " in 2021")
await createGraphs(allFeatures.filter(f => f.properties.date.startsWith("2022")), " in 2022")
await createGraphs(allFeatures.filter(f => f.properties.metadata.theme === "toerisme_vlaanderen"), " met pin je punt", 0)
await createGraphs(grbOnly, " with the GRB import tool", 0)
const allFiles = readdirSync("Docs/Tools/stats").filter(p => p.endsWith(".json"))
writeFileSync("Docs/Tools/stats/file-overview.json", JSON.stringify(allFiles))
/*
await createMiscGraphs(allFeatures, emptyCS)
const grbOnly = allFeatures.filter(f => f.properties.metadata.theme === "grb")
allFeatures = allFeatures.filter(f => f.properties.metadata.theme !== "grb")
await createGraphs(allFeatures, "")
await createGraphs(allFeatures.filter(f => f.properties.date.startsWith("2020")), " in 2020")
await createGraphs(allFeatures.filter(f => f.properties.date.startsWith("2021")), " in 2021")
await createGraphs(allFeatures.filter(f => f.properties.date.startsWith("2022")), " in 2022")
await createGraphs(allFeatures.filter(f => f.properties.metadata.theme === "toerisme_vlaanderen"), " met pin je punt", 0)
await createGraphs(grbOnly, " with the GRB import tool", 0)
*/
}
main().then(_ => console.log("All done!"))

View file

@ -0,0 +1 @@
["stats.2020-10.json","stats.2020-11.json","stats.2020-12.json","stats.2020-5.json","stats.2020-6.json","stats.2020-7.json","stats.2020-8.json","stats.2020-9.json","stats.2021-1.json","stats.2021-10.json","stats.2021-11.json","stats.2021-12.json","stats.2021-2.json","stats.2021-3.json","stats.2021-4.json","stats.2021-5.json","stats.2021-6.json","stats.2021-7.json","stats.2021-8.json","stats.2021-9.json","stats.2022-1.json","stats.2022-2.json","stats.2022-3.json","stats.2022-4.json","stats.2022-5-01.json","stats.2022-5-02.json","stats.2022-5-03.json","stats.2022-5-04.json","stats.2022-5-05.json","stats.2022-5-06.json","stats.2022-5-07.json","stats.2022-5-08.json","stats.2022-5-09.json","stats.2022-5-10.json","stats.2022-5-11.json","stats.2022-5-12.json","stats.2022-5-13.json","stats.2022-5-14.json","stats.2022-5-15.json","stats.2022-5-16.json","stats.2022-5-17.json","stats.2022-5-18.json","stats.2022-5-19.json","stats.2022-5-20.json","stats.2022-5-21.json","stats.2022-5-22.json","stats.2022-5-23.json","stats.2022-5-24.json","stats.2022-5-25.json","stats.2022-5-26.json","stats.2022-5-27.json","stats.2022-5-28.json","stats.2022-5-29.json","stats.2022-5-30.json","stats.2022-5-31.json","stats.2022-6-01.json","stats.2022-6-02.json","stats.2022-6-03.json","stats.2022-6-04.json","stats.2022-6-05.json","stats.2022-6-06.json","stats.2022-6-07.json","stats.2022-6-08.json","stats.2022-6-09.json","stats.2022-6-10.json","stats.2022-6-11.json","stats.2022-6-12.json","stats.2022-6-13.json","stats.2022-6-14.json","stats.2022-6-15.json","stats.2022-6-16.json","stats.2022-6-17.json","stats.2022-6-18.json","stats.2022-6-19.json","stats.2022-6-20.json","stats.2022-6-21.json","stats.2022-6-22.json","stats.2022-6-23.json","stats.2022-6-24.json","stats.2022-6-25.json","stats.2022-6-26.json","stats.2022-6-27.json","stats.2022-6-28.json","stats.2022-6-29.json","stats.2022-6-30.json","stats.2022-7-01.json","stats.2022-7-02.json","stats.2022-7-03.json","stats.2022-7-04.json","stats.2022-7-05.json","stats.2022-7-06.json","stats.2022-7-07.json","stats.2022-7-08.json","stats.2022-7-09.json","stats.2022-7-10.json","stats.2022-7-11.json","stats.2022-7-12.json","stats.2022-7-13.json","stats.2022-7-14.json","stats.2022-7-15.json","stats.2022-7-16.json","stats.2022-7-17.json","stats.2022-7-18.json","stats.2022-7-19.json","stats.2022-7-20.json","stats.2022-7-21.json","stats.2022-7-22.json","stats.2022-7-23.json","stats.2022-7-24.json","stats.2022-7-25.json","stats.2022-7-26.json","stats.2022-7-27.json","stats.2022-7-28.json"]

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -13,9 +13,9 @@ export interface ExtraFuncParams {
* Note that more features then requested can be given back.
* Format: [ [ geojson, geojson, geojson, ... ], [geojson, ...], ...]
*/
getFeaturesWithin: (layerId: string, bbox: BBox) => Feature<Geometry, {id: string}>[][],
getFeaturesWithin: (layerId: string, bbox: BBox) => Feature<Geometry, { id: string }>[][],
memberships: RelationsTracker
getFeatureById: (id: string) => Feature<Geometry, {id: string}>
getFeatureById: (id: string) => Feature<Geometry, { id: string }>
}
/**
@ -31,10 +31,11 @@ interface ExtraFunction {
class EnclosingFunc implements ExtraFunction {
_name = "enclosingFeatures"
_doc = ["Gives a list of all features in the specified layers which fully contain this object. Returned features will always be (multi)polygons. (LineStrings and Points from the other layers are ignored)","",
_doc = ["Gives a list of all features in the specified layers which fully contain this object. Returned features will always be (multi)polygons. (LineStrings and Points from the other layers are ignored)", "",
"The result is a list of features: `{feat: Polygon}[]`",
"This function will never return the feature itself."].join("\n")
"This function will never return the feature itself."].join("\n")
_args = ["...layerIds - one or more layer ids of the layer from which every feature is checked for overlap)"]
_f(params: ExtraFuncParams, feat: Feature<Geometry, any>) {
return (...layerIds: string[]) => {
const result: { feat: any }[] = []
@ -51,14 +52,14 @@ class EnclosingFunc implements ExtraFunction {
}
for (const otherFeatures of otherFeaturess) {
for (const otherFeature of otherFeatures) {
if(seenIds.has(otherFeature.properties.id)){
if (seenIds.has(otherFeature.properties.id)) {
continue
}
seenIds.add(otherFeature.properties.id)
if(otherFeature.geometry.type !== "Polygon" && otherFeature.geometry.type !== "MultiPolygon"){
if (otherFeature.geometry.type !== "Polygon" && otherFeature.geometry.type !== "MultiPolygon") {
continue;
}
if(GeoOperations.completelyWithin(feat, <Feature<Polygon | MultiPolygon, any>> otherFeature)){
if (GeoOperations.completelyWithin(feat, <Feature<Polygon | MultiPolygon, any>>otherFeature)) {
result.push({feat: otherFeature})
}
}
@ -75,10 +76,10 @@ class OverlapFunc implements ExtraFunction {
_name = "overlapWith";
_doc = ["Gives a list of features from the specified layer which this feature (partly) overlaps with. A point which is embedded in the feature is detected as well.",
"If the current feature is a point, all features that this point is embeded in are given." ,
"If the current feature is a point, all features that this point is embeded in 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." ,
"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')`",
"",
@ -89,6 +90,7 @@ class OverlapFunc implements ExtraFunction {
_f(params, feat) {
return (...layerIds: string[]) => {
const result: { feat: any, overlap: number }[] = []
const seenIds = new Set<string>()
const bbox = BBox.get(feat)
for (const layerId of layerIds) {
const otherFeaturess = params.getFeaturesWithin(layerId, bbox)
@ -99,12 +101,18 @@ class OverlapFunc implements ExtraFunction {
continue;
}
for (const otherFeatures of otherFeaturess) {
result.push(...GeoOperations.calculateOverlap(feat, otherFeatures));
const overlap = GeoOperations.calculateOverlap(feat, otherFeatures)
for (const overlappingFeature of overlap) {
if(seenIds.has(overlappingFeature.feat.properties.id)){
continue
}
seenIds.add(overlappingFeature.feat.properties.id)
result.push(overlappingFeature)
}
}
}
result.sort((a, b) => b.overlap - a.overlap)
return result;
}
}
@ -187,7 +195,7 @@ class DistanceToFunc implements ExtraFunction {
class ClosestObjectFunc implements ExtraFunction {
_name = "closest"
_doc = "Given either a list of geojson features or a single layer name, gives the single object which is nearest to the feature. In the case of ways/polygons, only the centerpoint is considered. Returns a single geojson feature or undefined if nothing is found (or not yet laoded)"
_doc = "Given either a list of geojson features or a single layer name, gives the single object which is nearest to the feature. In the case of ways/polygons, only the centerpoint is considered. Returns a single geojson feature or undefined if nothing is found (or not yet loaded)"
_args = ["list of features or a layer name or '*' to get all features"]

View file

@ -35,6 +35,7 @@ export default class MetaTagging {
return;
}
console.log("Recalculating metatags...")
const metatagsToApply: SimpleMetaTagger[] = []
for (const metatag of SimpleMetaTaggers.metatags) {
if (metatag.includesDates) {

View file

@ -93,7 +93,7 @@ export default class CreateNewNodeAction extends OsmCreateAction {
// Project the point onto the way
console.log("Snapping a node onto an existing way...")
const geojson = this._snapOnto.asGeoJson()
const projected = GeoOperations.nearestPoint(geojson, [this._lon, this._lat])
const projectedCoor= <[number, number]>projected.geometry.coordinates

View file

@ -219,6 +219,9 @@ export abstract class OsmObject {
/**
* Uses the list of polygon features to determine if the given tags are a polygon or not.
*
* OsmObject.isPolygon({"building":"yes"}) // => true
* OsmObject.isPolygon({"highway":"residential"}) // => false
* */
protected static isPolygon(tags: any): boolean {
for (const tagsKey in tags) {

View file

@ -366,7 +366,7 @@ export class RewriteSpecial extends DesugaringStep<TagRenderingConfigJson> {
* // should warn for unexpected keys
* const errors = []
* RewriteSpecial.convertIfNeeded({"special": {type: "image_carousel"}, "en": "xyz"}, errors, "test") // => {'*': "{image_carousel()}"}
* errors // => ["At test: Unexpected key in a special block: en"]
* errors // => ["The only keys allowed next to a 'special'-block are 'before' and 'after'. Perhaps you meant to put 'en' into the special block?"]
*
* // should give an error on unknown visualisations
* const errors = []
@ -378,6 +378,26 @@ export class RewriteSpecial extends DesugaringStep<TagRenderingConfigJson> {
* const errors = []
* RewriteSpecial.convertIfNeeded({"special": {}}, errors, "test") // => undefined
* errors // => ["A 'special'-block should define 'type' to indicate which visualisation should be used"]
*
*
* // an actual test
* const special = {
* "before": {
* "en": "<h3>Entrances</h3>This building has {_entrances_count} entrances:"
* },
* "after": {
* "en": "{_entrances_count_without_width_count} entrances don't have width information yet"
* },
* "special": {
* "type": "multi",
* "key": "_entrance_properties_with_width",
* "tagrendering": {
* "en": "An <a href='#{id}'>entrance</a> of {canonical(width)}"
* }
* }}
* const errors = []
* RewriteSpecial.convertIfNeeded(special, errors, "test") // => {"en": "<h3>Entrances</h3>This building has {_entrances_count} entrances:{multi(_entrance_properties_with_width,An <a href='#&LBRACEid&RBRACE'>entrance</a> of &LBRACEcanonical&LPARENSwidth&RPARENS&RBRACE)}{_entrances_count_without_width_count} entrances don't have width information yet"}
* errors // => []
*/
private static convertIfNeeded(input: (object & { special: { type: string } }) | any, errors: string[], context: string): any {
const special = input["special"]
@ -385,31 +405,33 @@ export class RewriteSpecial extends DesugaringStep<TagRenderingConfigJson> {
return input
}
for (const wrongKey of Object.keys(input).filter(k => k !== "special" && k !== "before" && k !== "after")) {
errors.push(`At ${context}: Unexpected key in a special block: ${wrongKey}`)
}
const type = special["type"]
if (type === undefined) {
errors.push("A 'special'-block should define 'type' to indicate which visualisation should be used")
return undefined
}
const vis = SpecialVisualizations.specialVisualizations.find(sp => sp.funcName === type)
if (vis === undefined) {
const options = Utils.sortedByLevenshteinDistance(type, SpecialVisualizations.specialVisualizations, sp => sp.funcName)
errors.push(`Special visualisation '${type}' not found. Did you perhaps mean ${options[0].funcName}, ${options[1].funcName} or ${options[2].funcName}?\n\tFor all known special visualisations, please see https://github.com/pietervdvn/MapComplete/blob/develop/Docs/SpecialRenderings.md`)
return undefined
}
errors.push(...
Array.from(Object.keys(input)).filter(k => k !== "special" && k !== "before" && k !== "after")
.map(k => {
return `The only keys allowed next to a 'special'-block are 'before' and 'after'. Perhaps you meant to put '${k}' into the special block?`;
}))
const argNamesList = vis.args.map(a => a.name)
const argNames = new Set<string>(argNamesList)
// Check for obsolete and misspelled arguments
errors.push(...Object.keys(special)
.filter(k => !argNames.has(k))
.filter(k => k !== "type")
.filter(k => k !== "type" && k !== "before" && k !== "after")
.map(wrongArg => {
const byDistance = Utils.sortedByLevenshteinDistance(wrongArg, argNamesList, x => x)
return `Unexpected argument with name '${wrongArg}'. Did you mean ${byDistance[0]}?\n\tAll known arguments are ${argNamesList.join(", ")}`;
return `Unexpected argument in special block at ${context} with name '${wrongArg}'. Did you mean ${byDistance[0]}?\n\tAll known arguments are ${argNamesList.join(", ")}`;
}))
// Check that all obligated arguments are present. They are obligated if they don't have a preset value
@ -456,19 +478,21 @@ export class RewriteSpecial extends DesugaringStep<TagRenderingConfigJson> {
for (const ln of languages) {
const args = []
for (const argName of argNamesList) {
const v = special[argName] ?? ""
let v = special[argName] ?? ""
if (Translations.isProbablyATranslation(v)) {
const txt = new Translation(v).textFor(ln)
.replace(/,/g, "&COMMA")
.replace(/\{/g, "&LBRACE")
.replace(/}/g, "&RBRACE")
;
args.push(txt)
} else if (typeof v === "string") {
v = new Translation(v).textFor(ln)
}
if (typeof v === "string") {
const txt = v.replace(/,/g, "&COMMA")
.replace(/\{/g, "&LBRACE")
.replace(/}/g, "&RBRACE")
.replace(/\(/g, "&LPARENS")
.replace(/\)/g, '&RPARENS')
args.push(txt)
} else if (typeof v === "object") {
args.push(JSON.stringify(v))
} else {
args.push(v)
}
@ -494,12 +518,21 @@ export class RewriteSpecial extends DesugaringStep<TagRenderingConfigJson> {
* const expected = {render: {'*': "{image_carousel(image)}"}, mappings: [{if: "other_image_key", then: {'*': "{image_carousel(other_image_key)}"}} ]}
* result // => expected
*
* // Should put text before if specified
* const tr = {
* render: {special: {type: "image_carousel", image_key: "image"}, before: {en: "Some introduction"} },
* }
* const result = new RewriteSpecial().convert(tr,"test").result
* const expected = {render: {'en': "Some introduction{image_carousel(image)}"}}
* result // => expected
*
* // Should put text after if specified
* const tr = {
* render: {special: {type: "image_carousel", image_key: "image"}, after: {en: "Some footer"} },
* }
* const result = new RewriteSpecial().convert(tr,"test").result
* const expected = {render: {'en': "{image_carousel(image)}Some footer"}}
* result // => expected
*/
convert(json: TagRenderingConfigJson, context: string): { result: TagRenderingConfigJson; errors?: string[]; warnings?: string[]; information?: string[] } {
const errors = []

View file

@ -14,10 +14,10 @@ import BaseUIElement from "../BaseUIElement";
* Selects a length after clicking on the minimap, in meters
*/
export default class LengthInput extends InputElement<string> {
public readonly IsSelected: UIEventSource<boolean> = new UIEventSource<boolean>(false);
private readonly _location: UIEventSource<Loc>;
private readonly value: UIEventSource<string>;
private background;
private readonly background: UIEventSource<any>;
constructor(mapBackground: UIEventSource<any>,
location: UIEventSource<Loc>,
@ -36,7 +36,7 @@ export default class LengthInput extends InputElement<string> {
IsValid(str: string): boolean {
const t = Number(str)
return !isNaN(t) && t >= 0 && t <= 360;
return !isNaN(t) && t >= 0;
}
protected InnerConstructElement(): HTMLElement {
@ -63,7 +63,7 @@ export default class LengthInput extends InputElement<string> {
}
const crosshair = new Combine([Svg.length_crosshair_svg().SetStyle(
`position: absolute;top: 0;left: 0;transform:rotate(${this.value.data ?? 0}deg);`)
]) .SetClass("block length-crosshair-svg relative")
]) .SetClass("block length-crosshair-svg relative pointer-events-none")
.SetStyle("z-index: 1000; visibility: hidden")
const element = new Combine([
@ -112,46 +112,42 @@ export default class LengthInput extends InputElement<string> {
lastClickXY = undefined;
}
}
if (isUp) {
const distance = Math.sqrt((dy - firstClickXY[1]) * (dy - firstClickXY[1]) + (dx - firstClickXY[0]) * (dx - firstClickXY[0]))
if (distance > 15) {
lastClickXY = [dx, dy]
}
} else if (lastClickXY !== undefined) {
if (firstClickXY === undefined) {
measurementCrosshair.style.visibility = "hidden"
return;
}
// const measurementCrosshair = htmlElement.getElementsByClassName("length-crosshair-svg")[0] as HTMLElement
const measurementCrosshairInner: HTMLElement = <HTMLElement>measurementCrosshair.firstChild
if (firstClickXY === undefined) {
measurementCrosshair.style.visibility = "hidden"
} else {
measurementCrosshair.style.visibility = "unset"
measurementCrosshair.style.left = firstClickXY[0] + "px";
measurementCrosshair.style.top = firstClickXY[1] + "px"
const angle = 180 * Math.atan2(firstClickXY[1] - dy, firstClickXY[0] - dx) / Math.PI;
const angleGeo = (angle + 270) % 360
measurementCrosshairInner.style.transform = `rotate(${angleGeo}deg)`;
const distance = Math.sqrt((dy - firstClickXY[1]) * (dy - firstClickXY[1]) + (dx - firstClickXY[0]) * (dx - firstClickXY[0]))
measurementCrosshairInner.style.width = (distance * 2) + "px"
measurementCrosshairInner.style.marginLeft = -distance + "px"
measurementCrosshairInner.style.marginTop = -distance + "px"
const leaflet = leafletMap?.data
if (leaflet) {
const first = leaflet.layerPointToLatLng(firstClickXY)
const last = leaflet.layerPointToLatLng([dx, dy])
const geoDist = Math.floor(GeoOperations.distanceBetween([first.lng, first.lat], [last.lng, last.lat]) * 10) / 10
self.value.setData("" + geoDist)
const distance = Math.sqrt((dy - firstClickXY[1]) * (dy - firstClickXY[1]) + (dx - firstClickXY[0]) * (dx - firstClickXY[0]))
if (isUp) {
if (distance > 15) {
lastClickXY = [dx, dy]
}
} else if (lastClickXY !== undefined) {
return;
}
measurementCrosshair.style.visibility = "unset"
measurementCrosshair.style.left = firstClickXY[0] + "px";
measurementCrosshair.style.top = firstClickXY[1] + "px"
const angle = 180 * Math.atan2(firstClickXY[1] - dy, firstClickXY[0] - dx) / Math.PI;
const angleGeo = (angle + 270) % 360
const measurementCrosshairInner: HTMLElement = <HTMLElement>measurementCrosshair.firstChild
measurementCrosshairInner.style.transform = `rotate(${angleGeo}deg)`;
measurementCrosshairInner.style.width = (distance * 2) + "px"
measurementCrosshairInner.style.marginLeft = -distance + "px"
measurementCrosshairInner.style.marginTop = -distance + "px"
const leaflet = leafletMap?.data
if (leaflet) {
const first = leaflet.layerPointToLatLng(firstClickXY)
const last = leaflet.layerPointToLatLng([dx, dy])
const geoDist = Math.floor(GeoOperations.distanceBetween([first.lng, first.lat], [last.lng, last.lat]) * 10) / 10
self.value.setData("" + geoDist)
}
}

View file

@ -42,7 +42,6 @@ import NoteCommentElement from "./Popup/NoteCommentElement";
import ImgurUploader from "../Logic/ImageProviders/ImgurUploader";
import FileSelectorButton from "./Input/FileSelectorButton";
import {LoginToggle} from "./Popup/LoginButton";
import {start} from "repl";
import {SubstitutedTranslation} from "./SubstitutedTranslation";
import {TextField} from "./Input/TextField";
import Wikidata, {WikidataResponse} from "../Logic/Web/Wikidata";
@ -60,7 +59,8 @@ import Slider from "./Input/Slider";
import List from "./Base/List";
import StatisticsPanel from "./BigComponents/StatisticsPanel";
import {OsmFeature} from "../Models/OsmFeature";
import Link from "./Base/Link";
import EditableTagRendering from "./Popup/EditableTagRendering";
import TagRenderingConfig from "../Models/ThemeConfig/TagRenderingConfig";
export interface SpecialVisualization {
funcName: string,
@ -334,6 +334,13 @@ export default class SpecialVisualizations {
render: {
special: {
type: "some_special_visualisation",
before: {
en: "Some text to prefix before the special element (e.g. a title)",
nl: "Een tekst om voor het element te zetten (bv. een titel)"
},
after: {
en: "Some text to put after the element, e.g. a footer"
},
"argname": "some_arg",
"message": {
en: "some other really long message",
@ -1202,6 +1209,96 @@ export default class SpecialVisualizations {
}))
}
},
{
funcName: "multi",
docs: "Given an embedded tagRendering (read only) and a key, will read the keyname as a JSON-list. Every element of this list will be considered as tags and rendered with the tagRendering",
example: "```json\n" + JSON.stringify({
render: {
special: {
type: "multi",
key: "_doors_from_building_properties",
tagRendering: {
render: "The building containing this feature has a <a href='#{id}'>door</a> of width {entrance:width}"
}
}
}
}, null, " ") + "```",
args: [
{
name: "key",
doc: "The property to read and to interpret as a list of properties",
required: true
},
{
name: "tagrendering",
doc: "An entire tagRenderingConfig",
required: true
}
]
,
constr(state, featureTags, args) {
const [key, tr] = args
const translation = new Translation({"*": tr})
return new VariableUiElement(featureTags.map(tags => {
const properties: object[] = JSON.parse(tags[key])
const elements = []
for (const property of properties) {
const subsTr = new SubstitutedTranslation(translation, new UIEventSource<any>(property), state)
elements.push(subsTr)
}
return new List(elements)
}))
}
},
{
funcName: "steal",
docs: "Shows a tagRendering from a different object as if this was the object itself",
args: [{
name: "featureId",
doc: "The key of the attribute which contains the id of the feature from which to use the tags",
required: true
},
{
name: "tagRenderingId",
doc: "The layer-id and tagRenderingId to render. Can be multiple value if ';'-separated (in which case every value must also contain the layerId, e.g. `layerId.tagRendering0; layerId.tagRendering1`). Note: this can cause layer injection",
required: true
}],
constr(state, featureTags, args) {
const [featureIdKey, layerAndtagRenderingIds] = args
const tagRenderings: [LayerConfig, TagRenderingConfig][] = []
for (const layerAndTagRenderingId of layerAndtagRenderingIds.split(";")) {
const [layerId, tagRenderingId] = layerAndTagRenderingId.trim().split(".")
const layer = state.layoutToUse.layers.find(l => l.id === layerId)
const tagRendering = layer.tagRenderings.find(tr => tr.id === tagRenderingId)
tagRenderings.push([layer, tagRendering])
}
return new VariableUiElement(featureTags.map(tags => {
const featureId = tags[featureIdKey]
if (featureId === undefined) {
return undefined;
}
const otherTags = state.allElements.getEventSourceById(featureId)
const elements: BaseUIElement[] = []
for (const [layer, tagRendering] of tagRenderings) {
const el = new EditableTagRendering(otherTags, tagRendering, layer.units, state, {})
elements.push(el)
}
if (elements.length === 1) {
return elements[0]
}
return new Combine(elements).SetClass("flex flex-col");
}))
},
getLayerDependencies(args): string[] {
const [_, tagRenderingId] = args
if (tagRenderingId.indexOf(".") < 0) {
throw "Error: argument 'layerId.tagRenderingId' of special visualisation 'steal' should contain a dot"
}
const [layerId, __] = tagRenderingId.split(".")
return [layerId]
}
}
]

View file

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<svg xmlns="http://www.w3.org/2000/svg" width="375px" height="375px" viewBox="0 0 375 375" version="1.1">
<g id="surface1">
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;" d="M 63.628906 7.519531 C 61.664062 7.894531 60.253906 9.632812 60.289062 11.636719 L 60.289062 361.71875 L 23.269531 361.71875 C 23.011719 361.691406 22.753906 361.691406 22.496094 361.71875 C 20.226562 361.929688 18.554688 363.945312 18.769531 366.222656 C 18.980469 368.496094 20.996094 370.167969 23.269531 369.953125 L 352.339844 369.953125 C 353.820312 369.976562 355.199219 369.195312 355.949219 367.914062 C 356.695312 366.628906 356.695312 365.042969 355.949219 363.761719 C 355.199219 362.476562 353.820312 361.695312 352.339844 361.71875 L 315.316406 361.71875 L 315.316406 11.636719 C 315.316406 9.363281 313.476562 7.519531 311.203125 7.519531 L 64.402344 7.519531 C 64.144531 7.496094 63.886719 7.496094 63.628906 7.519531 Z M 68.515625 15.753906 L 307.089844 15.753906 L 307.089844 361.71875 L 282.410156 361.71875 L 282.410156 44.585938 C 282.410156 42.3125 280.570312 40.46875 278.296875 40.46875 L 97.308594 40.46875 C 97.050781 40.441406 96.792969 40.441406 96.539062 40.46875 C 94.570312 40.84375 93.160156 42.582031 93.195312 44.585938 L 93.195312 361.71875 L 68.515625 361.71875 Z M 101.421875 48.703125 L 274.183594 48.703125 L 274.183594 361.71875 L 101.421875 361.71875 Z M 237.164062 199.03125 C 234.121094 199.03125 231.515625 200.691406 230.09375 203.152344 L 212.484375 203.152344 C 212.226562 203.128906 211.96875 203.128906 211.710938 203.152344 C 209.449219 203.375 207.789062 205.386719 208.003906 207.65625 C 208.214844 209.921875 210.21875 211.589844 212.484375 211.390625 L 230.09375 211.390625 C 231.515625 213.847656 234.121094 215.507812 237.164062 215.507812 C 241.707031 215.507812 245.390625 211.820312 245.390625 207.269531 C 245.390625 202.722656 241.707031 199.03125 237.164062 199.03125 Z M 237.164062 199.03125 "/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2 KiB

View file

@ -419,7 +419,7 @@
"centroid"
],
"icon": {
"render": "circle:white;./assets/layers/entrance/door.svg",
"render": "circle:white;./assets/layers/entrance/entrance.svg",
"mappings": [
{
"if": "entrance=emergency",

View file

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xml:space="preserve"
version="1.1"
style="shape-rendering:geometricPrecision;text-rendering:geometricPrecision;image-rendering:optimizeQuality;"
viewBox="0 0 846.66 846.66"
width="846"
height="846"
x="0px"
y="0px"
fill-rule="evenodd"
clip-rule="evenodd"
id="svg10"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs4"><style
type="text/css"
id="style2">
.fil0 {fill:black}
</style></defs><g
id="g8"
transform="matrix(0.80128517,0,0,0.80128517,84.121948,84.122941)"><path
class="fil0"
d="M 226.16,46.72 H 620.5 c 23.82,0 43.18,19.37 43.18,43.19 v 666.85 c 0,23.83 -19.36,43.19 -43.18,43.19 H 226.16 c -23.15,0 -42.22,-18.35 -43.14,-41.47 l -0.01,-0.05 v -0.07 -0.13 L 183,758.11 V 758 757.87 h -0.01 v -0.12 -0.12 -0.13 -0.12 l -0.01,-0.06 v -0.06 -0.13 -0.12 -0.12 -0.13 -666.85 -0.12 -0.13 -0.12 -0.13 -0.06 l 0.01,-0.06 V 89.17 89.04 88.92 88.8 H 183 v -0.13 -0.12 l 0.01,-0.12 V 88.3 88.24 l 0.01,-0.06 C 183.96,65.07 203,46.72 226.16,46.72 Z m 137.19,322.13 h 47.68 V 477.84 H 363.35 Z M 290.87,98.22 458.12,187.24 c 8.64,4.62 13.58,13.47 13.58,22.63 0.12,142.31 0.1,284.62 0.1,426.93 0,10.66 -6.49,19.81 -15.74,23.72 l -165.2,87.93 H 612.19 V 98.22 Z"
id="path6" /></g></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -1,14 +1,4 @@
[
{
"path": "door.svg",
"license": "CC-BY 3.0 Unported",
"authors": [
"Icons Bazaar"
],
"sources": [
"https://commons.wikimedia.org/wiki/File:Noun_Project_Door_icon_1512154.svg"
]
},
{
"path": "emergency_door.svg",
"license": "CC-BY-SA 4.0 international",
@ -18,5 +8,15 @@
"sources": [
"https://commons.wikimedia.org/wiki/File:Emergency_door_icon.svg"
]
},
{
"path": "entrance.svg",
"license": "CC-BY-SA",
"authors": [
"CT Steward"
],
"sources": [
"https://www.ctsteward.com/"
]
}
]

View file

@ -56,7 +56,19 @@
},
"minzoom": 13,
"tagRenderings": [
"images"
"images",
{
"id": "ref",
"question": {
"en": "What is the name of this room?"
},
"render": {
"en": "This room is named {name}"
},
"freeform": {
"key": "name"
}
}
],
"mapRendering": [
{

View file

@ -47,34 +47,85 @@
}
],
"calculatedTags": [
"_entrance_properties=feat.overlapWith('entrance')?.map(e => e.feat.properties).filter(p => p !== undefined).filter(p => p.width !== undefined)",
"_entrance:id=feat.get('_entrance_properties')?.map(e => e.id)?.at(0)",
"_entrance:width=feat.get('_entrance_properties')?.map(e => e.width)?.at(0)"
"_entrance_properties=feat.overlapWith('entrance')?.map(e => e.feat.properties)?.filter(p => p !== undefined && p.indoor !== 'door')",
"_entrance_properties_with_width=feat.get('_entrance_properties')?.filter(p => p['width'] !== undefined)",
"_entrances_count=feat.get('_entrance_properties').length",
"_entrances_count_without_width_count= feat.get('_entrances_count') - feat.get('_entrance_properties_with_width').length",
"_biggest_width= Math.max( feat.get('_entrance_properties').map(p => p.width))",
"_biggest_width_properties= /* Can be a list! */ feat.get('_entrance_properties').filter(p => p.width === feat.get('_biggest_width'))",
"_biggest_width_id=feat.get('_biggest_width_properties').id"
],
"tagRenderings": [
"units": [
{
"id": "_entrance:width",
"render": {
"en": "<a href ='#{_entrance:id} '>This door has a width of {canonical(_entrance:width)} meters </a>",
"nl": "<a href ='#{_entrance:id} '>Deze deur heeft een breedte van {canonical(_entrance:width)} meter </a>",
"de": "<a href ='#{_entrance:id} '>Diese Tür hat eine Durchgangsbreite von {canonical(_entrance:width)} Meter </a>",
"es": "<a href ='#{_entrance:id} '>Esta puerta tiene una ancho de {canonical(_entrance:width)} metros </a>",
"fr": "<a href ='#{_entrance:id} '>Cette porte a une largeur de {canonical(_entrance:width)} mètres </a>"
},
"freeform": {
"key": "_entrance:width"
},
"mappings": [
"appliesToKey": [
"width",
"_biggest_width"
],
"applicableUnits": [
{
"if": "_entrance:width=",
"then": {
"en": "This entrance has no width information",
"de": "Der Eingang hat keine Informationen zur Durchgangsbreite",
"fr": "Cette entrée n'a pas d'informations sur sa largeur",
"nl": "Deze toegang heeft geen informatie over deurbreedte"
"canonicalDenomination": "m",
"alternativeDenomination": [
"meter"
],
"human": {
"en": "meter",
"fr": "mètre",
"de": "Meter"
}
},
{
"default": true,
"canonicalDenomination": "cm",
"alternativeDenomination": [
"centimeter",
"cms"
],
"human": {
"en": "centimeter",
"fr": "centimètre",
"de": "Zentimeter"
}
}
]
}
],
"tagRenderings": [
{
"id": "entrance_info",
"render": {
"before": {
"en": "<h3>Entrances</h3>This building has {_entrances_count} entrances:"
},
"after": {
"en": "{_entrances_count_without_width_count} entrances don't have width information yet"
},
"special": {
"type": "multi",
"key": "_entrance_properties_with_width",
"tagrendering": {
"en": "An <a href='#{id}'>entrance</a> of {canonical(width)}"
}
}
},
"mappings": [
{
"if": "_entrances_count=0",
"then": {
"en": "No entrance has been marked"
}
},
{
"if": "_entrances_count_without_width:=_entrances_count",
"then": {
"en": "None of the {_entrance_count} entrances have width information yet"
}
}
]
},
{
"id": "biggest_width",
"render": "The <a href='#{_biggest_width_id}'>entrance with the biggest width</a> is {canonical(_biggest_width)} wide",
"condition": "_biggest_width_id~*"
}
]
}

View file

@ -1,43 +0,0 @@
{
"id": "entrances",
"title": {
"en": "Entrances",
"zh_Hant": "出入口",
"hu": "Épületek bejáratai",
"de": "Eingänge",
"nl": "Toegangsdeuren",
"ca": "Entrades",
"es": "Entradas",
"nb_NO": "Innganger",
"fr": "Entrées"
},
"icon": "./assets/layers/entrance/door.svg",
"description": {
"en": "A map showing all entrances, which surveys for important aspects for wheelchair users",
"zh_Hant": "顯示所有出入口的地圖,勘查對輪椅使用者重要的資訊",
"hu": "Épületek bejáratai mutató térkép, amely a kerekesszékkel közlekedőknek fontos szempontokat jeleníti meg",
"de": "Eine Karte mit allen Eingängen, die wichtige Aspekte für Rollstuhlfahrer erfasst",
"es": "Un mapa que muestra todas las entradas, que sondea aspectos importantes para usuarios de sillas de ruedas",
"nl": "Een kaart die alle toegangen toont,",
"fr": "Une carte renseignant toutes les entrées (importante pour les utilisateurs de sièges roulants",
"nb_NO": "Alle innganger, som er en viktig undersøkelse å gjøre for rullestolsbrukere"
},
"shortDescription": {
"en": "Survey entrances to help wheelchair routing",
"zh_Hant": "幫助輪椅導航來勘查出入口",
"hu": "Akadálymentes útvonaltervezést segítő bejáratok feltérképezése",
"de": "Eingänge prüfen, um die Rollstuhlnavigation zu verbessern",
"es": "Sondea entradas para ayudar el enrutado de sillas de ruedas",
"nl": "Breng ingangen in kaart om rolstoelnavigatie mogelijk te maken",
"fr": "Vérifez les entrées pour faciliter le routage des utilisateurs de sièges roulants",
"nb_NO": "Lag oversikt over innganger for å hjelpe rullestolsruteplanlegging"
},
"version": "2021-12-04",
"maintainer": "MapComplete",
"layers": [
"entrance"
],
"startZoom": 15,
"startLat": 51.049,
"startLon": 3.7297
}

View file

@ -1,11 +0,0 @@
<svg width="400" height="400" viewBox="0 0 400 400" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M397 200C397 308.8 308.8 397 200 397C91.1999 397 3 308.8 3 200C3 91.1999 91.1999 3 200 3C308.8 3 397 91.1999 397 200Z" fill="#22CA60" stroke="#E62222" stroke-width="6"/>
<path d="M242.105 240.241L137.282 291.429L155.338 312.97L167.374 327.329L185.428 348.868L254.141 254.6L242.105 240.241Z" fill="#ECF0F1"/>
<path d="M138.93 283.603L122.535 283.627L190.077 364.206L192.964 348.067L138.93 283.603Z" fill="#FE5757"/>
<path d="M242.105 240.241L224.633 248.772L242.689 270.312L254.141 254.6L242.105 240.241ZM201.309 260.11L177.983 271.447L212.214 312.285L227.45 291.296L201.309 260.11ZM154.752 282.898L137.282 291.429L155.338 312.97L167.374 327.329L185.428 348.868L196.88 333.157L154.754 282.9L154.752 282.898Z" fill="#E62222"/>
<path d="M122.535 283.627L190.077 364.206L185.257 368.246L117.714 287.667L122.535 283.627Z" fill="#E62222"/>
<path d="M243.871 187.187C241.2 193.219 236.746 198.533 230.685 202.228C214.87 211.87 194.229 206.866 184.586 191.05C180.892 184.991 179.363 178.227 179.755 171.642L162.087 167.358C160.538 178.79 162.721 190.803 169.195 201.422C184.17 225.982 216.219 233.753 240.777 218.78C251.396 212.306 258.87 202.636 262.73 191.761L243.871 187.187Z" fill="#FCFCFF"/>
<path d="M220.03 69.1601C228.136 64.2175 230.701 53.639 225.759 45.5323C220.816 37.4256 210.237 34.8606 202.131 39.8032C194.024 44.7458 191.459 55.3243 196.402 63.431C201.344 71.5377 211.923 74.1027 220.03 69.1601Z" fill="#FCFCFF"/>
<path d="M250.824 98.9454L218.518 118.642L225.708 88.9832C225.995 88.2028 226.192 87.3988 226.294 86.5735L226.353 86.3324L226.329 86.3268C226.556 84.0714 226.115 81.7309 224.844 79.6478C222.814 76.3168 219.268 74.5145 215.635 74.5114L215.635 74.5081L159.854 75.8435L159.857 75.9264C158.38 76.1088 156.92 76.5767 155.568 77.4013C153.203 78.8428 151.63 81.0604 150.891 83.5185L150.871 83.5137L150.823 83.7119C150.755 83.956 150.696 84.1972 150.645 84.4453L143.544 113.732L143.57 113.739C143.012 116.342 143.389 119.153 144.885 121.608C147.987 126.696 154.623 128.303 159.709 125.202C162.164 123.706 163.786 121.379 164.482 118.809L164.509 118.816L164.574 118.548L164.58 118.522L169.753 97.19L185.053 96.8249L174.632 139.811C170.442 144.547 167.246 149.967 165.048 155.739L182.58 159.99C185.251 153.959 189.705 148.644 195.764 144.951C211.581 135.307 232.221 140.313 241.863 156.128C245.557 162.187 247.086 168.952 246.694 175.536L265.414 180.077C266.655 169.003 264.383 157.449 258.132 147.197C253.744 140 247.883 134.257 241.182 130.094L252.845 122.983L275.833 160.687C278.935 165.775 285.572 167.384 290.66 164.282C295.748 161.18 297.356 154.541 294.255 149.455L265.65 102.538C262.549 97.4527 255.91 95.8446 250.824 98.9454Z" fill="#FCFCFF"/>
<path d="M104.007 155.511C105.121 154.899 105.528 153.5 104.917 152.386C104.305 151.272 102.905 150.865 101.791 151.476L95.7395 154.8C91.283 157.248 89.6546 162.845 92.1022 167.301C94.5498 171.758 100.147 173.386 104.603 170.938L130.828 156.535C131.942 155.923 132.349 154.524 131.737 153.41C131.125 152.296 129.726 151.889 128.612 152.501L102.387 166.904C100.159 168.128 97.3605 167.313 96.1367 165.085C94.9129 162.857 95.7272 160.059 97.9554 158.835L104.007 155.511ZM79.7999 168.806C80.914 168.194 81.3211 166.795 80.7092 165.681C80.0973 164.567 78.6981 164.16 77.584 164.772L73.5495 166.988C65.7507 171.271 62.9009 181.066 67.1842 188.864C71.4675 196.663 81.262 199.513 89.0608 195.23L145.544 164.207C146.659 163.595 147.066 162.196 146.454 161.082C145.842 159.968 144.443 159.56 143.329 160.172L86.8449 191.195C81.2743 194.255 74.2783 192.219 71.2187 186.648C68.1592 181.078 70.1948 174.082 75.7654 171.022L79.7999 168.806ZM136.054 185.174C137.168 184.562 137.575 183.163 136.963 182.049C136.351 180.935 134.952 180.528 133.838 181.14L103.579 197.759C99.1225 200.207 97.494 205.803 99.9417 210.26C102.389 214.716 107.986 216.345 112.443 213.897L118.494 210.573C119.608 209.961 120.016 208.562 119.404 207.448C118.792 206.334 117.393 205.927 116.278 206.539L110.227 209.863C107.998 211.086 105.2 210.272 103.976 208.044C102.752 205.816 103.567 203.017 105.795 201.793L136.054 185.174Z" fill="white"/>
</svg>

Before

Width:  |  Height:  |  Size: 4.1 KiB

View file

@ -1,22 +0,0 @@
{
"id": "governments",
"title": {
"en": "Governmental Offices",
"de": "Büros der öffentlichen Verwaltung"
},
"description": {
"en": "On this map, Governmental offices are shown and can be easily added",
"de": "Auf dieser Karte werden Büros der öffentlichen Verwaltung angezeigt und können leicht hinzugefügt werden"
},
"maintainer": "MapComplete",
"icon": "./assets/themes/onwheels/crest.svg",
"version": "0",
"startLat": 50.8465573,
"defaultBackgroundId": "CartoDB.Voyager",
"startLon": 4.351697,
"startZoom": 16,
"widenFactor": 2,
"layers": [
"governments"
]
}

View file

@ -1,10 +0,0 @@
[
{
"path": "crest.svg",
"license": "CC0",
"authors": [
"Free Wheelies"
],
"sources": []
}
]

View file

@ -9,7 +9,7 @@
"de": "Diese Karte zeigt öffentlich zugängliche Innenräume"
},
"maintainer": "MapComplete",
"icon": "./assets/themes/onwheels/crest.svg",
"icon": "./assets/layers/entrance/entrance.svg",
"version": "0",
"startLat": 51.17181,
"defaultBackgroundId": "CartoDB.Voyager",
@ -17,6 +17,16 @@
"startZoom": 14,
"widenFactor": 2,
"layers": [
"indoors"
"indoors",
{
"builtin": [
"walls_and_buildings"
],
"override": {
"shownByDefault": true
}
},
"pedestrian_path",
"entrance"
]
}

View file

@ -159,10 +159,6 @@
"if": "theme=education",
"then": "./assets/layers/school/college.svg"
},
{
"if": "theme=entrances",
"then": "./assets/layers/entrance/door.svg"
},
{
"if": "theme=etymology",
"then": "./assets/layers/etymology/logo.svg"
@ -187,10 +183,6 @@
"if": "theme=ghostbikes",
"then": "./assets/themes/ghostbikes/logo.svg"
},
{
"if": "theme=governments",
"then": "./assets/themes/onwheels/crest.svg"
},
{
"if": "theme=grb",
"then": "./assets/themes/grb_import/logo.svg"
@ -217,7 +209,7 @@
},
{
"if": "theme=indoors",
"then": "./assets/themes/onwheels/crest.svg"
"then": "./assets/layers/entrance/entrance.svg"
},
{
"if": "theme=kerbs_and_crossings",

View file

@ -369,13 +369,11 @@
],
"overrideAll": {
"+calculatedTags": [
"_poi_walls_and_buildings_entrance_properties=feat.closestn('walls_and_buildings', 1, undefined, 1000).map(w => ({id: w.feat.properties.id, width: w.feat.properties['_entrance:width']}))[0]",
"_poi_entrance:id=JSON.parce(feat.properties._poi_walls_and_buildings_entrance_properteis)?.id",
"_poi_entrance:width=JSON.parse(feat.properties._poi_walls_and_buildings_entrance_properties)?.width"
"_enclosing_building=feat.enclosingFeatures('walls_and_buildings')?.map(f => f.feat.properties.id)?.at(0)"
],
"+tagRenderings": [
"tagRenderings+": [
{
"id": "_containing_poi_entrance:width",
"id": "_stolen_entrances",
"condition": {
"and": [
"entrance=",
@ -385,26 +383,12 @@
]
},
"render": {
"en": "The containing building can be entered via <a href='#{_poi_entrance:id}'>a door of {canonical(_poi_entrance:width)}</a>",
"nl": "Het gebouw waarin dit zich bevindt kan binnengegaan worden <a href='#{_poi_entrance:id}'>via een deur</a> die {canonical(_poi_entrance:width)} breed is",
"fr": "On peut entrer dans ce batiment via <a href='#{_poi_entrance:id}'>une porte de {canonical(_poi_entrance:width)}</a>",
"de": "Das Gebäude kann über <a href='#{_poi_entrance:id}'>durch eine Tür von {canonical(_poi_entrance:width)} betreten werden.</a>"
},
"freeform": {
"key": "_poi_entrance:width",
"type": "distance"
},
"mappings": [
{
"if": "_poi_entrance:width=",
"then": {
"nl": "Het omvattende gebouw heeft geen gekende deurbreedtes. Voeg een deur en breedte toe.",
"en": "The containing building has no information on door widths. Add a door and measure the width to get information",
"fr": "Ce bâtiment n'a aucune information sur les largeurs de portes. Ajoutez une porte et mesurez la largeur pour obtenir des informations",
"de": "Das Gebäude hat keine Informationen über Türbreiten. Fügen Sie eine Tür hinzu und messen Sie die Breite, um Informationen zu erhalten"
}
"special": {
"type": "steal",
"featureId": "_enclosing_building",
"tagRenderingId": "walls_and_buildings.entrance_info; walls_and_buildings.biggest_width"
}
]
}
}
]
},

View file

@ -6738,18 +6738,20 @@
},
"walls_and_buildings": {
"description": "Spezielle Ebene, die alle Wände und Gebäude bereitstellt. Diese Ebene ist nützlich in Voreinstellungen für Objekte, die an Wänden platziert werden können (z. B. AEDs, Briefkästen, Eingänge, Adressen, Überwachungskameras, ...). Diese Ebene ist standardmäßig unsichtbar und kann vom Benutzer nicht umgeschaltet werden.",
"tagRenderings": {
"_entrance:width": {
"mappings": {
"0": {
"then": "Der Eingang hat keine Informationen zur Durchgangsbreite"
}
},
"render": "<a href ='#{_entrance:id} '>Diese Tür hat eine Durchgangsbreite von {canonical(_entrance:width)} Meter </a>"
}
},
"title": {
"render": "Wand oder Gebäude"
},
"units": {
"0": {
"applicableUnits": {
"0": {
"human": "Meter"
},
"1": {
"human": "Zentimeter"
}
}
}
}
},
"waste_basket": {

View file

@ -4256,6 +4256,12 @@
"indoors": {
"description": "Basic indoor mapping: shows room outlines",
"name": "indoors",
"tagRenderings": {
"ref": {
"question": "What is the name of this room?",
"render": "This room is named {name}"
}
},
"title": {
"render": "Indoor area {name}"
}
@ -6895,17 +6901,38 @@
"walls_and_buildings": {
"description": "Special builtin layer providing all walls and buildings. This layer is useful in presets for objects which can be placed against walls (e.g. AEDs, postboxes, entrances, addresses, surveillance cameras, …). This layer is invisible by default and not toggleable by the user.",
"tagRenderings": {
"_entrance:width": {
"entrance_info": {
"mappings": {
"0": {
"then": "This entrance has no width information"
"then": "No entrance has been marked"
},
"1": {
"then": "None of the {_entrance_count} entrances have width information yet"
}
},
"render": "<a href ='#{_entrance:id} '>This door has a width of {canonical(_entrance:width)} meters </a>"
"render": {
"after": "{_entrances_count_without_width_count} entrances don't have width information yet",
"before": "<h3>Entrances</h3>This building has {_entrances_count} entrances:",
"special": {
"tagrendering": "An <a href='#{id}'>entrance</a> of {canonical(width)}"
}
}
}
},
"title": {
"render": "Wall or building"
},
"units": {
"0": {
"applicableUnits": {
"0": {
"human": "meter"
},
"1": {
"human": "centimeter"
}
}
}
}
},
"waste_basket": {

View file

@ -3982,11 +3982,6 @@
}
},
"walls_and_buildings": {
"tagRenderings": {
"_entrance:width": {
"render": "<a href ='#{_entrance:id} '>Esta puerta tiene una ancho de {canonical(_entrance:width)} metros </a>"
}
},
"title": {
"render": "Pared o edificio"
}

View file

@ -4357,18 +4357,20 @@
},
"walls_and_buildings": {
"description": "Couche intégrée spéciale fournissant tous les murs et bâtiments. Cette couche est utile dans les préréglages pour les objets qui peuvent être placés contre les murs (par exemple, les DEA, les boîtes aux lettres, les entrées, les adresses, les caméras de surveillance, …). Ce calque est invisible par défaut et non inchangeable par l'utilisateur.",
"tagRenderings": {
"_entrance:width": {
"mappings": {
"0": {
"then": "Cette entrée n'a pas d'informations sur sa largeur"
}
},
"render": "<a href ='#{_entrance:id} '>Cette porte a une largeur de {canonical(_entrance:width)} mètres </a>"
}
},
"title": {
"render": "Mur ou bâtiment"
},
"units": {
"0": {
"applicableUnits": {
"0": {
"human": "mètre"
},
"1": {
"human": "centimètre"
}
}
}
}
},
"watermill": {

View file

@ -6524,16 +6524,6 @@
},
"walls_and_buildings": {
"description": "Speciale laag met alle muren en gebouwen. Deze laag is nuttig om objecten toe te voegen die met een muur verbonden zijn (zoals AEDs, brievenbussen, adressen, bewakingscamera's,…). Deze laag is standaard onzichtbaar en kan niet aangezet worden door de gebruiker.",
"tagRenderings": {
"_entrance:width": {
"mappings": {
"0": {
"then": "Deze toegang heeft geen informatie over deurbreedte"
}
},
"render": "<a href ='#{_entrance:id} '>Deze deur heeft een breedte van {canonical(_entrance:width)} meter </a>"
}
},
"title": {
"render": "Muur of gebouw"
}

View file

@ -663,68 +663,6 @@
"description": "Eine Karte mit Bordsteinen und Überwegen.",
"title": "Bordsteine und Überwege"
},
"mapcomplete-changes": {
"description": "Diese Karte zeigt alle mit MapComplete vorgenommenen Änderungen",
"layers": {
"0": {
"description": "Zeigt alle MapComplete Änderungen",
"filter": {
"0": {
"options": {
"0": {
"question": "Themenname enthält {search}"
}
}
},
"1": {
"options": {
"0": {
"question": "Erstellt von {search}"
}
}
},
"2": {
"options": {
"0": {
"question": "<b>Nicht</b> erstellt von {search}"
}
}
}
},
"name": "Zentrum der Änderungssätze",
"tagRenderings": {
"contributor": {
"render": "Geändert von <a href='https://openstreetmap.org/user/{_last_edit:contributor}' target='_blank'>{_last_edit:contributor}</a>"
},
"render_id": {
"render": "Änderungssatz <a href='https://openstreetmap.org/changeset/{id}' target='_blank'>{id}</a>"
},
"theme": {
"mappings": {
"0": {
"then": "Änderung mit <b>inoffiziellem</b> Thema <a href='https://mapcomplete.osm.be/theme.html?userlayout={theme}'>{theme}</a>"
}
},
"render": "Änderung mit Thema <a href='https://mapcomplete.osm.be/{theme}'>{theme}</a>"
}
},
"title": {
"render": "Änderungssatz für {theme}"
}
},
"1": {
"override": {
"tagRenderings": {
"link_to_more": {
"render": "Weitere Statistiken finden Sie <a href='https://github.com/pietervdvn/MapComplete/tree/develop/Docs/Tools/graphs' target='_blank'>hier</a>"
}
}
}
}
},
"shortDescription": "Zeigt die mit MapComplete vorgenommenen Änderungen",
"title": "Mit MapComplete vorgenommene Änderungen"
},
"maps": {
"description": "Auf dieser Karte findest du alle Karten, die OpenStreetMap kennt - typischerweise eine große Karte auf einer Informationstafel, die das Gebiet, die Stadt oder die Region zeigt, z.B. eine touristische Karte auf der Rückseite einer Plakatwand, eine Karte eines Naturschutzgebietes, eine Karte der Radwegenetze in der Region, ...) <br/><br/>Wenn eine Karte fehlt, können Sie diese leicht auf OpenStreetMap kartieren.",
"shortDescription": "Dieses Thema zeigt alle (touristischen) Karten, die OpenStreetMap kennt",
@ -818,18 +756,6 @@
}
}
},
"overrideAll": {
"+tagRenderings": {
"0": {
"mappings": {
"0": {
"then": "Das Gebäude hat keine Informationen über Türbreiten. Fügen Sie eine Tür hinzu und messen Sie die Breite, um Informationen zu erhalten"
}
},
"render": "Das Gebäude kann über <a href='#{_poi_entrance:id}'>durch eine Tür von {canonical(_poi_entrance:width)} betreten werden.</a>"
}
}
},
"title": "Auf Rädern"
},
"openwindpowermap": {

View file

@ -822,18 +822,6 @@
}
}
},
"overrideAll": {
"+tagRenderings": {
"0": {
"mappings": {
"0": {
"then": "The containing building has no information on door widths. Add a door and measure the width to get information"
}
},
"render": "The containing building can be entered via <a href='#{_poi_entrance:id}'>a door of {canonical(_poi_entrance:width)}</a>"
}
}
},
"title": "OnWheels"
},
"openwindpowermap": {

View file

@ -673,18 +673,6 @@
},
"onwheels": {
"description": "Sur cette carte nous pouvons voir et ajouter les différents endroits publiques accessibles aux chaises roulantes",
"overrideAll": {
"+tagRenderings": {
"0": {
"mappings": {
"0": {
"then": "Ce bâtiment n'a aucune information sur les largeurs de portes. Ajoutez une porte et mesurez la largeur pour obtenir des informations"
}
},
"render": "On peut entrer dans ce batiment via <a href='#{_poi_entrance:id}'>une porte de {canonical(_poi_entrance:width)}</a>"
}
}
},
"title": "OnWheels"
},
"openwindpowermap": {

View file

@ -790,67 +790,6 @@
"description": "Een kaart met stoepranden en oversteekplaatsen.",
"title": "Stoepranden en oversteekplaatsen"
},
"mapcomplete-changes": {
"description": "Deze kaart toont alle wijzigingen die met MapComplete werden gemaakt",
"layers": {
"0": {
"description": "Toont alle wijzigingen met MapComplete",
"filter": {
"0": {
"options": {
"0": {
"question": "Themanaam bevat {search}"
}
}
},
"1": {
"options": {
"0": {
"question": "Gemaakt door bijdrager {search}"
}
}
},
"2": {
"options": {
"0": {
"question": "<b>Niet</b> gemaakt door bijdrager {search}"
}
}
}
},
"tagRenderings": {
"contributor": {
"render": "Wijziging gemaakt door <a href='https://openstreetmap.org/user/{_last_edit:contributor}' target='_blank'>{_last_edit:contributor}</a>"
},
"render_id": {
"render": "Wijzigingset <a href='https://openstreetmap.org/changeset/{id}' target='_blank'>{id}</a>"
},
"theme": {
"mappings": {
"0": {
"then": "Wijziging met <b>officieus</b> thema <a href='https://mapcomplete.osm.be/theme.html?userlayout={theme}'>{theme}</a>"
}
},
"render": "Wijziging met thema <a href='https://mapcomplete.osm.be/{theme}'>{theme}</a>"
}
},
"title": {
"render": "Wijzigingset voor {theme}"
}
},
"1": {
"override": {
"tagRenderings": {
"link_to_more": {
"render": "Meer statistieken kunnen <a href='https://github.com/pietervdvn/MapComplete/tree/develop/Docs/Tools/graphs' target='_blank'>hier</a> gevonden worden"
}
}
}
}
},
"shortDescription": "Toont wijzigingen gemaakt met MapComplete",
"title": "Wijzigingen gemaakt met MapComplete"
},
"maps": {
"description": "Op deze kaart kan je alle kaarten zien die OpenStreetMap kent.<br/><br/>Ontbreekt er een kaart, dan kan je die kaart hier ook gemakelijk aan deze kaart toevoegen.",
"shortDescription": "Dit thema toont alle (toeristische) kaarten die OpenStreetMap kent",
@ -917,18 +856,6 @@
}
}
},
"overrideAll": {
"+tagRenderings": {
"0": {
"mappings": {
"0": {
"then": "Het omvattende gebouw heeft geen gekende deurbreedtes. Voeg een deur en breedte toe."
}
},
"render": "Het gebouw waarin dit zich bevindt kan binnengegaan worden <a href='#{_poi_entrance:id}'>via een deur</a> die {canonical(_poi_entrance:width)} breed is"
}
}
},
"title": "OnWheels"
},
"openwindpowermap": {

94
test.ts
View file

@ -1,9 +1,93 @@
import {Utils} from "./Utils";
import Loading from "./UI/Base/Loading";
import {UIEventSource} from "./Logic/UIEventSource";
import {VariableUiElement} from "./UI/Base/VariableUIElement";
import ChartJs from "./UI/Base/ChartJs";
import Combine from "./UI/Base/Combine";
import * as onwheels from "./assets/generated/themes/onwheels.json"
import FeaturePipelineState from "./Logic/State/FeaturePipelineState";
import LayoutConfig from "./Models/ThemeConfig/LayoutConfig";
const homeUrl = "https://raw.githubusercontent.com/pietervdvn/MapComplete/develop/Docs/Tools/stats/"
const stats_files = "file-overview.json"
const index = UIEventSource.FromPromise(Utils.downloadJson(homeUrl + stats_files))
const layout = new LayoutConfig(<any> onwheels, true)
new FeaturePipelineState(layout)
interface ChangeSetData {
"id": number,
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [number, number][][]
},
"properties": {
"check_user": null,
"reasons": [],
"tags": [],
"features": [],
"user": string,
"uid": string,
"editor": string,
"comment": string,
"comments_count": number,
"source": string,
"imagery_used": string,
"date": string,
"reviewed_features": [],
"create": number,
"modify": number,
"delete": number,
"area": number,
"is_suspect": boolean,
"harmful": any,
"checked": boolean,
"check_date": any,
"metadata": {
"host": string,
"theme": string,
"imagery": string,
"language": string
}
}
}
new VariableUiElement(index.map(paths => {
if (paths === undefined) {
return new Loading("Loading overview...")
}
const downloaded = new UIEventSource<{ features: ChangeSetData[] }[]>([])
for (const filepath of paths) {
Utils.downloadJson(homeUrl + filepath).then(data => {
downloaded.data.push(data)
downloaded.ping()
})
}
return new VariableUiElement(downloaded.map(downloaded => {
const themeBreakdown = new Map<string, number>()
for (const feats of downloaded) {
console.log("Feats:", feats)
for (const feat of feats.features) {
const key = feat.properties.metadata.theme
const count = themeBreakdown.get(key) ?? 0
themeBreakdown.set(key, count + 1)
}
}
const keys = Array.from(themeBreakdown.keys())
const values = keys.map( k => themeBreakdown.get(k))
console.log(keys, values)
return new Combine([
"Got " + downloaded.length + " files out of " + paths.length,
new ChartJs({
type: "pie",
data: {
datasets: [{data: values}],
labels: keys
}
}).SetClass("w-1/3 h-full")
]).SetClass("block w-full h-full")
})).SetClass("block w-full h-full")
})).SetClass("block w-full h-full").AttachTo("maindiv")

View file

@ -1,6 +1,5 @@
import {describe} from 'mocha'
import {expect} from 'chai'
import {Utils} from "../Utils";
describe("TestSuite", () => {

View file

@ -0,0 +1,243 @@
import {describe} from 'mocha'
import {expect} from 'chai'
import {ExtraFuncParams, ExtraFunctions} from "../../Logic/ExtraFunctions";
import {OsmFeature} from "../../Models/OsmFeature";
describe("OverlapFunc", () => {
it("should give doors on the edge", () => {
const door: OsmFeature = {
"type": "Feature",
"id": "node/9909268725",
"properties": {
"automatic_door": "no",
"door": "hinged",
"indoor": "door",
"kerb:height": "0 cm",
"width": "1",
"id": "node/9909268725",
},
"geometry": {
"type": "Point",
"coordinates": [
4.3494436,
50.8657928
]
},
}
const hermanTeirlinck = {
"type": "Feature",
"id": "way/444059131",
"properties": {
"timestamp": "2022-07-27T15:15:01Z",
"version": 27,
"changeset": 124146283,
"user": "Pieter Vander Vennet",
"uid": 3818858,
"addr:city": "Bruxelles - Brussel",
"addr:housenumber": "88",
"addr:postcode": "1000",
"addr:street": "Avenue du Port - Havenlaan",
"building": "government",
"building:levels": "5",
"name": "Herman Teirlinckgebouw",
"operator": "Vlaamse overheid",
"wikidata": "Q47457146",
"wikipedia": "nl:Herman Teirlinckgebouw",
"id": "way/444059131",
"_backend": "https://www.openstreetmap.org",
"_lat": "50.86622355",
"_lon": "4.3501212",
"_layer": "walls_and_buildings",
"_length": "380.5933566256343",
"_length:km": "0.4",
"_now:date": "2022-07-29",
"_now:datetime": "2022-07-29 14:19:25",
"_loaded:date": "2022-07-29",
"_loaded:datetime": "2022-07-29 14:19:25",
"_last_edit:contributor": "Pieter Vander Vennet",
"_last_edit:contributor:uid": 3818858,
"_last_edit:changeset": 124146283,
"_last_edit:timestamp": "2022-07-27T15:15:01Z",
"_version_number": 27,
"_geometry:type": "Polygon",
"_surface": "7461.252251355437",
"_surface:ha": "0.7",
"_country": "be"
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
4.3493369,
50.8658274
],
[
4.3493393,
50.8658266
],
[
4.3494436,
50.8657928
],
[
4.3495272,
50.8657658
],
[
4.349623,
50.8657348
],
[
4.3497442,
50.8656956
],
[
4.3498441,
50.8656632
],
[
4.3500768,
50.8655878
],
[
4.3501619,
50.8656934
],
[
4.3502113,
50.8657551
],
[
4.3502729,
50.8658321
],
[
4.3503063,
50.8658737
],
[
4.3503397,
50.8659153
],
[
4.3504159,
50.8660101
],
[
4.3504177,
50.8660123
],
[
4.3504354,
50.8660345
],
[
4.3505348,
50.8661584
],
[
4.3504935,
50.866172
],
[
4.3506286,
50.8663405
],
[
4.3506701,
50.8663271
],
[
4.3508563,
50.8665592
],
[
4.3509055,
50.8666206
],
[
4.3506278,
50.8667104
],
[
4.3504502,
50.8667675
],
[
4.3503132,
50.8668115
],
[
4.3502162,
50.8668427
],
[
4.3501645,
50.8668593
],
[
4.3499296,
50.8665664
],
[
4.3498821,
50.8665073
],
[
4.3498383,
50.8664527
],
[
4.3498126,
50.8664207
],
[
4.3497459,
50.8663376
],
[
4.3497227,
50.8663086
],
[
4.3496517,
50.8662201
],
[
4.3495158,
50.8660507
],
[
4.3493369,
50.8658274
]
]
]
},
"bbox": {
"maxLat": 50.8668593,
"maxLon": 4.3509055,
"minLat": 50.8655878,
"minLon": 4.3493369
}
}
const params: ExtraFuncParams = {
getFeatureById: id => undefined,
getFeaturesWithin: () => [[door]],
memberships: undefined
}
ExtraFunctions.FullPatchFeature(params, hermanTeirlinck)
const overlap = (<any>hermanTeirlinck).overlapWith("*")
console.log(JSON.stringify(overlap))
expect(overlap[0].feat == door).true
})
})