001/*
002 * Java GPX Library (jpx-3.1.0).
003 * Copyright (c) 2016-2023 Franz Wilhelmstötter
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 *
017 * Author:
018 *    Franz Wilhelmstötter (franz.wilhelmstoetter@gmail.com)
019 */
020package io.jenetics.jpx;
021
022import static java.lang.String.format;
023import static java.util.Objects.requireNonNull;
024
025import java.io.DataInput;
026import java.io.DataOutput;
027import java.io.IOException;
028import java.io.InvalidObjectException;
029import java.io.ObjectInputStream;
030import java.io.Serial;
031import java.io.Serializable;
032
033/**
034 * Represents the GPS speed value in m/s.
035 *
036 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
037 * @version 1.2
038 * @since 1.0
039 */
040public final class Speed
041        extends Number
042        implements
043                Comparable<Speed>,
044                Serializable
045{
046
047        @Serial
048        private static final long serialVersionUID = 2L;
049
050        /**
051         * Represents a given speed unit.
052         */
053        public enum Unit {
054
055                /**
056                 * Represents the speed unit <em>meters per second</em>: <b>m/s</b>.
057                 */
058                METERS_PER_SECOND(1.0),
059
060                /**
061                 * Represents the speed unit <em>kilometers per hour</em>: <b>km/h</b>.
062                 */
063                KILOMETERS_PER_HOUR(5.0/18.0),
064
065                /**
066                 * Represents the speed unit <em>miles per hour</em>: <b>mi/h</b>.
067                 */
068                MILES_PER_HOUR(1_397.0/3_125.0),
069
070                /**
071                 * Represents the speed unit <em>knots</em>: <b>kt</b>.
072                 */
073                KNOTS(463.0/900.0),
074
075                /**
076                 * Represents the speed unit <em>mach</em>: <b>Ma</b>.
077                 */
078                MACH(331.3);
079
080                // The conversion factor to the base unit m/s.
081                private final double _factor;
082
083                Unit(final double factor) {
084                        _factor = factor;
085                }
086
087                /**
088                 * Convert the given speed value of the given {@code sourceUnit} into a
089                 * speed value of {@code this} speed unit. The given example converts 3
090                 * knots into kilometers per hour.
091                 *
092                 * <pre>{@code
093                 * final double kilometersPerHour = KILOMETERS_PER_HOUR.convert(3, KNOTS);
094                 * }</pre>
095                 *
096                 * @param speed the speed value
097                 * @param sourceUnit the source speed unit
098                 * @return the speed value of {@code this} speed unit
099                 */
100                public double convert(final double speed, final Unit sourceUnit) {
101                        requireNonNull(sourceUnit);
102                        final double metersPerSecond = speed*sourceUnit._factor;
103                        return metersPerSecond/_factor;
104                }
105
106        }
107
108
109        private final double _value;
110
111        /**
112         * Create a new GPS {@code Speed} object in m/s.
113         *
114         * @param value the GPS speed value in m/s.
115         */
116        private Speed(final double value) {
117                _value = value;
118        }
119
120        /**
121         * Return the GPS speed value in m/s.
122         *
123         * @return the GPS speed value in m/s
124         */
125        @Override
126        public double doubleValue() {
127                return _value;
128        }
129
130        /**
131         * Return the GPS speed value in the desired unit.
132         *
133         * @param unit the speed unit
134         * @return the GPS speed value in the desired unit
135         * @throws NullPointerException if the given speed {@code unit} is
136         *         {@code null}
137         */
138        public double to(final Unit unit) {
139                requireNonNull(unit);
140                return unit.convert(_value, Unit.METERS_PER_SECOND);
141        }
142
143        @Override
144        public int intValue() {
145                return (int)doubleValue();
146        }
147
148        @Override
149        public long longValue() {
150                return (long)doubleValue();
151        }
152
153        @Override
154        public float floatValue() {
155                return (float)doubleValue();
156        }
157
158        @Override
159        public int compareTo(final Speed speed) {
160                return Double.compare(_value, speed._value);
161        }
162
163        @Override
164        public int hashCode() {
165                return Double.hashCode(_value);
166        }
167
168        @Override
169        public boolean equals(final Object obj) {
170                return obj == this ||
171                        obj instanceof Speed speed &&
172                        Double.compare(speed._value, _value) == 0;
173        }
174
175        @Override
176        public String toString() {
177                return format("%s m/s", _value);
178        }
179
180
181        /* *************************************************************************
182         *  Static object creation methods
183         * ************************************************************************/
184
185        /**
186         * Create a new GPS {@code Speed} object.
187         *
188         * @param speed the GPS speed value
189         * @param unit the speed unit
190         * @return a new GPS {@code Speed} object
191         * @throws NullPointerException if the given speed {@code unit} is
192         *         {@code null}
193         */
194        public static Speed of(final double speed, final Unit unit) {
195                requireNonNull(unit);
196                return new Speed(Unit.METERS_PER_SECOND.convert(speed, unit));
197        }
198
199        static Speed parse(final String value) {
200                final String speed = Strings.trim(value);
201                return speed != null
202                        ? Speed.of(Double.parseDouble(speed), Unit.METERS_PER_SECOND)
203                        : null;
204        }
205
206        /* *************************************************************************
207         *  Java object serialization
208         * ************************************************************************/
209
210        @Serial
211        private Object writeReplace() {
212                return new SerialProxy(SerialProxy.SPEED, this);
213        }
214
215        @Serial
216        private void readObject(final ObjectInputStream stream)
217                throws InvalidObjectException
218        {
219                throw new InvalidObjectException("Serialization proxy required.");
220        }
221
222        void write(final DataOutput out) throws IOException {
223                out.writeDouble(_value);
224        }
225
226        static Speed read(final DataInput in) throws IOException {
227                return new Speed(in.readDouble());
228        }
229
230}