Modify

Opened 2 years ago

Closed 2 years ago

Last modified 23 months ago

#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)

text_fuct_parent_way_angle.mapcss (1.4 KB ) - added by mikeho 23 months ago.
railway_signal_ger.zip (153.7 KB ) - added by mikeho 23 months ago.

Download all attachments as: .zip

Change History (17)

comment:1 by taylor.smock, 2 years ago

In 18661/josm:

Fix #22703, see #22695, #22704: 90° offset of icon-rotation: way; in mapcss (patch by Woazboat, modified)

This is due to Geometry.getSegmentAngle getting the rotation from the positive
x-axis instead of the positive y-axis, but this is easily corrected by
subtracting 90 degrees (pi/2) from the calculation.

The previous behavior differed from the static rotation angle behavior, which
uses the positive y-axis.

comment:2 by taylor.smock, 2 years ago

Resolution: fixed
Status: newclosed

In 18664/josm:

Fix #22704: Add parent_way_angle mapcss function (patch by Woazboat)

comment:3 by taylor.smock, 2 years ago

Milestone: 23.02

comment:4 by marcello@…, 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:

https://github.com/MarcelloPerathoner/josm/blob/db91553ca76761592a63a0f759d186aa44c1e273/src/org/openstreetmap/josm/gui/mappaint/mapcss/Functions.java#L655

See also:

https://wiki.openstreetmap.org/wiki/Key:traffic_sign#As_part_of_a_way

comment:5 by marcello@…, 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.

comment:6 by taylor.smock, 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

Last edited 2 years ago by taylor.smock (previous) (diff)

comment:7 by marcello@…, 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.

in reply to:  6 comment:8 by skyper, 2 years ago

Replying to taylor.smock:

@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).

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.

Last edited 2 years ago by skyper (previous) (diff)

comment:9 by Woazboat, 2 years ago

@marcello: Like I said in #22695, feel free to submit a patch with your improvements

comment:10 by Woazboat, 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 marcello@…, 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 Woazboat, 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_anglereturned 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 mikeho, 23 months ago

I tested the function with the appended style. It seems the function doesn't work well (returns nothing).

comment:14 by taylor.smock, 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 mikeho, 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.

Last edited 23 months ago by mikeho (previous) (diff)

by mikeho, 23 months ago

Attachment: railway_signal_ger.zip added

Modify Ticket

Change Properties
Set your email in Preferences
Action
as closed The owner will remain team.
as The resolution will be set.
The resolution will be deleted. Next status will be 'reopened'.

Add Comment


E-mail address and name can be saved in the Preferences .
 
Note: See TracTickets for help on using tickets.