Speedup loading of opening hours; add slope measurements to stairs
This commit is contained in:
parent
3cf6ebbb69
commit
d8c22ca6be
4 changed files with 259 additions and 134 deletions
|
@ -289,6 +289,35 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"condition": "conveying!=yes"
|
"condition": "conveying!=yes"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "incline",
|
||||||
|
"render": {
|
||||||
|
"en": "These stairs have an incline of {incline}"
|
||||||
|
},
|
||||||
|
"freeform": {
|
||||||
|
"key": "incline",
|
||||||
|
"type": "slope"
|
||||||
|
},
|
||||||
|
"question": {
|
||||||
|
"en": "What is the incline of these stairs?"
|
||||||
|
},
|
||||||
|
"mappings": [
|
||||||
|
{
|
||||||
|
"if": "incline=up",
|
||||||
|
"then": {
|
||||||
|
"en": "The upward direction is {direction_absolute()}"
|
||||||
|
},
|
||||||
|
"hideInAnswer": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"if": "incline=down",
|
||||||
|
"then": {
|
||||||
|
"en": "The downward direction is {direction_absolute()}"
|
||||||
|
},
|
||||||
|
"hideInAnswer": true
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,35 @@ import { Utils } from "../Utils"
|
||||||
export class GeoOperations {
|
export class GeoOperations {
|
||||||
private static readonly _earthRadius = 6378137
|
private static readonly _earthRadius = 6378137
|
||||||
private static readonly _originShift = (2 * Math.PI * GeoOperations._earthRadius) / 2
|
private static readonly _originShift = (2 * Math.PI * GeoOperations._earthRadius) / 2
|
||||||
|
private static readonly directions = ["N", "NE", "E", "SE", "S", "SW", "W", "NW"] as const
|
||||||
|
private static readonly directionsRelative = [
|
||||||
|
"straight",
|
||||||
|
"slight_right",
|
||||||
|
"right",
|
||||||
|
"sharp_right",
|
||||||
|
"behind",
|
||||||
|
"sharp_left",
|
||||||
|
"left",
|
||||||
|
"slight_left",
|
||||||
|
] as const
|
||||||
|
private static reverseBearing = {
|
||||||
|
N: 0,
|
||||||
|
NNE: 22.5,
|
||||||
|
NE: 45,
|
||||||
|
ENE: 67.5,
|
||||||
|
E: 90,
|
||||||
|
ESE: 112.5,
|
||||||
|
SE: 135,
|
||||||
|
SSE: 157.5,
|
||||||
|
S: 180,
|
||||||
|
SSW: 202.5,
|
||||||
|
SW: 225,
|
||||||
|
WSW: 247.5,
|
||||||
|
W: 270,
|
||||||
|
WNW: 292.5,
|
||||||
|
NW: 315,
|
||||||
|
NNW: 337.5,
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a union between two features
|
* Create a union between two features
|
||||||
|
@ -293,9 +322,11 @@ export class GeoOperations {
|
||||||
* @param way
|
* @param way
|
||||||
*/
|
*/
|
||||||
public static forceLineString(way: Feature<LineString | Polygon>): Feature<LineString>
|
public static forceLineString(way: Feature<LineString | Polygon>): Feature<LineString>
|
||||||
|
|
||||||
public static forceLineString(
|
public static forceLineString(
|
||||||
way: Feature<MultiLineString | MultiPolygon>
|
way: Feature<MultiLineString | MultiPolygon>
|
||||||
): Feature<MultiLineString>
|
): Feature<MultiLineString>
|
||||||
|
|
||||||
public static forceLineString(
|
public static forceLineString(
|
||||||
way: Feature<LineString | MultiLineString | Polygon | MultiPolygon>
|
way: Feature<LineString | MultiLineString | Polygon | MultiPolygon>
|
||||||
): Feature<LineString | MultiLineString> {
|
): Feature<LineString | MultiLineString> {
|
||||||
|
@ -800,6 +831,146 @@ export class GeoOperations {
|
||||||
return { lon, lat }
|
return { lon, lat }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static SplitSelfIntersectingWays(features: Feature[]): Feature[] {
|
||||||
|
const result: Feature[] = []
|
||||||
|
|
||||||
|
for (const feature of features) {
|
||||||
|
if (feature.geometry.type === "LineString") {
|
||||||
|
let coors = feature.geometry.coordinates
|
||||||
|
for (let i = coors.length - 1; i >= 0; i--) {
|
||||||
|
// Go back, to nick of the back when needed
|
||||||
|
const ci = coors[i]
|
||||||
|
for (let j = i + 1; j < coors.length; j++) {
|
||||||
|
const cj = coors[j]
|
||||||
|
if (
|
||||||
|
Math.abs(ci[0] - cj[0]) <= 0.000001 &&
|
||||||
|
Math.abs(ci[1] - cj[1]) <= 0.0000001
|
||||||
|
) {
|
||||||
|
// Found a self-intersecting way!
|
||||||
|
console.debug("SPlitting way", feature.properties.id)
|
||||||
|
result.push({
|
||||||
|
...feature,
|
||||||
|
geometry: { ...feature.geometry, coordinates: coors.slice(i + 1) },
|
||||||
|
})
|
||||||
|
coors = coors.slice(0, i + 1)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.push({
|
||||||
|
...feature,
|
||||||
|
geometry: { ...feature.geometry, coordinates: coors },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GeoOperations.distanceToHuman(52.8) // => "53m"
|
||||||
|
* GeoOperations.distanceToHuman(2800) // => "2.8km"
|
||||||
|
* GeoOperations.distanceToHuman(12800) // => "13km"
|
||||||
|
*
|
||||||
|
* @param meters
|
||||||
|
*/
|
||||||
|
public static distanceToHuman(meters: number): string {
|
||||||
|
if (meters === undefined) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
meters = Math.round(meters)
|
||||||
|
if (meters < 1000) {
|
||||||
|
return meters + "m"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (meters >= 10000) {
|
||||||
|
const km = Math.round(meters / 1000)
|
||||||
|
return km + "km"
|
||||||
|
}
|
||||||
|
|
||||||
|
meters = Math.round(meters / 100)
|
||||||
|
const kmStr = "" + meters
|
||||||
|
|
||||||
|
return kmStr.substring(0, kmStr.length - 1) + "." + kmStr.substring(kmStr.length - 1) + "km"
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GeoOperations.parseBearing("N") // => 0
|
||||||
|
* GeoOperations.parseBearing("E") // => 90
|
||||||
|
* GeoOperations.parseBearing("NE") // => 22.5
|
||||||
|
*
|
||||||
|
* GeoOperations.parseBearing("90") // => 90
|
||||||
|
* GeoOperations.parseBearing("-90°") // => 270
|
||||||
|
* GeoOperations.parseBearing("180 °") // => 180
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public static parseBearing(str: string | number) {
|
||||||
|
let n: number
|
||||||
|
if (typeof str === "string") {
|
||||||
|
str = str.trim()
|
||||||
|
if (str.endsWith("°")) {
|
||||||
|
str = str.substring(0, str.length - 1).trim()
|
||||||
|
}
|
||||||
|
n = Number(str)
|
||||||
|
} else {
|
||||||
|
n = str
|
||||||
|
}
|
||||||
|
if (!isNaN(n)) {
|
||||||
|
while (n < 0) {
|
||||||
|
n += 360
|
||||||
|
}
|
||||||
|
return n % 360
|
||||||
|
}
|
||||||
|
return GeoOperations.reverseBearing[str]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GeoOperations.bearingToHuman(0) // => "N"
|
||||||
|
* GeoOperations.bearingToHuman(-9) // => "N"
|
||||||
|
* GeoOperations.bearingToHuman(-10) // => "N"
|
||||||
|
* GeoOperations.bearingToHuman(-180) // => "S"
|
||||||
|
* GeoOperations.bearingToHuman(181) // => "S"
|
||||||
|
* GeoOperations.bearingToHuman(46) // => "NE"
|
||||||
|
*/
|
||||||
|
public static bearingToHuman(
|
||||||
|
bearing: number
|
||||||
|
): "N" | "NE" | "E" | "SE" | "S" | "SW" | "W" | "NW" {
|
||||||
|
while (bearing < 0) {
|
||||||
|
bearing += 360
|
||||||
|
}
|
||||||
|
bearing %= 360
|
||||||
|
bearing += 22.5
|
||||||
|
const segment = Math.floor(bearing / 45) % GeoOperations.directions.length
|
||||||
|
return GeoOperations.directions[segment]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GeoOperations.bearingToHuman(0) // => "N"
|
||||||
|
* GeoOperations.bearingToHuman(-10) // => "N"
|
||||||
|
* GeoOperations.bearingToHuman(-180) // => "S"
|
||||||
|
* GeoOperations.bearingToHuman(181) // => "S"
|
||||||
|
* GeoOperations.bearingToHuman(46) // => "NE"
|
||||||
|
*/
|
||||||
|
public static bearingToHumanRelative(
|
||||||
|
bearing: number
|
||||||
|
):
|
||||||
|
| "straight"
|
||||||
|
| "slight_right"
|
||||||
|
| "right"
|
||||||
|
| "sharp_right"
|
||||||
|
| "behind"
|
||||||
|
| "sharp_left"
|
||||||
|
| "left"
|
||||||
|
| "slight_left" {
|
||||||
|
while (bearing < 0) {
|
||||||
|
bearing += 360
|
||||||
|
}
|
||||||
|
bearing %= 360
|
||||||
|
bearing += 22.5
|
||||||
|
const segment = Math.floor(bearing / 45) % GeoOperations.directionsRelative.length
|
||||||
|
return GeoOperations.directionsRelative[segment]
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function which does the heavy lifting for 'inside'
|
* Helper function which does the heavy lifting for 'inside'
|
||||||
*/
|
*/
|
||||||
|
@ -949,126 +1120,4 @@ export class GeoOperations {
|
||||||
}
|
}
|
||||||
throw "CalculateIntersection fallthrough: can not calculate an intersection between features"
|
throw "CalculateIntersection fallthrough: can not calculate an intersection between features"
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SplitSelfIntersectingWays(features: Feature[]): Feature[] {
|
|
||||||
const result: Feature[] = []
|
|
||||||
|
|
||||||
for (const feature of features) {
|
|
||||||
if (feature.geometry.type === "LineString") {
|
|
||||||
let coors = feature.geometry.coordinates
|
|
||||||
for (let i = coors.length - 1; i >= 0; i--) {
|
|
||||||
// Go back, to nick of the back when needed
|
|
||||||
const ci = coors[i]
|
|
||||||
for (let j = i + 1; j < coors.length; j++) {
|
|
||||||
const cj = coors[j]
|
|
||||||
if (
|
|
||||||
Math.abs(ci[0] - cj[0]) <= 0.000001 &&
|
|
||||||
Math.abs(ci[1] - cj[1]) <= 0.0000001
|
|
||||||
) {
|
|
||||||
// Found a self-intersecting way!
|
|
||||||
console.debug("SPlitting way", feature.properties.id)
|
|
||||||
result.push({
|
|
||||||
...feature,
|
|
||||||
geometry: { ...feature.geometry, coordinates: coors.slice(i + 1) },
|
|
||||||
})
|
|
||||||
coors = coors.slice(0, i + 1)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result.push({
|
|
||||||
...feature,
|
|
||||||
geometry: { ...feature.geometry, coordinates: coors },
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GeoOperations.distanceToHuman(52.8) // => "53m"
|
|
||||||
* GeoOperations.distanceToHuman(2800) // => "2.8km"
|
|
||||||
* GeoOperations.distanceToHuman(12800) // => "13km"
|
|
||||||
*
|
|
||||||
* @param meters
|
|
||||||
*/
|
|
||||||
public static distanceToHuman(meters: number): string {
|
|
||||||
if (meters === undefined) {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
meters = Math.round(meters)
|
|
||||||
if (meters < 1000) {
|
|
||||||
return meters + "m"
|
|
||||||
}
|
|
||||||
|
|
||||||
if (meters >= 10000) {
|
|
||||||
const km = Math.round(meters / 1000)
|
|
||||||
return km + "km"
|
|
||||||
}
|
|
||||||
|
|
||||||
meters = Math.round(meters / 100)
|
|
||||||
const kmStr = "" + meters
|
|
||||||
|
|
||||||
return kmStr.substring(0, kmStr.length - 1) + "." + kmStr.substring(kmStr.length - 1) + "km"
|
|
||||||
}
|
|
||||||
|
|
||||||
private static readonly directions = ["N", "NE", "E", "SE", "S", "SW", "W", "NW"] as const
|
|
||||||
private static readonly directionsRelative = [
|
|
||||||
"straight",
|
|
||||||
"slight_right",
|
|
||||||
"right",
|
|
||||||
"sharp_right",
|
|
||||||
"behind",
|
|
||||||
"sharp_left",
|
|
||||||
"left",
|
|
||||||
"slight_left",
|
|
||||||
] as const
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GeoOperations.bearingToHuman(0) // => "N"
|
|
||||||
* GeoOperations.bearingToHuman(-9) // => "N"
|
|
||||||
* GeoOperations.bearingToHuman(-10) // => "N"
|
|
||||||
* GeoOperations.bearingToHuman(-180) // => "S"
|
|
||||||
* GeoOperations.bearingToHuman(181) // => "S"
|
|
||||||
* GeoOperations.bearingToHuman(46) // => "NE"
|
|
||||||
*/
|
|
||||||
public static bearingToHuman(
|
|
||||||
bearing: number
|
|
||||||
): "N" | "NE" | "E" | "SE" | "S" | "SW" | "W" | "NW" {
|
|
||||||
while (bearing < 0) {
|
|
||||||
bearing += 360
|
|
||||||
}
|
|
||||||
bearing %= 360
|
|
||||||
bearing += 22.5
|
|
||||||
const segment = Math.floor(bearing / 45) % GeoOperations.directions.length
|
|
||||||
return GeoOperations.directions[segment]
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GeoOperations.bearingToHuman(0) // => "N"
|
|
||||||
* GeoOperations.bearingToHuman(-10) // => "N"
|
|
||||||
* GeoOperations.bearingToHuman(-180) // => "S"
|
|
||||||
* GeoOperations.bearingToHuman(181) // => "S"
|
|
||||||
* GeoOperations.bearingToHuman(46) // => "NE"
|
|
||||||
*/
|
|
||||||
public static bearingToHumanRelative(
|
|
||||||
bearing: number
|
|
||||||
):
|
|
||||||
| "straight"
|
|
||||||
| "slight_right"
|
|
||||||
| "right"
|
|
||||||
| "sharp_right"
|
|
||||||
| "behind"
|
|
||||||
| "sharp_left"
|
|
||||||
| "left"
|
|
||||||
| "slight_left" {
|
|
||||||
while (bearing < 0) {
|
|
||||||
bearing += 360
|
|
||||||
}
|
|
||||||
bearing %= 360
|
|
||||||
bearing += 22.5
|
|
||||||
const segment = Math.floor(bearing / 45) % GeoOperations.directionsRelative.length
|
|
||||||
return GeoOperations.directionsRelative[segment]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -144,6 +144,9 @@ class CountryTagger extends SimpleMetaTagger {
|
||||||
tagsSource.data["_country"] = newCountry
|
tagsSource.data["_country"] = newCountry
|
||||||
tagsSource?.ping()
|
tagsSource?.ping()
|
||||||
} else {
|
} else {
|
||||||
|
// We set, be we don't ping... this is for later
|
||||||
|
tagsSource.data["_country"] = newCountry
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* What is this weird construction?
|
* What is this weird construction?
|
||||||
*
|
*
|
||||||
|
@ -160,7 +163,6 @@ class CountryTagger extends SimpleMetaTagger {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
window.requestIdleCallback(() => {
|
window.requestIdleCallback(() => {
|
||||||
tagsSource.data["_country"] = newCountry
|
|
||||||
tagsSource?.ping()
|
tagsSource?.ping()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -478,10 +480,19 @@ export default class SimpleMetaTaggers {
|
||||||
// isOpen is irrelevant
|
// isOpen is irrelevant
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if (feature.properties.opening_hours === undefined) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if (feature.properties.opening_hours === "24/7") {
|
if (feature.properties.opening_hours === "24/7") {
|
||||||
feature.properties._isOpen = "yes"
|
feature.properties._isOpen = "yes"
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
console.log(
|
||||||
|
"Calculating opening hours for",
|
||||||
|
feature.properties.name,
|
||||||
|
":",
|
||||||
|
feature.properties.opening_hours
|
||||||
|
)
|
||||||
|
|
||||||
// _isOpen is calculated dynamically on every call
|
// _isOpen is calculated dynamically on every call
|
||||||
Object.defineProperty(feature.properties, "_isOpen", {
|
Object.defineProperty(feature.properties, "_isOpen", {
|
||||||
|
@ -492,7 +503,8 @@ export default class SimpleMetaTaggers {
|
||||||
if (tags.opening_hours === undefined) {
|
if (tags.opening_hours === undefined) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (tags._country === undefined) {
|
const country = tags._country
|
||||||
|
if (country === undefined) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1598,6 +1598,41 @@ export default class SpecialVisualizations {
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
funcName: "direction_absolute",
|
||||||
|
docs: "Converts compass degrees (with 0° being north, 90° being east, ...) into a human readable, translated direction such as 'north', 'northeast'",
|
||||||
|
args: [
|
||||||
|
{
|
||||||
|
name: "key",
|
||||||
|
doc: "The attribute containing the degrees",
|
||||||
|
defaultValue: "_direction:centerpoint",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
needsUrls: [],
|
||||||
|
constr(
|
||||||
|
state: SpecialVisualizationState,
|
||||||
|
tagSource: UIEventSource<Record<string, string>>,
|
||||||
|
args: string[],
|
||||||
|
feature: Feature,
|
||||||
|
layer: LayerConfig
|
||||||
|
): BaseUIElement {
|
||||||
|
const key = args[0] === "" ? "_direction:centerpoint" : args[0]
|
||||||
|
return new VariableUiElement(
|
||||||
|
tagSource
|
||||||
|
.map((tags) => {
|
||||||
|
console.log("Direction value", tags[key], key)
|
||||||
|
return tags[key]
|
||||||
|
})
|
||||||
|
.mapD((value) => {
|
||||||
|
const dir = GeoOperations.bearingToHuman(
|
||||||
|
GeoOperations.parseBearing(value)
|
||||||
|
)
|
||||||
|
console.log("Human dir", dir)
|
||||||
|
return Translations.t.general.visualFeedback.directionsAbsolute[dir]
|
||||||
|
})
|
||||||
|
)
|
||||||
|
},
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
specialVisualizations.push(new AutoApplyButton(specialVisualizations))
|
specialVisualizations.push(new AutoApplyButton(specialVisualizations))
|
||||||
|
|
Loading…
Reference in a new issue