[8378] | 1 | // License: GPL. For details, see LICENSE file.
|
---|
[8199] | 2 | package org.openstreetmap.josm.gui.util;
|
---|
| 3 |
|
---|
[8404] | 4 | import java.util.Locale;
|
---|
| 5 |
|
---|
[8199] | 6 | import org.openstreetmap.josm.data.osm.Node;
|
---|
| 7 | import org.openstreetmap.josm.data.osm.OsmPrimitive;
|
---|
| 8 | import org.openstreetmap.josm.data.osm.Way;
|
---|
| 9 | import org.openstreetmap.josm.tools.Geometry;
|
---|
| 10 | import org.openstreetmap.josm.tools.SubclassFilteredCollection;
|
---|
| 11 | import org.openstreetmap.josm.tools.Utils;
|
---|
| 12 |
|
---|
| 13 | /**
|
---|
[10599] | 14 | * Determines how an icon is to be rotated depending on the primitive to be displayed.
|
---|
| 15 | * @since 8199 (creation)
|
---|
| 16 | * @since 10599 (functional interface)
|
---|
[8199] | 17 | */
|
---|
[10599] | 18 | @FunctionalInterface
|
---|
| 19 | public interface RotationAngle {
|
---|
[8929] | 20 |
|
---|
[8199] | 21 | /**
|
---|
[11725] | 22 | * The rotation along a way.
|
---|
| 23 | */
|
---|
[11731] | 24 | final class WayDirectionRotationAngle implements RotationAngle {
|
---|
[11725] | 25 | @Override
|
---|
| 26 | public double getRotationAngle(OsmPrimitive p) {
|
---|
| 27 | if (!(p instanceof Node)) {
|
---|
| 28 | return 0;
|
---|
| 29 | }
|
---|
| 30 | final Node n = (Node) p;
|
---|
| 31 | final SubclassFilteredCollection<OsmPrimitive, Way> ways = Utils.filteredCollection(n.getReferrers(), Way.class);
|
---|
| 32 | if (ways.isEmpty()) {
|
---|
| 33 | return 0;
|
---|
| 34 | }
|
---|
| 35 | final Way w = ways.iterator().next();
|
---|
| 36 | final int idx = w.getNodes().indexOf(n);
|
---|
| 37 | if (idx == 0) {
|
---|
| 38 | return -Geometry.getSegmentAngle(n.getEastNorth(), w.getNode(idx + 1).getEastNorth());
|
---|
| 39 | } else {
|
---|
| 40 | return -Geometry.getSegmentAngle(w.getNode(idx - 1).getEastNorth(), n.getEastNorth());
|
---|
| 41 | }
|
---|
| 42 | }
|
---|
| 43 |
|
---|
| 44 | @Override
|
---|
| 45 | public String toString() {
|
---|
| 46 | return "way-direction";
|
---|
| 47 | }
|
---|
| 48 |
|
---|
| 49 | @Override
|
---|
| 50 | public int hashCode() {
|
---|
| 51 | return 1;
|
---|
| 52 | }
|
---|
| 53 |
|
---|
| 54 | @Override
|
---|
| 55 | public boolean equals(Object obj) {
|
---|
| 56 | if (this == obj) {
|
---|
| 57 | return true;
|
---|
| 58 | }
|
---|
[11893] | 59 | return obj != null && getClass() == obj.getClass();
|
---|
[11725] | 60 | }
|
---|
| 61 | }
|
---|
| 62 |
|
---|
| 63 | /**
|
---|
| 64 | * A static rotation
|
---|
| 65 | */
|
---|
[11731] | 66 | final class StaticRotationAngle implements RotationAngle {
|
---|
[11725] | 67 | private final double angle;
|
---|
| 68 |
|
---|
| 69 | private StaticRotationAngle(double angle) {
|
---|
| 70 | this.angle = angle;
|
---|
| 71 | }
|
---|
| 72 |
|
---|
| 73 | @Override
|
---|
| 74 | public double getRotationAngle(OsmPrimitive p) {
|
---|
| 75 | return angle;
|
---|
| 76 | }
|
---|
| 77 |
|
---|
| 78 | @Override
|
---|
| 79 | public String toString() {
|
---|
| 80 | return angle + "rad";
|
---|
| 81 | }
|
---|
| 82 |
|
---|
| 83 | @Override
|
---|
| 84 | public int hashCode() {
|
---|
| 85 | final int prime = 31;
|
---|
| 86 | int result = 1;
|
---|
[11893] | 87 | long temp = Double.doubleToLongBits(angle);
|
---|
[11725] | 88 | result = prime * result + (int) (temp ^ (temp >>> 32));
|
---|
| 89 | return result;
|
---|
| 90 | }
|
---|
| 91 |
|
---|
| 92 | @Override
|
---|
| 93 | public boolean equals(Object obj) {
|
---|
| 94 | if (this == obj) {
|
---|
| 95 | return true;
|
---|
| 96 | }
|
---|
[11893] | 97 | if (obj == null || getClass() != obj.getClass()) {
|
---|
[11725] | 98 | return false;
|
---|
| 99 | }
|
---|
| 100 | StaticRotationAngle other = (StaticRotationAngle) obj;
|
---|
[11893] | 101 | return Double.doubleToLongBits(angle) == Double.doubleToLongBits(other.angle);
|
---|
[11725] | 102 | }
|
---|
| 103 | }
|
---|
| 104 |
|
---|
| 105 | /**
|
---|
[11726] | 106 | * A no-rotation angle that always returns 0.
|
---|
| 107 | * @since 11726
|
---|
| 108 | */
|
---|
[11731] | 109 | RotationAngle NO_ROTATION = new StaticRotationAngle(0);
|
---|
[11726] | 110 |
|
---|
| 111 | /**
|
---|
[10599] | 112 | * Calculates the rotation angle depending on the primitive to be displayed.
|
---|
[8929] | 113 | * @param p primitive
|
---|
| 114 | * @return rotation angle
|
---|
[8199] | 115 | */
|
---|
[10599] | 116 | double getRotationAngle(OsmPrimitive p);
|
---|
[8199] | 117 |
|
---|
| 118 | /**
|
---|
| 119 | * Always returns the fixed {@code angle}.
|
---|
[8929] | 120 | * @param angle angle
|
---|
| 121 | * @return rotation angle
|
---|
[8199] | 122 | */
|
---|
[10599] | 123 | static RotationAngle buildStaticRotation(final double angle) {
|
---|
[11725] | 124 | return new StaticRotationAngle(angle);
|
---|
[8199] | 125 | }
|
---|
| 126 |
|
---|
| 127 | /**
|
---|
| 128 | * Parses the rotation angle from the specified {@code string}.
|
---|
[8929] | 129 | * @param string angle as string
|
---|
| 130 | * @return rotation angle
|
---|
[8199] | 131 | */
|
---|
[10599] | 132 | static RotationAngle buildStaticRotation(final String string) {
|
---|
[8260] | 133 | try {
|
---|
[8394] | 134 | return buildStaticRotation(parseCardinalRotation(string));
|
---|
| 135 | } catch (IllegalArgumentException e) {
|
---|
| 136 | throw new IllegalArgumentException("Invalid string: " + string, e);
|
---|
[8199] | 137 | }
|
---|
| 138 | }
|
---|
| 139 |
|
---|
| 140 | /**
|
---|
| 141 | * Converts an angle diven in cardinal directions to radians.
|
---|
| 142 | * The following values are supported: {@code n}, {@code north}, {@code ne}, {@code northeast},
|
---|
| 143 | * {@code e}, {@code east}, {@code se}, {@code southeast}, {@code s}, {@code south},
|
---|
| 144 | * {@code sw}, {@code southwest}, {@code w}, {@code west}, {@code nw}, {@code northwest}.
|
---|
| 145 | * @param cardinal the angle in cardinal directions
|
---|
| 146 | * @return the angle in radians
|
---|
| 147 | */
|
---|
[10599] | 148 | static double parseCardinalRotation(final String cardinal) {
|
---|
[8404] | 149 | switch (cardinal.toLowerCase(Locale.ENGLISH)) {
|
---|
[8199] | 150 | case "n":
|
---|
| 151 | case "north":
|
---|
[10228] | 152 | return 0; // 0 degree => 0 radian
|
---|
[8199] | 153 | case "ne":
|
---|
| 154 | case "northeast":
|
---|
[12131] | 155 | return Utils.toRadians(45);
|
---|
[8199] | 156 | case "e":
|
---|
| 157 | case "east":
|
---|
[12131] | 158 | return Utils.toRadians(90);
|
---|
[8199] | 159 | case "se":
|
---|
| 160 | case "southeast":
|
---|
[12131] | 161 | return Utils.toRadians(135);
|
---|
[8199] | 162 | case "s":
|
---|
| 163 | case "south":
|
---|
[10228] | 164 | return Math.PI; // 180 degree
|
---|
[8199] | 165 | case "sw":
|
---|
| 166 | case "southwest":
|
---|
[12131] | 167 | return Utils.toRadians(225);
|
---|
[8199] | 168 | case "w":
|
---|
| 169 | case "west":
|
---|
[12131] | 170 | return Utils.toRadians(270);
|
---|
[8199] | 171 | case "nw":
|
---|
| 172 | case "northwest":
|
---|
[12131] | 173 | return Utils.toRadians(315);
|
---|
[8199] | 174 | default:
|
---|
| 175 | throw new IllegalArgumentException("Unexpected cardinal direction " + cardinal);
|
---|
| 176 | }
|
---|
| 177 | }
|
---|
| 178 |
|
---|
| 179 | /**
|
---|
| 180 | * Computes the angle depending on the referencing way segment, or {@code 0} if none exists.
|
---|
[8929] | 181 | * @return rotation angle
|
---|
[8199] | 182 | */
|
---|
[10599] | 183 | static RotationAngle buildWayDirectionRotation() {
|
---|
[11725] | 184 | return new WayDirectionRotationAngle();
|
---|
[8199] | 185 | }
|
---|
| 186 | }
|
---|