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