diff --git a/bundles/org.openhab.core.compat1x/src/main/java/org/openhab/core/library/types/PointType.java b/bundles/org.openhab.core.compat1x/src/main/java/org/openhab/core/library/types/PointType.java index d32d573964..20828247d6 100644 --- a/bundles/org.openhab.core.compat1x/src/main/java/org/openhab/core/library/types/PointType.java +++ b/bundles/org.openhab.core.compat1x/src/main/java/org/openhab/core/library/types/PointType.java @@ -13,6 +13,7 @@ import java.util.Formatter; import java.util.SortedMap; import java.util.TreeMap; +import org.apache.commons.lang.StringUtils; import org.openhab.core.types.Command; import org.openhab.core.types.ComplexType; import org.openhab.core.types.PrimitiveType; @@ -26,142 +27,213 @@ import org.openhab.core.types.State; */ public class PointType implements ComplexType, Command, State { - public static final double EARTH_GRAVITATIONAL_CONSTANT = 3.986004418e14; - public static final double WGS84_a = 6378137; // The equatorial radius of - // WGS84 ellipsoid (6378137 - // m). - private BigDecimal latitude; // in decimal degrees - private BigDecimal longitude; // in decimal degrees - private BigDecimal altitude = BigDecimal.ZERO; // in decimal meters - // constants for the constituents - static final public String KEY_LATITUDE = "lat"; - static final public String KEY_LONGITUDE = "long"; - static final public String KEY_ALTITUDE = "alt"; - private static final BigDecimal circle = new BigDecimal(360); - private static final BigDecimal flat = new BigDecimal(180); - private static final BigDecimal right = new BigDecimal(90); - public static final PointType EMPTY = new PointType(new DecimalType(0), - new DecimalType(0)); + public static final double EARTH_GRAVITATIONAL_CONSTANT = 3.986004418e14; + public static final double WGS84_a = 6378137; // The equatorial radius of + // WGS84 ellipsoid (6378137 + // m). + private BigDecimal latitude = BigDecimal.ZERO; // in decimal degrees + private BigDecimal longitude = BigDecimal.ZERO; // in decimal degrees + private BigDecimal altitude = BigDecimal.ZERO; // in decimal meters + // constants for the constituents + static final public String KEY_LATITUDE = "lat"; + static final public String KEY_LONGITUDE = "long"; + static final public String KEY_ALTITUDE = "alt"; + private static final BigDecimal circle = new BigDecimal(360); + private static final BigDecimal flat = new BigDecimal(180); + private static final BigDecimal right = new BigDecimal(90); + public static final PointType EMPTY = new PointType(new DecimalType(0), new DecimalType(0)); - public PointType(DecimalType latitude, DecimalType longitude) { - canonicalize(latitude, longitude); - } + /** + * Default constructor creates a point at sea level where the equator + * (0° latitude) and the prime meridian (0° longitude) intersect. (A + * nullary constructor is needed by + * {@link org.openhab.core.internal.items.ItemUpdater#receiveUpdate}) + */ + @SuppressWarnings("restriction") + public PointType() { + } - public PointType(DecimalType latitude, DecimalType longitude, - DecimalType altitude) { - this(latitude, longitude); - setAltitude(altitude); - } + public PointType(DecimalType latitude, DecimalType longitude) { + canonicalize(latitude, longitude); + } - public PointType(StringType latitude, StringType longitude) { - this(new DecimalType(latitude.toString()), new DecimalType( - longitude.toString())); - } + public PointType(DecimalType latitude, DecimalType longitude, DecimalType altitude) { + this(latitude, longitude); + setAltitude(altitude); + } - public PointType(StringType latitude, StringType longitude, - StringType altitude) { - this(new DecimalType(latitude.toString()), new DecimalType( - longitude.toString()), new DecimalType(altitude.toString())); - } + public PointType(StringType latitude, StringType longitude) { + this(new DecimalType(latitude.toString()), new DecimalType(longitude.toString())); + } - public PointType(String value) { - if (!value.isEmpty()) { - String[] elements = value.split(","); - if (elements.length >= 2) { - canonicalize(new DecimalType(elements[0]), new DecimalType( - elements[1])); - if (elements.length == 3) { - setAltitude(new DecimalType(elements[2])); - } - } - } - } + public PointType(StringType latitude, StringType longitude, StringType altitude) { + this(new DecimalType(latitude.toString()), new DecimalType(longitude.toString()), + new DecimalType(altitude.toString())); + } - public DecimalType getLatitude() { - return new DecimalType(latitude); - } + public PointType(String value) { + if (StringUtils.isNotBlank(value)) { + String[] elements = value.split(","); + if (elements.length >= 2) { + canonicalize(new DecimalType(elements[0]), new DecimalType(elements[1])); + if (elements.length == 3) { + setAltitude(new DecimalType(elements[2])); + } + } else { + throw new IllegalArgumentException(value + " is not a valid PointType syntax"); + } + } else { + throw new IllegalArgumentException("Constructor argument must not be blank"); + } + } - public DecimalType getLongitude() { - return new DecimalType(longitude); - } + public DecimalType getLatitude() { + return new DecimalType(latitude); + } - public DecimalType getAltitude() { - return new DecimalType(altitude); - } + public DecimalType getLongitude() { + return new DecimalType(longitude); + } - public void setAltitude(DecimalType altitude) { - this.altitude = altitude.toBigDecimal(); - } + public DecimalType getAltitude() { + return new DecimalType(altitude); + } - public DecimalType getGravity() { - double latRad = Math.toRadians(latitude.doubleValue()); - double deltaG = -2000.0 * (altitude.doubleValue() / 1000) - * EARTH_GRAVITATIONAL_CONSTANT / (Math.pow(WGS84_a, 3.0)); - double sin2lat = Math.sin(latRad) * Math.sin(latRad); - double sin22lat = Math.sin(2.0 * latRad) * Math.sin(2.0 * latRad); - double result = (9.780327 * (1.0 + 5.3024e-3 * sin2lat - 5.8e-6 * sin22lat) + deltaG); - return new DecimalType(result); - } + public void setAltitude(DecimalType altitude) { + this.altitude = altitude.toBigDecimal(); + } - /** - *
- * Formats the value of this type according to a pattern (@see - * {@link Formatter}). One single value of this type can be referenced by - * the pattern using an index. The item order is defined by the natural - * (alphabetical) order of their keys. - *
- * - * @param pattern - * the pattern to use containing indexes to reference the single - * elements of this type. - */ - @Override - public String format(String pattern) { - return String.format(pattern, getConstituents().values().toArray()); - } + public DecimalType getGravity() { + double latRad = Math.toRadians(latitude.doubleValue()); + double deltaG = -2000.0 * (altitude.doubleValue() / 1000) * EARTH_GRAVITATIONAL_CONSTANT + / (Math.pow(WGS84_a, 3.0)); + double sin2lat = Math.sin(latRad) * Math.sin(latRad); + double sin22lat = Math.sin(2.0 * latRad) * Math.sin(2.0 * latRad); + double result = (9.780327 * (1.0 + 5.3024e-3 * sin2lat - 5.8e-6 * sin22lat) + deltaG); + return new DecimalType(result); + } - public PointType valueOf(String value) { - return new PointType(value); - } + /** + * Return the distance in meters from otherPoint, ignoring altitude. This algorithm also + * ignores the oblate spheroid shape of Earth and assumes a perfect sphere, so results + * are inexact. + * + * @param otherPoint + * @return distance in meters + * @see Haversine formula + */ + public DecimalType distanceFrom(PointType otherPoint) { + double dLat = Math.toRadians(otherPoint.latitude.doubleValue() - this.latitude.doubleValue()); + double dLong = Math.toRadians(otherPoint.longitude.doubleValue() - this.longitude.doubleValue()); + double a = Math.pow(Math.sin(dLat / 2D), 2D) + Math.cos(Math.toRadians(this.latitude.doubleValue())) + * Math.cos(Math.toRadians(otherPoint.latitude.doubleValue())) * Math.pow(Math.sin(dLong / 2D), 2D); + double c = 2D * Math.atan2(Math.sqrt(a), Math.sqrt(1D - a)); + return new DecimalType(WGS84_a * c); + } - @Override - public String toString() { - return String.format("%1$.2f°N, %2$.2f°W, %3$.2f m", latitude, longitude, altitude); - } + /** + *+ * Formats the value of this type according to a pattern (@see + * {@link Formatter}). One single value of this type can be referenced by + * the pattern using an index. The item order is defined by the natural + * (alphabetical) order of their keys. + *
+ * + * @param pattern + * the pattern to use containing indexes to reference the single + * elements of this type. + */ + @Override + public String format(String pattern) { + return String.format(pattern, getConstituents().values().toArray()); + } - @Override - public SortedMap- * -90 <= latitude <= +90 - 180 < longitude <= +180 - *- */ - private void canonicalize(DecimalType aLat, DecimalType aLon) { - latitude = flat.add(aLat.toBigDecimal()).remainder(circle); - longitude = aLon.toBigDecimal(); - if (latitude.compareTo(BigDecimal.ZERO) == -1) - latitude.add(circle); - latitude = latitude.subtract(flat); - if (latitude.compareTo(right) == 1) { - latitude = flat.subtract(latitude); - longitude = longitude.add(flat); - } else if (latitude.compareTo(right.negate()) == -1) { - latitude = flat.negate().subtract(latitude); - longitude = longitude.add(flat); - } - longitude = flat.add(longitude).remainder(circle); - if (longitude.compareTo(BigDecimal.ZERO) <= 0) - longitude = longitude.add(circle); - longitude = longitude.subtract(flat); - } + @Override + public String toString() { + StringBuilder sb = new StringBuilder(latitude.toPlainString()); + sb.append(','); + sb.append(longitude.toPlainString()); + if (!altitude.equals(BigDecimal.ZERO)) { + sb.append(','); + sb.append(altitude.toPlainString()); + } + return sb.toString(); + } -} + @Override + public SortedMap
+ * -90 <= latitude <= +90 - 180 < longitude <= +180 + *+ */ + private void canonicalize(DecimalType aLat, DecimalType aLon) { + latitude = flat.add(aLat.toBigDecimal()).remainder(circle); + longitude = aLon.toBigDecimal(); + if (latitude.compareTo(BigDecimal.ZERO) == -1) { + latitude.add(circle); + } + latitude = latitude.subtract(flat); + if (latitude.compareTo(right) == 1) { + latitude = flat.subtract(latitude); + longitude = longitude.add(flat); + } else if (latitude.compareTo(right.negate()) == -1) { + latitude = flat.negate().subtract(latitude); + longitude = longitude.add(flat); + } + longitude = flat.add(longitude).remainder(circle); + if (longitude.compareTo(BigDecimal.ZERO) <= 0) { + longitude = longitude.add(circle); + } + longitude = longitude.subtract(flat); + } + + @Override + public int hashCode() { + int tmp = 10000 * (getLatitude() == null ? 0 : getLatitude().hashCode()); + tmp += 100 * (getLongitude() == null ? 0 : getLongitude().hashCode()); + tmp += (getAltitude() == null ? 0 : getAltitude().hashCode()); + return tmp; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof PointType)) { + return false; + } + PointType other = (PointType) obj; + if ((getLatitude() != null && other.getLatitude() == null) + || (getLatitude() == null && other.getLatitude() != null) + || (getLongitude() != null && other.getLongitude() == null) + || (getLongitude() == null && other.getLongitude() != null) + || (getAltitude() != null && other.getAltitude() == null) + || (getAltitude() == null && other.getAltitude() != null)) { + return false; + } + if (!getLatitude().equals(other.getLatitude()) || !getLongitude().equals(other.getLongitude()) + || !getAltitude().equals(other.getAltitude())) { + return false; + } + return true; + } + +} \ No newline at end of file