All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.mitre.caasd.commons.Position Maven / Gradle / Ivy

/*
 *    Copyright 2022 The MITRE Corporation
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package org.mitre.caasd.commons;

import static com.google.common.base.Preconditions.checkState;
import static java.util.Objects.*;
import static org.mitre.caasd.commons.LatLong.checkLatitude;
import static org.mitre.caasd.commons.LatLong.checkLongitude;

import java.time.Instant;
import java.util.Optional;

/**
 * Position gives the 3d or 4d location of an object. The time, latitude, and longitude dimensions
 * are strictly required but the altitude dimension is optional.
 * 

* The goals of this class are to (A) have a useful Java API and (B) have a compact serialized form * that will be easy to work with when working with other languages that cannot use the Java API */ public class Position implements HasTime, HasPosition { private final long epochTime; // latitude kept independently for serialized form (i.e. not nested inside a LatLong instance) private final double latitude; // longitude kept independently for serialized form (i.e. not nested inside a LatLong instance) private final double longitude; private final double altitudeInFeet; private final boolean altitudeIsValid; /** * Creates a Position without an altitude component * * @param time The time * @param location The LatLong */ public Position(Instant time, LatLong location) { this(time, location, null); } /** * Creates a Position with or without an altitude component * * @param time The time * @param location The LatLong * @param altitude An Optional (i.e. nullable) altitude */ public Position(Instant time, LatLong location, Distance altitude) { this( time.toEpochMilli(), location.latitude(), location.longitude(), nonNull(altitude) ? altitude.inFeet() : null); } /** * Creates a Position without an altitude component * * @param epochTimeInMilli The epoch time in milliseconds * @param latitude The latitude * @param longitude The longitude */ public Position(long epochTimeInMilli, double latitude, double longitude) { this(epochTimeInMilli, latitude, longitude, null); } /** * Creates a Position with or without an altitude component * * @param epochTimeInMilli The epoch time in milliseconds * @param latitude The latitude * @param longitude The longitude * @param altInFeet An Optional (i.e. nullable) altitude value (in feet) */ public Position(long epochTimeInMilli, double latitude, double longitude, Double altInFeet) { /* * Note: Using the nullable "Double altInFeet" parameter is ugly. But it enables all * constructors to funnel to a single "master constructor". If "double altInFeet" was used * instead the method signature would not support null altitudes. */ checkLatitude(latitude); checkLongitude(longitude); this.epochTime = epochTimeInMilli; this.latitude = latitude; this.longitude = longitude; this.altitudeInFeet = nonNull(altInFeet) ? altInFeet : 0.0; this.altitudeIsValid = nonNull(altInFeet); } private Position() { /* * This constructor supports Avro's reflection-based object instantiation. This constructor * is private to prevent standard API users from seeing it. * * Note, tools that use reflection (e.g. Avro) are the only users who will benefit from this * constructor. Those tools use reflection magic to build the object, then they use more * reflection magic to mutate the values inside the "Immutable object". */ this(0L, 0.0, 0.0, null); } @Override public Instant time() { return Instant.ofEpochMilli(epochTime); } @Override public LatLong latLong() { return LatLong.of(latitude, longitude); } /** @return The altitude of this Position (which may be null). */ public Distance altitude() { /* For API simplicity do not return an "Optional" */ return (altitudeIsValid) ? Distance.ofFeet(altitudeInFeet) : null; } /** @return Equivalent to: {@code Optional.ofNullable(this.altitude());} */ public Optional altitudeAsOpt() { return Optional.ofNullable(altitude()); } public boolean hasAltitude() { return altitudeIsValid; } @Override public int hashCode() { int hash = 3; hash = 23 * hash + (int) (this.epochTime ^ (this.epochTime >>> 32)); hash = 23 * hash + (int) (Double.doubleToLongBits(this.latitude) ^ (Double.doubleToLongBits(this.latitude) >>> 32)); hash = 23 * hash + (int) (Double.doubleToLongBits(this.longitude) ^ (Double.doubleToLongBits(this.longitude) >>> 32)); hash = 23 * hash + (int) (Double.doubleToLongBits(this.altitudeInFeet) ^ (Double.doubleToLongBits(this.altitudeInFeet) >>> 32)); return hash; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final Position other = (Position) obj; if (this.epochTime != other.epochTime) { return false; } if (Double.doubleToLongBits(this.latitude) != Double.doubleToLongBits(other.latitude)) { return false; } if (Double.doubleToLongBits(this.longitude) != Double.doubleToLongBits(other.longitude)) { return false; } if (Double.doubleToLongBits(this.altitudeInFeet) != Double.doubleToLongBits(other.altitudeInFeet)) { return false; } return true; } public static Builder builder() { return new Builder(); } public static Builder builder(Position seed) { return builder().time(seed.time()).latLong(seed.latLong()).altitude(seed.altitude()); } public static class Builder { private Long epochTime; private Double latitude; private Double longitude; private Double altitudeInFeet; public Position build() { requireNonNull(epochTime); requireNonNull(latitude); requireNonNull(longitude); return new Position(epochTime, latitude, longitude, altitudeInFeet); } /** Set the time while enforcing that it was not set previously. */ public Builder time(Instant time) { checkState(isNull(epochTime), "time was already set"); return butTime(time); } /** Set the time regardless of whether it was set previously. */ public Builder butTime(Instant time) { this.epochTime = time.toEpochMilli(); return this; } /** Set the LatLong while enforcing that it was not set previously. */ public Builder latLong(LatLong location) { checkState(isNull(latitude), "latitude was already set"); checkState(isNull(longitude), "longitude was already set"); return butLatLong(location); } /** Set the LatLong regardless of whether it was set previously. */ public Builder butLatLong(LatLong location) { requireNonNull(location); return butLatLong(location.latitude(), location.longitude()); } /** Set the LatLong while enforcing that it was not set previously. */ public Builder latLong(double lat, double lon) { checkState(isNull(latitude), "latitude was already set"); checkState(isNull(longitude), "longitude was already set"); return butLatLong(lat, lon); } /** Set the LatLong regardless of whether it was set previously. */ public Builder butLatLong(double latitude, double longitude) { checkLatitude(latitude); checkLongitude(longitude); this.latitude = latitude; this.longitude = longitude; return this; } /** Set the altitude while enforcing that it was not set previously. */ public Builder altitude(Distance altitude) { checkState(isNull(altitudeInFeet), "altitude was already set"); return butAltitude(altitude); } /** Set the altitude regardless of whether it was set previously. */ public Builder butAltitude(Distance altitude) { this.altitudeInFeet = isNull(altitude) ? null : altitude.inFeet(); return this; } /** Set the altitude while enforcing that it was not set previously. */ public Builder altitudeInFeet(double altitudeInFeet) { checkState(isNull(this.altitudeInFeet), "altitude was already set"); return butAltitudeInFeet(altitudeInFeet); } /** Set the altitude regardless of whether it was set previously. */ public Builder butAltitudeInFeet(double altitudeInFeet) { this.altitudeInFeet = altitudeInFeet; return this; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy