1 package org.djunits.quantity;
2
3 import org.djunits.quantity.Direction.Reference;
4 import org.djunits.quantity.def.AbsQuantity;
5 import org.djunits.quantity.def.AbstractReference;
6 import org.djunits.quantity.def.Quantity;
7 import org.djunits.unit.Units;
8
9 /**
10 * Direction is the absolute equivalent of Angle, and can, e.g., represent an angle relative to a defined "zero" angle such as
11 * NORTH or EAST.
12 * <p>
13 * Copyright (c) 2025-2026 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
14 * for project information <a href="https://djunits.org" target="_blank">https://djunits.org</a>. The DJUNITS project is
15 * distributed under a <a href="https://djunits.org/docs/license.html" target="_blank">three-clause BSD-style license</a>.
16 * @author Alexander Verbraeck
17 */
18 public class Direction extends AbsQuantity<Direction, Angle, Reference>
19 {
20 /** */
21 private static final long serialVersionUID = 600L;
22
23 /**
24 * Instantiate a Direction quantity with a unit and a reference point.
25 * @param valueInUnit the angle value, expressed in a angle unit
26 * @param unit the angle unit in which the value is expressed, relative to the reference point
27 * @param reference the reference point of this direction
28 */
29 public Direction(final double valueInUnit, final Angle.Unit unit, final Reference reference)
30 {
31 super(new Angle(valueInUnit, unit), reference);
32 }
33
34 /**
35 * Instantiate a Direction quantity with a unit, expressed as a String, and a reference point.
36 * @param valueInUnit the angle value, expressed in the unit, relative to the reference point
37 * @param abbreviation the String abbreviation of the unit in which the value is expressed
38 * @param reference the reference point of this direction
39 */
40 public Direction(final double valueInUnit, final String abbreviation, final Reference reference)
41 {
42 this(valueInUnit, Units.resolve(Angle.Unit.class, abbreviation), reference);
43 }
44
45 /**
46 * Instantiate a Direction instance based on an angle and a reference point.
47 * @param angle the angle, relative to the reference point
48 * @param reference the reference point of this direction
49 */
50 public Direction(final Angle angle, final Reference reference)
51 {
52 super(angle, reference);
53 }
54
55 /**
56 * Return a Direction instance based on an SI value and a reference point.
57 * @param si the angle si value, relative to the reference point
58 * @param reference the reference point of this direction
59 * @return the Direction instance based on an SI value
60 */
61 public static Direction ofSi(final double si, final Reference reference)
62 {
63 return new Direction(si, Angle.Unit.SI, reference);
64 }
65
66 @Override
67 public Direction instantiate(final Angle angle, final Reference reference)
68 {
69 return new Direction(angle, reference);
70 }
71
72 /**
73 * Returns a Direction representation of a textual representation of a value with a unit. The String representation that can
74 * be parsed is the double value in the unit, followed by a localized or English abbreviation of the unit. Spaces are
75 * allowed, but not required, between the value and the unit.
76 * @param text the textual representation to parse into a Direction
77 * @param reference the reference point of this direction
78 * @return the Scalar representation of the value in its unit
79 * @throws IllegalArgumentException when the text cannot be parsed
80 * @throws NullPointerException when the text argument is null
81 */
82 public static Direction valueOf(final String text, final Reference reference)
83 {
84 return new Direction(Quantity.valueOf(text, Angle.ZERO), reference);
85 }
86
87 /**
88 * Returns a Direction based on a value and the textual representation of the unit, which can be localized.
89 * @param valueInUnit the value, expressed in the unit as given by unitString
90 * @param unitString the textual representation of the unit
91 * @param reference the reference point of this direction
92 * @return the Scalar representation of the value in its unit
93 * @throws IllegalArgumentException when the unit cannot be parsed or is incorrect
94 * @throws NullPointerException when the unitString argument is null
95 */
96 public static Direction of(final double valueInUnit, final String unitString, final Reference reference)
97 {
98 return new Direction(Quantity.of(valueInUnit, unitString, Angle.ZERO), reference);
99 }
100
101 @Override
102 public Angle subtract(final Direction other)
103 {
104 var otherRef = other.relativeTo(getReference());
105 return Angle.ofSi(si() - otherRef.si()).setDisplayUnit(getDisplayUnit());
106 }
107
108 @Override
109 public Direction add(final Angle other)
110 {
111 return new Direction(Angle.ofSi(si() + other.si()).setDisplayUnit(getDisplayUnit()), getReference());
112 }
113
114 @Override
115 public Direction subtract(final Angle other)
116 {
117 return new Direction(Angle.ofSi(si() - other.si()).setDisplayUnit(getDisplayUnit()), getReference());
118 }
119
120 /**
121 * The reference class to define a reference point for the direction.
122 */
123 public static final class Reference extends AbstractReference<Reference, Direction, Angle>
124 {
125 /** East is zero. */
126 public static final Reference EAST = new Reference("EAST", "East = 0 degrees (counter-clockwise)", Angle.ZERO, null);
127
128 /** North is zero. */
129 public static final Reference NORTH =
130 new Reference("NORTH", "North = 0 degrees (counter-clockwise)", Angle.HALF_PI, EAST);
131
132 /**
133 * Define a new reference point for the direction.
134 * @param id the id
135 * @param name the name or explanation
136 * @param offset the offset w.r.t. the offsetReference
137 * @param offsetReference the reference to which the offset is relative
138 */
139 public Reference(final String id, final String name, final Angle offset, final Reference offsetReference)
140 {
141 super(id, name, offset, offsetReference);
142 }
143
144 /**
145 * Define a new reference point for the direction.
146 * @param id the id
147 * @param name the name or explanation
148 * @param offset the offset w.r.t. EAST
149 */
150 public Reference(final String id, final String name, final Angle offset)
151 {
152 this(id, name, offset, EAST);
153 }
154
155 /**
156 * Define a new reference point for the direction.
157 * @param id the id
158 * @param name the name or explanation
159 * @param offset the offset w.r.t. offsetReference
160 * @param offsetReference the reference to which the offset is relative
161 */
162 public static void add(final String id, final String name, final Angle offset, final Reference offsetReference)
163 {
164 new Reference(id, name, offset, offsetReference);
165 }
166
167 /**
168 * Define a new reference point for the direction.
169 * @param id the id
170 * @param name the name or explanation
171 * @param offset the offset w.r.t. EAST
172 */
173 public static void add(final String id, final String name, final Angle offset)
174 {
175 new Reference(id, name, offset);
176 }
177
178 /**
179 * Get a reference point for the direction, based on its id. Return null when the id could not be found.
180 * @param id the id
181 * @return the DirectionReference object
182 */
183 public static Reference get(final String id)
184 {
185 return AbstractReference.get(Direction.Reference.class, id);
186 }
187
188 @Override
189 public Direction instantiate(final Angle angle)
190 {
191 return new Direction(angle, this);
192 }
193 }
194 }