forked from MapComplete/MapComplete
Fix all the bugs, feature-complete with the non-refactored version
This commit is contained in:
parent
5adc355a48
commit
25f2aa8e92
11 changed files with 467 additions and 99 deletions
|
@ -56,6 +56,9 @@ export class FilteredLayer {
|
|||
|
||||
const iconUrl = layerDef.icon?.GetRenderValue(tags)?.txt ?? "./assets/bug.svg";
|
||||
const iconSize = (layerDef.iconSize?.GetRenderValue(tags)?.txt ?? "40,40,center").split(",");
|
||||
|
||||
|
||||
const dashArray = layerDef.dashArray.GetRenderValue(tags)?.txt.split(" ").map(Number);
|
||||
|
||||
function num(str, deflt = 40) {
|
||||
const n = Number(str);
|
||||
|
@ -97,7 +100,8 @@ export class FilteredLayer {
|
|||
popupAnchor: [0, 3 - anchorH]
|
||||
},
|
||||
color: color,
|
||||
weight: weight
|
||||
weight: weight,
|
||||
dashArray: dashArray
|
||||
};
|
||||
};
|
||||
this.name = name;
|
||||
|
|
|
@ -2,6 +2,8 @@ import {GeoOperations} from "./GeoOperations";
|
|||
import CodeGrid from "./Web/CodeGrid";
|
||||
import State from "../State";
|
||||
import opening_hours from "opening_hours";
|
||||
import {And, Or, Tag} from "./Tags";
|
||||
import {Utils} from "../Utils";
|
||||
|
||||
|
||||
class SimpleMetaTagger {
|
||||
|
@ -40,90 +42,201 @@ class SimpleMetaTagger {
|
|||
export default class MetaTagging {
|
||||
|
||||
|
||||
private static latlon = new SimpleMetaTagger(["_lat", "_lon"], "The latitude and longitude of the point (or centerpoint in the case of a way/area)",
|
||||
(feature => {
|
||||
const centerPoint = GeoOperations.centerpoint(feature);
|
||||
const lat = centerPoint.geometry.coordinates[1];
|
||||
const lon = centerPoint.geometry.coordinates[0];
|
||||
feature.properties["_lat"] = "" + lat;
|
||||
feature.properties["_lon"] = "" + lon;
|
||||
})
|
||||
);
|
||||
private static surfaceArea = new SimpleMetaTagger(
|
||||
["_surface", "_surface:ha"], "The surface area of the feature, in square meters and in hectare. Not set on points and ways",
|
||||
(feature => {
|
||||
const sqMeters = GeoOperations.surfaceAreaInSqMeters(feature);
|
||||
feature.properties["_surface"] = "" + sqMeters;
|
||||
feature.properties["_surface:ha"] = "" + Math.floor(sqMeters / 1000) / 10;
|
||||
|
||||
})
|
||||
);
|
||||
private static country = new SimpleMetaTagger(
|
||||
["_country"], "The country code of the point",
|
||||
((feature, index) => {
|
||||
const centerPoint = GeoOperations.centerpoint(feature);
|
||||
const lat = centerPoint.geometry.coordinates[1];
|
||||
const lon = centerPoint.geometry.coordinates[0]
|
||||
// But the codegrid SHOULD be a number!
|
||||
CodeGrid.getCode(lat, lon, (error, code) => {
|
||||
if (error === null) {
|
||||
feature.properties["_country"] = code;
|
||||
|
||||
// There is a huge performance issue: if there are ~1000 features receiving a ping at the same time,
|
||||
// The application hangs big time
|
||||
// So we disable pinging all together
|
||||
|
||||
} else {
|
||||
console.warn("Could not determine country for", feature.properties.id, error);
|
||||
}
|
||||
});
|
||||
})
|
||||
)
|
||||
private static isOpen = new SimpleMetaTagger(
|
||||
["_isOpen", "_isOpen:description"], "If 'opening_hours' is present, it will add the current state of the feature (being 'yes' or 'no",
|
||||
(feature => {
|
||||
const tagsSource = State.state.allElements.addOrGetElement(feature);
|
||||
tagsSource.addCallback(tags => {
|
||||
|
||||
if (tags["opening_hours"] !== undefined && tags["_country"] !== undefined) {
|
||||
|
||||
if (tags._isOpen !== undefined) {
|
||||
// Already defined
|
||||
return;
|
||||
}
|
||||
|
||||
const oh = new opening_hours(tags["opening_hours"], {
|
||||
lat: tags._lat,
|
||||
lon: tags._lon,
|
||||
address: {
|
||||
country_code: tags._country
|
||||
}
|
||||
}, {tag_key: "opening_hours"});
|
||||
|
||||
const updateTags = () => {
|
||||
tags["_isOpen"] = oh.getState() ? "yes" : "no";
|
||||
const comment = oh.getComment();
|
||||
if (comment) {
|
||||
tags["_isOpen:description"] = comment;
|
||||
}
|
||||
const nextChange = oh.getNextChange() as Date;
|
||||
if (nextChange !== undefined) {
|
||||
window.setTimeout(
|
||||
updateTags,
|
||||
(nextChange.getTime() - (new Date()).getTime())
|
||||
)
|
||||
}
|
||||
}
|
||||
updateTags();
|
||||
}
|
||||
|
||||
})
|
||||
})
|
||||
)
|
||||
|
||||
public static carriageWayWidth = new SimpleMetaTagger(
|
||||
["_width:needed","_width:needed:no_pedestrians", "_width:difference"],
|
||||
"Legacy for a specific project calculating the needed width for safe traffic on a road",
|
||||
(feature: any, index: number) => {
|
||||
|
||||
const carWidth = 2;
|
||||
const cyclistWidth = 1.5;
|
||||
const pedestrianWidth = 0.75;
|
||||
|
||||
const properties = feature.properties;
|
||||
|
||||
const _leftSideParking =
|
||||
new And([new Tag("parking:lane:left", "parallel"), new Tag("parking:lane:right", "no_parking")]);
|
||||
const _rightSideParking =
|
||||
new And([new Tag("parking:lane:right", "parallel"), new Tag("parking:lane:left", "no_parking")]);
|
||||
|
||||
const _bothSideParking = new Tag("parking:lane:both", "parallel");
|
||||
const _noSideParking = new Tag("parking:lane:both", "no_parking");
|
||||
const _otherParkingMode =
|
||||
new Or([
|
||||
new Tag("parking:lane:both", "perpendicular"),
|
||||
new Tag("parking:lane:left", "perpendicular"),
|
||||
new Tag("parking:lane:right", "perpendicular"),
|
||||
new Tag("parking:lane:both", "diagonal"),
|
||||
new Tag("parking:lane:left", "diagonal"),
|
||||
new Tag("parking:lane:right", "diagonal"),
|
||||
])
|
||||
|
||||
const _sidewalkBoth = new Tag("sidewalk", "both");
|
||||
const _sidewalkLeft = new Tag("sidewalk", "left");
|
||||
const _sidewalkRight = new Tag("sidewalk", "right");
|
||||
const _sidewalkNone = new Tag("sidewalk", "none");
|
||||
|
||||
|
||||
let parkingStateKnown = true;
|
||||
let parallelParkingCount = 0;
|
||||
|
||||
|
||||
const _oneSideParking = new Or([_leftSideParking, _rightSideParking]);
|
||||
|
||||
if (_oneSideParking.matchesProperties(properties)) {
|
||||
parallelParkingCount = 1;
|
||||
} else if (_bothSideParking.matchesProperties(properties)) {
|
||||
parallelParkingCount = 2;
|
||||
} else if (_noSideParking.matchesProperties(properties)) {
|
||||
parallelParkingCount = 0;
|
||||
} else if (_otherParkingMode.matchesProperties(properties)) {
|
||||
parallelParkingCount = 0;
|
||||
} else {
|
||||
parkingStateKnown = false;
|
||||
console.log("No parking data for ", properties.name, properties.id, properties)
|
||||
}
|
||||
|
||||
|
||||
let pedestrianFlowNeeded;
|
||||
if (_sidewalkBoth.matchesProperties(properties)) {
|
||||
pedestrianFlowNeeded = 0;
|
||||
} else if (_sidewalkNone.matchesProperties(properties)) {
|
||||
pedestrianFlowNeeded = 2;
|
||||
} else if (_sidewalkLeft.matchesProperties(properties) || _sidewalkRight.matchesProperties(properties)) {
|
||||
pedestrianFlowNeeded = 1;
|
||||
} else {
|
||||
pedestrianFlowNeeded = -1;
|
||||
}
|
||||
|
||||
|
||||
let onewayCar = properties.oneway === "yes";
|
||||
let onewayBike = properties["oneway:bicycle"] === "yes" ||
|
||||
(onewayCar && properties["oneway:bicycle"] === undefined)
|
||||
|
||||
let cyclingAllowed =
|
||||
!(properties.bicycle === "use_sidepath"
|
||||
|| properties.bicycle === "no");
|
||||
|
||||
let carWidthUsed = (onewayCar ? 1 : 2) * carWidth;
|
||||
properties["_width:needed:cars"] = Utils.Round(carWidthUsed);
|
||||
properties["_width:needed:parking"] = Utils.Round(parallelParkingCount * carWidth)
|
||||
|
||||
|
||||
let cyclistWidthUsed = 0;
|
||||
if (cyclingAllowed) {
|
||||
cyclistWidthUsed = (onewayBike ? 1 : 2) * cyclistWidth;
|
||||
}
|
||||
properties["_width:needed:cyclists"] = Utils.Round(cyclistWidthUsed)
|
||||
|
||||
|
||||
const width = parseFloat(properties["width:carriageway"]);
|
||||
|
||||
|
||||
const targetWidthIgnoringPedestrians =
|
||||
carWidthUsed +
|
||||
cyclistWidthUsed +
|
||||
parallelParkingCount * carWidthUsed;
|
||||
properties["_width:needed:no_pedestrians"] =Utils.Round(targetWidthIgnoringPedestrians);
|
||||
|
||||
const pedestriansNeed = Math.max(0, pedestrianFlowNeeded) * pedestrianWidth;
|
||||
const targetWidth = targetWidthIgnoringPedestrians + pedestriansNeed ;
|
||||
properties["_width:needed"] = Utils.Round(targetWidth);
|
||||
properties["_width:needed:pedestrians"] = Utils.Round(pedestriansNeed)
|
||||
|
||||
|
||||
properties["_width:difference"] = Utils.Round(targetWidth - width );
|
||||
properties["_width:difference:no_pedestrians"] = Utils.Round(targetWidthIgnoringPedestrians - width) ;
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
public static metatags = [
|
||||
new SimpleMetaTagger(["_lat", "_lon"], "The latitude and longitude of the point (or centerpoint in the case of a way/area)",
|
||||
(feature => {
|
||||
const centerPoint = GeoOperations.centerpoint(feature);
|
||||
const lat = centerPoint.geometry.coordinates[1];
|
||||
const lon = centerPoint.geometry.coordinates[0];
|
||||
feature.properties["_lat"] = "" + lat;
|
||||
feature.properties["_lon"] = "" + lon;
|
||||
})
|
||||
),
|
||||
new SimpleMetaTagger(
|
||||
["_surface", "_surface:ha"], "The surface area of the feature, in square meters and in hectare. Not set on points and ways",
|
||||
(feature => {
|
||||
const sqMeters = GeoOperations.surfaceAreaInSqMeters(feature);
|
||||
feature.properties["_surface"] = "" + sqMeters;
|
||||
feature.properties["_surface:ha"] = "" + Math.floor(sqMeters / 1000) / 10;
|
||||
MetaTagging.latlon,
|
||||
MetaTagging.surfaceArea,
|
||||
MetaTagging.country,
|
||||
MetaTagging.isOpen,
|
||||
MetaTagging.carriageWayWidth
|
||||
|
||||
})
|
||||
),
|
||||
|
||||
|
||||
new SimpleMetaTagger(
|
||||
["_country"], "The country code of the point",
|
||||
((feature, index) => {
|
||||
const centerPoint = GeoOperations.centerpoint(feature);
|
||||
const lat = centerPoint.geometry.coordinates[1];
|
||||
const lon = centerPoint.geometry.coordinates[0]
|
||||
// But the codegrid SHOULD be a number!
|
||||
CodeGrid.getCode(lat, lon, (error, code) => {
|
||||
if (error === null) {
|
||||
feature.properties["_country"] = code;
|
||||
|
||||
// There is a huge performance issue: if there are ~1000 features receiving a ping at the same time,
|
||||
// The application hangs big time
|
||||
// So we disable pinging all together
|
||||
|
||||
} else {
|
||||
console.warn("Could not determine country for", feature.properties.id, error);
|
||||
}
|
||||
});
|
||||
})
|
||||
),
|
||||
new SimpleMetaTagger(
|
||||
["_isOpen", "_isOpen:description"], "If 'opening_hours' is present, it will add the current state of the feature (being 'yes' or 'no",
|
||||
(feature => {
|
||||
const tagsSource = State.state.allElements.addOrGetElement(feature);
|
||||
tagsSource.addCallback(tags => {
|
||||
|
||||
if (tags["opening_hours"] !== undefined && tags["_country"] !== undefined) {
|
||||
|
||||
if (tags._isOpen !== undefined) {
|
||||
// Already defined
|
||||
return;
|
||||
}
|
||||
|
||||
const oh = new opening_hours(tags["opening_hours"], {
|
||||
lat: tags._lat,
|
||||
lon: tags._lon,
|
||||
address: {
|
||||
country_code: tags._country
|
||||
}
|
||||
}, {tag_key: "opening_hours"});
|
||||
|
||||
const updateTags = () => {
|
||||
tags["_isOpen"] = oh.getState() ? "yes" : "no";
|
||||
const comment = oh.getComment();
|
||||
if (comment) {
|
||||
tags["_isOpen:description"] = comment;
|
||||
}
|
||||
const nextChange = oh.getNextChange() as Date;
|
||||
if (nextChange !== undefined) {
|
||||
window.setTimeout(
|
||||
updateTags,
|
||||
(nextChange.getTime() - (new Date()).getTime())
|
||||
)
|
||||
}
|
||||
}
|
||||
updateTags();
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
)
|
||||
];
|
||||
|
||||
static addMetatags(features: any[]) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue