2021-03-24 01:25:57 +01:00
import { GeoOperations } from "./GeoOperations" ;
import State from "../State" ;
2021-03-29 02:04:42 +02:00
import { And } from "./Tags/And" ;
import { Tag } from "./Tags/Tag" ;
import { Or } from "./Tags/Or" ;
2021-03-24 01:25:57 +01:00
import { Utils } from "../Utils" ;
import opening_hours from "opening_hours" ;
import { UIElement } from "../UI/UIElement" ;
import Combine from "../UI/Base/Combine" ;
2021-04-21 01:26:13 +02:00
import UpdateTagsFromOsmAPI from "./Actors/UpdateTagsFromOsmAPI" ;
2021-03-24 01:25:57 +01:00
2021-04-22 13:30:00 +02:00
Fix issues with camera rotation
This commit fixes at least these issues that I was aware of:
* Cardinal directions (e.g. NE) were not recognized.
* The camera icon did not rotatie when direction=* was used instead of
camera:direction, but the blue direction visualizer did.
Pietervdvn said he would have liked to convert the code for direction
normalizing to calculatedTags in a JSON file (as documented in
Docs/CalculatedTags.md), but when he saw the oneliners I had to produce
in response, I was allowed to keep it in SimpleMetaTagger.ts for now.
For your amusement, the oneliners are included below.
"calculatedTags": [
"_direction:numerical=(dir => dir === undefined ? undefined : ({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}[dir] ?? (isNaN(parseFloat(dir)) ? undefined : ((parseFloat(dir) % 360 + 360) % 360)))))(feat.properties['camera:direction'] ?? feat.properties.direction)",
"_direction:leftright=feat.properties['_direction:numerical'] === undefined ? undefined : (feat.properties['_direction:numerical'] <= 180 ? 'right' : 'left')"
]
2021-04-28 16:45:48 +02:00
const cardinalDirections = {
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
}
2021-03-24 01:25:57 +01:00
export default class SimpleMetaTagger {
static coder : any ;
2021-04-25 13:25:03 +02:00
public static readonly objectMetaInfo = new SimpleMetaTagger (
{
keys : [ "_last_edit:contributor" ,
"_last_edit:contributor:uid" ,
"_last_edit:changeset" ,
"_last_edit:timestamp" ,
"_version_number" ] ,
doc : "Information about the last edit of this object."
} ,
( feature ) = > { /*Note: also handled by 'UpdateTagsFromOsmAPI'*/
const tgs = feature . properties ;
tgs [ "_last_edit:contributor" ] = tgs [ "user" ]
tgs [ "_last_edit:contributor:uid" ] = tgs [ "uid" ]
tgs [ "_last_edit:changeset" ] = tgs [ "changeset" ]
tgs [ "_last_edit:timestamp" ] = tgs [ "timestamp" ]
tgs [ "_version_number" ] = tgs [ "version" ]
delete tgs [ "timestamp" ]
delete tgs [ "version" ]
delete tgs [ "changeset" ]
delete tgs [ "user" ]
delete tgs [ "uid" ]
}
)
private static latlon = new SimpleMetaTagger ( {
keys : [ "_lat" , "_lon" ] ,
doc : "The latitude and longitude of the point (or centerpoint in the case of a way/area)"
} ,
2021-03-24 01:25:57 +01:00
( 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 ;
feature . _lon = lon ; // This is dirty, I know
feature . _lat = lat ;
} )
) ;
private static surfaceArea = new SimpleMetaTagger (
2021-04-25 13:25:03 +02:00
{
keys : [ "_surface" , "_surface:ha" ] ,
doc : "The surface area of the feature, in square meters and in hectare. Not set on points and ways"
} ,
2021-03-24 01:25:57 +01:00
( feature = > {
const sqMeters = GeoOperations . surfaceAreaInSqMeters ( feature ) ;
feature . properties [ "_surface" ] = "" + sqMeters ;
feature . properties [ "_surface:ha" ] = "" + Math . floor ( sqMeters / 1000 ) / 10 ;
2021-03-26 03:24:58 +01:00
feature . area = sqMeters ;
2021-03-24 01:25:57 +01:00
} )
) ;
2021-04-18 14:24:30 +02:00
private static lngth = new SimpleMetaTagger (
2021-04-25 13:25:03 +02:00
{
keys : [ "_length" , "_length:km" ] ,
doc : "The total length of a feature in meters (and in kilometers, rounded to one decimal for '_length:km'). For a surface, the length of the perimeter"
} ,
2021-04-18 14:24:30 +02:00
( feature = > {
const l = GeoOperations . lengthInMeters ( feature )
feature . properties [ "_length" ] = "" + l
const km = Math . floor ( l / 1000 )
const kmRest = Math . round ( ( l - km * 1000 ) / 100 )
2021-04-25 13:25:03 +02:00
feature . properties [ "_length:km" ] = "" + km + "." + kmRest
2021-04-18 14:24:30 +02:00
} )
)
2021-03-24 01:25:57 +01:00
private static country = new SimpleMetaTagger (
2021-04-25 13:25:03 +02:00
{
keys : [ "_country" ] ,
doc : "The country code of the property (with latlon2country)"
} ,
2021-03-24 01:25:57 +01:00
feature = > {
let centerPoint : any = GeoOperations . centerpoint ( feature ) ;
const lat = centerPoint . geometry . coordinates [ 1 ] ;
const lon = centerPoint . geometry . coordinates [ 0 ] ;
SimpleMetaTagger . GetCountryCodeFor ( lon , lat , ( countries ) = > {
try {
feature . properties [ "_country" ] = countries [ 0 ] . trim ( ) . toLowerCase ( ) ;
2021-04-17 23:36:46 +02:00
const tagsSource = State . state . allElements . getEventSourceById ( feature . properties . id ) ;
2021-03-24 01:25:57 +01:00
tagsSource . ping ( ) ;
} catch ( e ) {
console . warn ( e )
}
} ) ;
}
)
private static isOpen = new SimpleMetaTagger (
2021-04-25 13:25:03 +02:00
{
keys : [ "_isOpen" , "_isOpen:description" ] ,
doc : "If 'opening_hours' is present, it will add the current state of the feature (being 'yes' or 'no')" ,
includesDates : true
} ,
2021-03-24 01:25:57 +01:00
( feature = > {
2021-04-25 13:25:03 +02:00
if ( Utils . runningFromConsole ) {
2021-04-22 13:30:00 +02:00
// We are running from console, thus probably creating a cache
// isOpen is irrelevant
return
}
2021-04-25 13:25:03 +02:00
2021-04-17 23:36:46 +02:00
const tagsSource = State . state . allElements . getEventSourceById ( feature . properties . id ) ;
2021-03-24 01:25:57 +01:00
tagsSource . addCallbackAndRun ( tags = > {
if ( tags . opening_hours === undefined || tags . _country === undefined ) {
return ;
}
try {
const oh = new opening_hours ( tags [ "opening_hours" ] , {
lat : tags._lat ,
lon : tags._lon ,
address : {
country_code : tags._country.toLowerCase ( )
}
} , { tag_key : "opening_hours" } ) ;
// AUtomatically triggered on the next change
const updateTags = ( ) = > {
const oldValueIsOpen = tags [ "_isOpen" ] ;
const oldNextChange = tags [ "_isOpen:nextTrigger" ] ? ? 0 ;
if ( oldNextChange > ( new Date ( ) ) . getTime ( ) &&
tags [ "_isOpen:oldvalue" ] === tags [ "opening_hours" ] ) {
// Already calculated and should not yet be triggered
return ;
}
tags [ "_isOpen" ] = oh . getState ( ) ? "yes" : "no" ;
const comment = oh . getComment ( ) ;
if ( comment ) {
tags [ "_isOpen:description" ] = comment ;
}
if ( oldValueIsOpen !== tags . _isOpen ) {
tagsSource . ping ( ) ;
}
const nextChange = oh . getNextChange ( ) ;
if ( nextChange !== undefined ) {
const timeout = nextChange . getTime ( ) - ( new Date ( ) ) . getTime ( ) ;
tags [ "_isOpen:nextTrigger" ] = nextChange . getTime ( ) ;
tags [ "_isOpen:oldvalue" ] = tags . opening_hours
window . setTimeout (
( ) = > {
console . log ( "Updating the _isOpen tag for " , tags . id , ", it's timer expired after" , timeout ) ;
updateTags ( ) ;
} ,
timeout
)
}
}
updateTags ( ) ;
} catch ( e ) {
console . warn ( "Error while parsing opening hours of " , tags . id , e ) ;
tags [ "_isOpen" ] = "parse_error" ;
}
} )
} )
)
private static directionSimplified = new SimpleMetaTagger (
2021-04-25 13:25:03 +02:00
{
Fix issues with camera rotation
This commit fixes at least these issues that I was aware of:
* Cardinal directions (e.g. NE) were not recognized.
* The camera icon did not rotatie when direction=* was used instead of
camera:direction, but the blue direction visualizer did.
Pietervdvn said he would have liked to convert the code for direction
normalizing to calculatedTags in a JSON file (as documented in
Docs/CalculatedTags.md), but when he saw the oneliners I had to produce
in response, I was allowed to keep it in SimpleMetaTagger.ts for now.
For your amusement, the oneliners are included below.
"calculatedTags": [
"_direction:numerical=(dir => dir === undefined ? undefined : ({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}[dir] ?? (isNaN(parseFloat(dir)) ? undefined : ((parseFloat(dir) % 360 + 360) % 360)))))(feat.properties['camera:direction'] ?? feat.properties.direction)",
"_direction:leftright=feat.properties['_direction:numerical'] === undefined ? undefined : (feat.properties['_direction:numerical'] <= 180 ? 'right' : 'left')"
]
2021-04-28 16:45:48 +02:00
keys : [ "_direction:numerical" , "_direction:leftright" ] ,
doc : "_direction:numerical is a normalized, numerical direction based on 'camera:direction' or on 'direction'; it is only present if a valid direction is found (e.g. 38.5 or NE). _direction:leftright is either 'left' or 'right', which is left-looking on the map or 'right-looking' on the map"
2021-04-25 13:25:03 +02:00
} ,
2021-03-24 01:25:57 +01:00
( feature = > {
const tags = feature . properties ;
const direction = tags [ "camera:direction" ] ? ? tags [ "direction" ] ;
if ( direction === undefined ) {
return ;
}
Fix issues with camera rotation
This commit fixes at least these issues that I was aware of:
* Cardinal directions (e.g. NE) were not recognized.
* The camera icon did not rotatie when direction=* was used instead of
camera:direction, but the blue direction visualizer did.
Pietervdvn said he would have liked to convert the code for direction
normalizing to calculatedTags in a JSON file (as documented in
Docs/CalculatedTags.md), but when he saw the oneliners I had to produce
in response, I was allowed to keep it in SimpleMetaTagger.ts for now.
For your amusement, the oneliners are included below.
"calculatedTags": [
"_direction:numerical=(dir => dir === undefined ? undefined : ({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}[dir] ?? (isNaN(parseFloat(dir)) ? undefined : ((parseFloat(dir) % 360 + 360) % 360)))))(feat.properties['camera:direction'] ?? feat.properties.direction)",
"_direction:leftright=feat.properties['_direction:numerical'] === undefined ? undefined : (feat.properties['_direction:numerical'] <= 180 ? 'right' : 'left')"
]
2021-04-28 16:45:48 +02:00
const n = cardinalDirections [ direction ] ? ? Number ( direction ) ;
2021-03-24 01:25:57 +01:00
if ( isNaN ( n ) ) {
return ;
}
Fix issues with camera rotation
This commit fixes at least these issues that I was aware of:
* Cardinal directions (e.g. NE) were not recognized.
* The camera icon did not rotatie when direction=* was used instead of
camera:direction, but the blue direction visualizer did.
Pietervdvn said he would have liked to convert the code for direction
normalizing to calculatedTags in a JSON file (as documented in
Docs/CalculatedTags.md), but when he saw the oneliners I had to produce
in response, I was allowed to keep it in SimpleMetaTagger.ts for now.
For your amusement, the oneliners are included below.
"calculatedTags": [
"_direction:numerical=(dir => dir === undefined ? undefined : ({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}[dir] ?? (isNaN(parseFloat(dir)) ? undefined : ((parseFloat(dir) % 360 + 360) % 360)))))(feat.properties['camera:direction'] ?? feat.properties.direction)",
"_direction:leftright=feat.properties['_direction:numerical'] === undefined ? undefined : (feat.properties['_direction:numerical'] <= 180 ? 'right' : 'left')"
]
2021-04-28 16:45:48 +02:00
// The % operator has range (-360, 360). We apply a trick to get [0, 360).
const normalized = ( ( n % 360 ) + 360 ) % 360 ;
2021-03-24 01:25:57 +01:00
Fix issues with camera rotation
This commit fixes at least these issues that I was aware of:
* Cardinal directions (e.g. NE) were not recognized.
* The camera icon did not rotatie when direction=* was used instead of
camera:direction, but the blue direction visualizer did.
Pietervdvn said he would have liked to convert the code for direction
normalizing to calculatedTags in a JSON file (as documented in
Docs/CalculatedTags.md), but when he saw the oneliners I had to produce
in response, I was allowed to keep it in SimpleMetaTagger.ts for now.
For your amusement, the oneliners are included below.
"calculatedTags": [
"_direction:numerical=(dir => dir === undefined ? undefined : ({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}[dir] ?? (isNaN(parseFloat(dir)) ? undefined : ((parseFloat(dir) % 360 + 360) % 360)))))(feat.properties['camera:direction'] ?? feat.properties.direction)",
"_direction:leftright=feat.properties['_direction:numerical'] === undefined ? undefined : (feat.properties['_direction:numerical'] <= 180 ? 'right' : 'left')"
]
2021-04-28 16:45:48 +02:00
tags [ "_direction:numerical" ] = normalized ;
tags [ "_direction:leftright" ] = normalized <= 180 ? "right" : "left" ;
2021-03-24 01:25:57 +01:00
} )
)
private static carriageWayWidth = new SimpleMetaTagger (
2021-04-25 13:25:03 +02:00
{
keys : [ "_width:needed" , "_width:needed:no_pedestrians" , "_width:difference" ] ,
doc : "Legacy for a specific project calculating the needed width for safe traffic on a road. Only activated if 'width:carriageway' is present"
} ,
2021-03-24 01:25:57 +01:00
( feature : any , index : number ) = > {
const properties = feature . properties ;
if ( properties [ "width:carriageway" ] === undefined ) {
return ;
}
const carWidth = 2 ;
const cyclistWidth = 1.5 ;
const pedestrianWidth = 0.75 ;
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 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 {
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 ) ;
}
) ;
private static currentTime = new SimpleMetaTagger (
2021-04-25 13:25:03 +02:00
{
keys : [ "_now:date" , "_now:datetime" , "_loaded:date" , "_loaded:_datetime" ] ,
doc : "Adds the time that the data got loaded - pretty much the time of downloading from overpass. The format is YYYY-MM-DD hh:mm, aka 'sortable' aka ISO-8601-but-not-entirely" ,
includesDates : true
} ,
2021-03-24 01:25:57 +01:00
( feature , _ , freshness ) = > {
const now = new Date ( ) ;
if ( typeof freshness === "string" ) {
freshness = new Date ( freshness )
}
function date ( d : Date ) {
return d . toISOString ( ) . slice ( 0 , 10 ) ;
}
function datetime ( d : Date ) {
return d . toISOString ( ) . slice ( 0 , - 5 ) . replace ( "T" , " " ) ;
}
feature . properties [ "_now:date" ] = date ( now ) ;
feature . properties [ "_now:datetime" ] = datetime ( now ) ;
feature . properties [ "_loaded:date" ] = date ( freshness ) ;
feature . properties [ "_loaded:datetime" ] = datetime ( freshness ) ;
}
)
public static metatags = [
SimpleMetaTagger . latlon ,
SimpleMetaTagger . surfaceArea ,
2021-04-18 14:24:30 +02:00
SimpleMetaTagger . lngth ,
2021-03-24 01:25:57 +01:00
SimpleMetaTagger . country ,
SimpleMetaTagger . isOpen ,
SimpleMetaTagger . carriageWayWidth ,
SimpleMetaTagger . directionSimplified ,
2021-04-25 13:25:03 +02:00
SimpleMetaTagger . currentTime ,
SimpleMetaTagger . objectMetaInfo
2021-03-24 01:25:57 +01:00
] ;
2021-04-25 13:25:03 +02:00
public readonly keys : string [ ] ;
public readonly doc : string ;
public readonly includesDates : boolean
private readonly _f : ( feature : any , index : number , freshness : Date ) = > void ;
constructor ( docs : { keys : string [ ] , doc : string , includesDates? : boolean } , f : ( ( feature : any , index : number , freshness : Date ) = > void ) ) {
this . keys = docs . keys ;
this . doc = docs . doc ;
this . _f = f ;
this . includesDates = docs . includesDates ? ? false ;
for ( const key of docs . keys ) {
if ( ! key . startsWith ( '_' ) ) {
throw ` Incorrect metakey ${ key } : it should start with underscore (_) `
}
}
}
2021-03-24 01:25:57 +01:00
static GetCountryCodeFor ( lon : number , lat : number , callback : ( country : string ) = > void ) {
2021-04-22 13:30:00 +02:00
SimpleMetaTagger . coder ? . GetCountryCodeFor ( lon , lat , callback )
2021-03-24 01:25:57 +01:00
}
static HelpText ( ) : UIElement {
const subElements : UIElement [ ] = [
new Combine ( [
2021-03-24 02:01:04 +01:00
"<h2>Metatags</h2>" ,
2021-04-25 16:01:46 +02:00
"<p>Metatags are extra tags available, in order to display more data or to give better questions.</p>" ,
"<p>The are calculated automatically on every feature when the data arrives in the webbrowser. This document gives an overview of the available metatags.</p>" ,
"<p><b>Hint:</b> when using metatags, add the [query parameter](URL_Parameters.md) `debug=true` to the URL. This will include a box in the popup for features which shows all the properties of the object</p>"
2021-03-24 01:25:57 +01:00
] )
] ;
2021-04-25 13:25:03 +02:00
for ( const metatag of SimpleMetaTagger . metatags ) {
2021-03-24 01:25:57 +01:00
subElements . push (
new Combine ( [
"<h3>" , metatag . keys . join ( ", " ) , "</h3>" ,
metatag . doc ]
)
)
}
return new Combine ( subElements )
}
2021-04-25 13:25:03 +02:00
addMetaTags ( features : { feature : any , freshness : Date } [ ] ) {
for ( let i = 0 ; i < features . length ; i ++ ) {
let feature = features [ i ] ;
this . _f ( feature . feature , i , feature . freshness ) ;
}
}
2021-03-24 01:25:57 +01:00
Fix issues with camera rotation
This commit fixes at least these issues that I was aware of:
* Cardinal directions (e.g. NE) were not recognized.
* The camera icon did not rotatie when direction=* was used instead of
camera:direction, but the blue direction visualizer did.
Pietervdvn said he would have liked to convert the code for direction
normalizing to calculatedTags in a JSON file (as documented in
Docs/CalculatedTags.md), but when he saw the oneliners I had to produce
in response, I was allowed to keep it in SimpleMetaTagger.ts for now.
For your amusement, the oneliners are included below.
"calculatedTags": [
"_direction:numerical=(dir => dir === undefined ? undefined : ({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}[dir] ?? (isNaN(parseFloat(dir)) ? undefined : ((parseFloat(dir) % 360 + 360) % 360)))))(feat.properties['camera:direction'] ?? feat.properties.direction)",
"_direction:leftright=feat.properties['_direction:numerical'] === undefined ? undefined : (feat.properties['_direction:numerical'] <= 180 ? 'right' : 'left')"
]
2021-04-28 16:45:48 +02:00
}