#22704 closed enhancement (fixed)
[Patch] Add parent_way_angle() mapcss function
Reported by: | Woazboat | Owned by: | team |
---|---|---|---|
Priority: | normal | Milestone: | 23.02 |
Component: | Core mappaint | Version: | |
Keywords: | Cc: |
Description
Add a mapcss function that returns the angle of the parent way section of a node (matched via child element selector) in radians. Similar to the existing icon-rotation: way;, just as a function which enables a lot more flexibility.
https://github.com/JOSM/josm/pull/110
https://github.com/JOSM/josm/pull/110.patch
Patch is based on changes made for #22703
See also #22695, #10217
https://josm.openstreetmap.de/ticket/10217#comment:7
Attachments (2)
Change History (17)
comment:1 by , 2 years ago
comment:3 by , 2 years ago
Milestone: | → 23.02 |
---|
comment:4 by , 2 years ago
I think this is a bad solution and should be reverted. The icon-rotation: way
kludge should be reverted too.
Reason:
Afore mentioned patches always select one parent way (out of potentially many) in a random fashion. This can provoke tagging mistakes if the node is member of more than one way.
Consider adding a traffic_sign=maxspeed direction=forward
on a node that splits a highway. If the two ways at the split go in opposite directions the node has no meaningful direction=forward
but this patch will nonetheless select one direction at random and rotate the traffic sign accordingly. In 50% of the cases the user will be induced to believe his tagging is correct when in fact the tagging is wrong.
A correct solution (albeit more involved) that considers all parent ways is outlined here:
See also:
https://wiki.openstreetmap.org/wiki/Key:traffic_sign#As_part_of_a_way
comment:5 by , 2 years ago
Another reason:
with a correct solution the validator can find tagging errors where a direction
is set on a node that has no meaningful direction.
follow-up: 8 comment:6 by , 2 years ago
@marcello: You need a better reason than direction=forward
for traffic_sign=*
. If you have a better reason, please share. Anyway, from the osmwiki:Key:traffic_sign#As_part_of_a_way:
The affected direction can only be specified by using nodes which are part of exactly one way.
As far as I can remember, this hasn't changed since 2018 or so (I wrote a validator rule once to find this issue around that time, I don't think it is in JOSM core though).
Example mapcss:
way[highway] > node[highway=stop][direction=forward] { icon-image: "presets/vehicle/restriction/stop"; icon-rotation: way; } way[highway] > node[highway=stop][direction=backward] { icon-image: "presets/vehicle/restriction/stop"; icon-rotation: -way; }
(note: we don't currently support -way
)
With this patch, the roughly equivalent mapcss is
way[highway] > node[highway=stop][direction=forward] { icon-image: "presets/vehicle/restriction/stop"; icon-rotation: parent_way_angle(); } way[highway] > node[highway=stop][direction=backward] { icon-image: "presets/vehicle/restriction/stop"; icon-rotation: -parent_way_angle(); }
EDIT: If you think the current behavior is wrong and can point at cases where the tagging is correct but rendering would be wrong, I'd be more inclined to revert
comment:7 by , 2 years ago
My point was that the rendering could be right even if the tagging was wrong. It doesn't suffice to be correct when the tagging is correct, but the function must also signal an error where the tagging is wrong. It doesn't.
You can easily find examples that violate the "one-way rule" in the wiki:
https://www.openstreetmap.org/node/279669516
https://www.openstreetmap.org/node/286094618
https://www.openstreetmap.org/node/3644830783
(The first one is a clear tagging error because the node is on a T junction of a street and a footway => the icon should *not* rotate.)
I think that rule is stupid because in many cases the only correct place for a traffic sign (eg. maxspeed) is at the split of the road and the direction is perfectly well defined unless the roads go in opposite directions.
comment:8 by , 2 years ago
Replying to taylor.smock:
@marcello: You need a better reason than
direction=forward
fortraffic_sign=*
. If you have a better reason, please share. Anyway, from the osmwiki:Key:traffic_sign#As_part_of_a_way:
The affected direction can only be specified by using nodes which are part of exactly one way.
As far as I can remember, this hasn't changed since 2018 or so (I wrote a validator rule once to find this issue around that time, I don't think it is in JOSM core though).
Yes, it is in core but only an informational warning: Disputed usage of direction on node
direction=forward/backward
on nodes is non-sense a broken concept as you always have to look at the parent way which is not needed at all if you use cardinal directions or values in degree.
comment:9 by , 2 years ago
@marcello: Like I said in #22695, feel free to submit a patch with your improvements
comment:10 by , 2 years ago
About the traffic sign example:
- Icon direction has nothing to do with the validator
- A map style for traffic signs ideally shouldn't display a rotated traffic sign symbol in the first place when its invalid (because its placed at the junction of two ways with different directions). It should display an appropriate warning/error symbol instead.
comment:11 by , 2 years ago
You could a rule like this:
node[direction=forward][heading() = 0.0] { icon-image: broken.png; }
In the validator:
node[direction=forward][heading() = 0.0] { throwWarning: tr("Node with bogus forward direction."); }
It would be even better if you could test for heading() = null
, but I don't think that works yet?
comment:12 by , 2 years ago
parent_way_angle()
intentionally returns null instead of 0 to allow differentiating the two cases. Not sure if the way angle function is the best way to do this detection though. And yes, it would be nice if parent_way_angle
returned null if the way angle is ambiguous because there are multiple matching parent candidates. It already does that if there are no matching parents. Unfortunately that requires extending the mapcss matching environment to include a list of parent matches instead of just the first one it encounters (similar to what it already does for child matches).
IIRC null
would be parsed as a string in your example.
comment:13 by , 23 months ago
I tested the function with the appended style. It seems the function doesn't work well (returns nothing).
by , 23 months ago
Attachment: | text_fuct_parent_way_angle.mapcss added |
---|
comment:14 by , 23 months ago
Replacing the node|z18-::[...]
with way > node|z18-::[...]
worked.
I guess I should have asked everyone what they thought the function was supposed to do. @Woazboat believes that the it was up to the user to set the correct parent way, @marcello believes that the code should try to pick the right one, and @mikho believes it should automatically select the correct way, at least if there is only one parent way.
comment:15 by , 23 months ago
May thanks ! I apologize for the misunderstanding. To avoid this, I uploaded the MapCSS style in ticket #22965.
This is the corresponding path in the style in the different version of support by JOSM:
/* sgnl stands for "signal" direction position of osm way ________________________________________________ sgnl_00 - not set, not set (one or both) sgnl_FL - forward, left sgnl_FM - forward, middle (e.g. bridge) sgnl_FR - forward, right sgnl_BL - backward, left sgnl_BM - backward, middle (e.g. bridge) sgnl_BR - backward, right */ node[is_prop_set("sgnl_00", "default")][is_prop_set("sgnl_hght_miss", "default")]::L1 { z-index: 501; icon-image: "icon/sgnl_hght_miss_normal.svg"; icon-width: 61; icon-height: 121; allow-overlap: true; } node[is_prop_set("sgnl_FL", "default")][is_prop_set("sgnl_hght_miss", "default")]::L1 { icon-rotation: way; icon-offset-y: -35; z-index: 501; icon-image: "icon/sgnl_hght_miss_forwrd.svg"; icon-width: 122; icon-height: 121; allow-overlap: true; } node[is_prop_set("sgnl_FM", "default")][is_prop_set("sgnl_hght_miss", "default")]::L1 { icon-rotation: way; icon-offset-y: 0; z-index: 501; icon-image: "icon/sgnl_hght_miss_forwrd.svg"; icon-width: 122; icon-height: 121; allow-overlap: true; } node[is_prop_set("sgnl_FR", "default")][is_prop_set("sgnl_hght_miss", "default")]::L1 { icon-rotation: way; icon-offset-y: 35; z-index: 501; icon-image: "icon/sgnl_hght_miss_forwrd.svg"; icon-width: 122; icon-height: 121; allow-overlap: true; } node[is_prop_set("sgnl_BL", "default")][is_prop_set("sgnl_hght_miss", "default")]::L1 { icon-rotation: way; icon-offset-y: -35; z-index: 501; icon-image: "icon/sgnl_hght_miss_bckwrd.svg"; icon-width: 122; icon-height: 121; allow-overlap: true; } node[is_prop_set("sgnl_BM", "default")][is_prop_set("sgnl_hght_miss", "default")]::L1 { icon-rotation: way; icon-offset-y: 0; z-index: 501; icon-image: "icon/sgnl_hght_miss_bckwrd.svg"; icon-width: 122; icon-height: 121; allow-overlap: true; } node[is_prop_set("sgnl_BR", "default")][is_prop_set("sgnl_hght_miss", "default")]::L1 { icon-rotation: way; icon-offset-y: 35; z-index: 501; icon-image: "icon/sgnl_hght_miss_bckwrd.svg"; icon-width: 122; icon-height: 121; allow-overlap: true; } /* this should be working since Version 18678 (March 2023) (my idea !): */ node[is_prop_set("sgnl_00", "default")][is_prop_set("sgnl_hght_miss", "default")]::L1 { z-index: 501; icon-image: "icon/sgnl_hght_miss.svg"; icon-width: 61; icon-height: 121; allow-overlap: true; } node[is_prop_set("sgnl_FL", "default")][is_prop_set("sgnl_hght_miss", "default")]::L1 { icon-rotation: parent_way_angle(); icon-offset-y: -35; z-index: 501; icon-image: "icon/sgnl_hght_miss.svg"; icon-width: 122; icon-height: 121; allow-overlap: true; } node[is_prop_set("sgnl_FM", "default")][is_prop_set("sgnl_hght_miss", "default")]::L1 { icon-rotation: parent_way_angle(); icon-offset-y: 0; z-index: 501; icon-image: "icon/sgnl_hght_miss.svg"; icon-width: 122; icon-height: 121; allow-overlap: true; } node[is_prop_set("sgnl_FR", "default")][is_prop_set("sgnl_hght_miss", "default")]::L1 { icon-rotation: parent_way_angle(); icon-offset-y: 35; z-index: 501; icon-image: "icon/sgnl_hght_miss.svg"; icon-width: 122; icon-height: 121; allow-overlap: true; } node[is_prop_set("sgnl_BL", "default")][is_prop_set("sgnl_hght_miss", "default")]::L1 { icon-rotation: parent_way_angle() + 180°; icon-offset-y: -35; z-index: 501; icon-image: "icon/sgnl_hght_miss.svg"; icon-width: 122; icon-height: 121; allow-overlap: true; } node[is_prop_set("sgnl_BM", "default")][is_prop_set("sgnl_hght_miss", "default")]::L1 { icon-rotation: parent_way_angle() + 180°; icon-offset-y: 0; z-index: 501; icon-image: "icon/sgnl_hght_miss.svg"; icon-width: 122; icon-height: 121; allow-overlap: true; } node[is_prop_set("sgnl_BR", "default")][is_prop_set("sgnl_hght_miss", "default")]::L1 { icon-rotation: parent_way_angle() + 180°; icon-offset-y: 35; z-index: 501; icon-image: "icon/sgnl_hght_miss.svg"; icon-width: 122; icon-height: 121; allow-overlap: true; } /* this is working since Version 18678 (March 2023 after tickets #22703 #22704: */ way > node[is_prop_set("sgnl_00", "default")][is_prop_set("sgnl_hght_miss", "default")]::L1 { z-index: 501; icon-image: "icon/sgnl_hght_miss.svg"; icon-width: 101; icon-height: 201; allow-overlap: true; } way > node[is_prop_set("sgnl_FL", "default")][is_prop_set("sgnl_hght_miss", "default")]::L1 { icon-rotation: parent_way_angle(); icon-offset-x: -35; z-index: 501; icon-image: "icon/sgnl_hght_miss.svg"; icon-width: 202; icon-height: 201; allow-overlap: true; } way > node[is_prop_set("sgnl_FM", "default")][is_prop_set("sgnl_hght_miss", "default")]::L1 { icon-rotation: parent_way_angle(); icon-offset-x: 32; z-index: 501; icon-image: "icon/sgnl_hght_miss.svg"; icon-width: 202; icon-height: 201; allow-overlap: true; } way > node[is_prop_set("sgnl_FR", "default")][is_prop_set("sgnl_hght_miss", "default")]::L1 { icon-rotation: parent_way_angle(); icon-offset-x: 35; z-index: 501; icon-image: "icon/sgnl_hght_miss.svg"; icon-width: 202; icon-height: 201; allow-overlap: true; } way > node[is_prop_set("sgnl_BL", "default")][is_prop_set("sgnl_hght_miss", "default")]::L1 { icon-rotation: eval( parent_way_angle() + 180deg ) ; icon-offset-x: -35; z-index: 501; icon-image: "icon/sgnl_hght_miss.svg"; icon-width: 202; icon-height: 201; allow-overlap: true; } way > node[is_prop_set("sgnl_BM", "default")][is_prop_set("sgnl_hght_miss", "default")]::L1 { icon-rotation: eval( parent_way_angle() + 180deg ) ; icon-offset-x: -32; z-index: 501; icon-image: "icon/sgnl_hght_miss.svg"; icon-width: 202; icon-height: 201; allow-overlap: true; } way > node[is_prop_set("sgnl_BR", "default")][is_prop_set("sgnl_hght_miss", "default")]::L1 { icon-rotation: eval( parent_way_angle() + 180deg ) ; icon-offset-x: 35; z-index: 501; icon-image: "icon/sgnl_hght_miss.svg"; icon-width: 202; icon-height: 201; allow-overlap: true; }
Attention - possible error:
/* it works well: */ ... icon-rotation: eval( parent_way_angle() + 180deg ) ; ... /* it works well: */ ... icon-rotation: parent_way_angle() + 3.1415927 ; ... /* but this doesn't work: */ ... icon-rotation: parent_way_angle() + 180deg ; ...
Attention - possible error 2:
/* none rotated: */ icon-width: 101; /* rotated: */ icon-width: 202; /* why 2*101 is needed ? */
See also attached Style Version.
by , 23 months ago
Attachment: | railway_signal_ger.zip added |
---|
In 18661/josm: