001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.util; 003 004import java.util.Locale; 005 006import org.openstreetmap.josm.data.osm.Node; 007import org.openstreetmap.josm.data.osm.OsmPrimitive; 008import org.openstreetmap.josm.data.osm.Way; 009import org.openstreetmap.josm.tools.Geometry; 010import org.openstreetmap.josm.tools.SubclassFilteredCollection; 011import org.openstreetmap.josm.tools.Utils; 012 013/** 014 * Determines how an icon is to be rotated depending on the primitive to displayed. 015 */ 016public abstract class RotationAngle { 017 018 /** 019 * Calculates the rotation angle depending on the primitive to displayed. 020 * @param p primitive 021 * @return rotation angle 022 */ 023 public abstract double getRotationAngle(OsmPrimitive p); 024 025 /** 026 * Always returns the fixed {@code angle}. 027 * @param angle angle 028 * @return rotation angle 029 */ 030 public static RotationAngle buildStaticRotation(final double angle) { 031 return new RotationAngle() { 032 @Override 033 public double getRotationAngle(OsmPrimitive p) { 034 return angle; 035 } 036 037 @Override 038 public String toString() { 039 return angle + "rad"; 040 } 041 }; 042 } 043 044 /** 045 * Parses the rotation angle from the specified {@code string}. 046 * @param string angle as string 047 * @return rotation angle 048 */ 049 public static RotationAngle buildStaticRotation(final String string) { 050 try { 051 return buildStaticRotation(parseCardinalRotation(string)); 052 } catch (IllegalArgumentException e) { 053 throw new IllegalArgumentException("Invalid string: " + string, e); 054 } 055 } 056 057 /** 058 * Converts an angle diven in cardinal directions to radians. 059 * The following values are supported: {@code n}, {@code north}, {@code ne}, {@code northeast}, 060 * {@code e}, {@code east}, {@code se}, {@code southeast}, {@code s}, {@code south}, 061 * {@code sw}, {@code southwest}, {@code w}, {@code west}, {@code nw}, {@code northwest}. 062 * @param cardinal the angle in cardinal directions 063 * @return the angle in radians 064 */ 065 public static double parseCardinalRotation(final String cardinal) { 066 switch (cardinal.toLowerCase(Locale.ENGLISH)) { 067 case "n": 068 case "north": 069 return Math.toRadians(0); 070 case "ne": 071 case "northeast": 072 return Math.toRadians(45); 073 case "e": 074 case "east": 075 return Math.toRadians(90); 076 case "se": 077 case "southeast": 078 return Math.toRadians(135); 079 case "s": 080 case "south": 081 return Math.toRadians(180); 082 case "sw": 083 case "southwest": 084 return Math.toRadians(225); 085 case "w": 086 case "west": 087 return Math.toRadians(270); 088 case "nw": 089 case "northwest": 090 return Math.toRadians(315); 091 default: 092 throw new IllegalArgumentException("Unexpected cardinal direction " + cardinal); 093 } 094 } 095 096 /** 097 * Computes the angle depending on the referencing way segment, or {@code 0} if none exists. 098 * @return rotation angle 099 */ 100 public static RotationAngle buildWayDirectionRotation() { 101 return new RotationAngle() { 102 @Override 103 public double getRotationAngle(OsmPrimitive p) { 104 if (!(p instanceof Node)) { 105 return 0; 106 } 107 final Node n = (Node) p; 108 final SubclassFilteredCollection<OsmPrimitive, Way> ways = Utils.filteredCollection(n.getReferrers(), Way.class); 109 if (ways.isEmpty()) { 110 return 0; 111 } 112 final Way w = ways.iterator().next(); 113 final int idx = w.getNodes().indexOf(n); 114 if (idx == 0) { 115 return -Geometry.getSegmentAngle(n.getEastNorth(), w.getNode(idx + 1).getEastNorth()); 116 } else { 117 return -Geometry.getSegmentAngle(w.getNode(idx - 1).getEastNorth(), n.getEastNorth()); 118 } 119 } 120 121 @Override 122 public String toString() { 123 return "way-direction"; 124 } 125 }; 126 } 127}