/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.apache.lucene.spatial3d.geom;

import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;

This class represents a point on the surface of a sphere or ellipsoid.
@lucene.experimental
/** * This class represents a point on the surface of a sphere or ellipsoid. * * @lucene.experimental */
public class GeoPoint extends Vector implements SerializableObject { // By making lazily-evaluated variables be "volatile", we guarantee atomicity when they // are updated. This is necessary if we are using these classes in a multi-thread fashion, // because we don't try to synchronize for the lazy computation.
This is the lazily-evaluated magnitude. Some constructors include it, but others don't, and we try not to create extra computation by always computing it. Does not need to be synchronized for thread safety, because depends wholly on immutable variables of this class.
/** This is the lazily-evaluated magnitude. Some constructors include it, but others don't, and * we try not to create extra computation by always computing it. Does not need to be * synchronized for thread safety, because depends wholly on immutable variables of this class. */
protected volatile double magnitude = Double.NEGATIVE_INFINITY;
Lazily-evaluated latitude. Does not need to be synchronized for thread safety, because depends wholly on immutable variables of this class.
/** Lazily-evaluated latitude. Does not need to be * synchronized for thread safety, because depends wholly on immutable variables of this class. */
protected volatile double latitude = Double.NEGATIVE_INFINITY;
Lazily-evaluated longitude. Does not need to be synchronized for thread safety, because depends wholly on immutable variables of this class.
/** Lazily-evaluated longitude. Does not need to be * synchronized for thread safety, because depends wholly on immutable variables of this class. */
protected volatile double longitude = Double.NEGATIVE_INFINITY;
Construct a GeoPoint from the trig functions of a lat and lon pair.
Params:
  • planetModel – is the planetModel to put the point on.
  • sinLat – is the sin of the latitude.
  • sinLon – is the sin of the longitude.
  • cosLat – is the cos of the latitude.
  • cosLon – is the cos of the longitude.
  • lat – is the latitude.
  • lon – is the longitude.
/** Construct a GeoPoint from the trig functions of a lat and lon pair. * @param planetModel is the planetModel to put the point on. * @param sinLat is the sin of the latitude. * @param sinLon is the sin of the longitude. * @param cosLat is the cos of the latitude. * @param cosLon is the cos of the longitude. * @param lat is the latitude. * @param lon is the longitude. */
public GeoPoint(final PlanetModel planetModel, final double sinLat, final double sinLon, final double cosLat, final double cosLon, final double lat, final double lon) { this(computeDesiredEllipsoidMagnitude(planetModel, cosLat * cosLon, cosLat * sinLon, sinLat), cosLat * cosLon, cosLat * sinLon, sinLat, lat, lon); }
Construct a GeoPoint from the trig functions of a lat and lon pair.
Params:
  • planetModel – is the planetModel to put the point on.
  • sinLat – is the sin of the latitude.
  • sinLon – is the sin of the longitude.
  • cosLat – is the cos of the latitude.
  • cosLon – is the cos of the longitude.
/** Construct a GeoPoint from the trig functions of a lat and lon pair. * @param planetModel is the planetModel to put the point on. * @param sinLat is the sin of the latitude. * @param sinLon is the sin of the longitude. * @param cosLat is the cos of the latitude. * @param cosLon is the cos of the longitude. */
public GeoPoint(final PlanetModel planetModel, final double sinLat, final double sinLon, final double cosLat, final double cosLon) { this(computeDesiredEllipsoidMagnitude(planetModel, cosLat * cosLon, cosLat * sinLon, sinLat), cosLat * cosLon, cosLat * sinLon, sinLat); }
Construct a GeoPoint from a latitude/longitude pair.
Params:
  • planetModel – is the planetModel to put the point on.
  • lat – is the latitude.
  • lon – is the longitude.
/** Construct a GeoPoint from a latitude/longitude pair. * @param planetModel is the planetModel to put the point on. * @param lat is the latitude. * @param lon is the longitude. */
public GeoPoint(final PlanetModel planetModel, final double lat, final double lon) { this(planetModel, Math.sin(lat), Math.sin(lon), Math.cos(lat), Math.cos(lon), lat, lon); }
Construct a GeoPoint from an input stream.
Params:
  • planetModel – is the planet model
  • inputStream – is the input stream
/** Construct a GeoPoint from an input stream. * @param planetModel is the planet model * @param inputStream is the input stream */
public GeoPoint(final PlanetModel planetModel, final InputStream inputStream) throws IOException { this(inputStream); }
Construct a GeoPoint from an input stream with no planet model.
Params:
  • inputStream – is the input stream
/** Construct a GeoPoint from an input stream with no planet model. * @param inputStream is the input stream */
public GeoPoint(final InputStream inputStream) throws IOException { // Note: this relies on left-right parameter execution order!! Much code depends on that though and // it is apparently in a java spec: https://stackoverflow.com/questions/2201688/order-of-execution-of-parameters-guarantees-in-java this(SerializableObject.readDouble(inputStream), SerializableObject.readDouble(inputStream), SerializableObject.readDouble(inputStream), SerializableObject.readDouble(inputStream), SerializableObject.readDouble(inputStream)); }
Construct a GeoPoint from five unchecked parameters: lat, lon, x, y, z. This is primarily used for deserialization, but can also be used to fully initialize a point externally.
Params:
  • lat – is the latitude in radians
  • lon – is the longitude in radians
  • x – is the unit x value
  • y – is the unit y value
  • z – is the unit z value
/** Construct a GeoPoint from five unchecked parameters: lat, lon, x, y, z. This is primarily used for deserialization, * but can also be used to fully initialize a point externally. * @param lat is the latitude in radians * @param lon is the longitude in radians * @param x is the unit x value * @param y is the unit y value * @param z is the unit z value */
public GeoPoint(final double lat, final double lon, final double x, final double y, final double z) { super(x, y, z); this.latitude = lat; this.longitude = lon; }
Construct a GeoPoint from a unit (x,y,z) vector and a magnitude.
Params:
  • magnitude – is the desired magnitude, provided to put the point on the ellipsoid.
  • x – is the unit x value.
  • y – is the unit y value.
  • z – is the unit z value.
  • lat – is the latitude.
  • lon – is the longitude.
/** Construct a GeoPoint from a unit (x,y,z) vector and a magnitude. * @param magnitude is the desired magnitude, provided to put the point on the ellipsoid. * @param x is the unit x value. * @param y is the unit y value. * @param z is the unit z value. * @param lat is the latitude. * @param lon is the longitude. */
public GeoPoint(final double magnitude, final double x, final double y, final double z, double lat, double lon) { super(x * magnitude, y * magnitude, z * magnitude); this.magnitude = magnitude; if (lat > Math.PI * 0.5 || lat < -Math.PI * 0.5) { throw new IllegalArgumentException("Latitude " + lat + " is out of range: must range from -Math.PI/2 to Math.PI/2"); } if (lon < -Math.PI || lon > Math.PI) { throw new IllegalArgumentException("Longitude " + lon + " is out of range: must range from -Math.PI to Math.PI"); } this.latitude = lat; this.longitude = lon; }
Construct a GeoPoint from a unit (x,y,z) vector and a magnitude.
Params:
  • magnitude – is the desired magnitude, provided to put the point on the ellipsoid.
  • x – is the unit x value.
  • y – is the unit y value.
  • z – is the unit z value.
/** Construct a GeoPoint from a unit (x,y,z) vector and a magnitude. * @param magnitude is the desired magnitude, provided to put the point on the ellipsoid. * @param x is the unit x value. * @param y is the unit y value. * @param z is the unit z value. */
public GeoPoint(final double magnitude, final double x, final double y, final double z) { super(x * magnitude, y * magnitude, z * magnitude); this.magnitude = magnitude; }
Construct a GeoPoint from an (x,y,z) value. The (x,y,z) tuple must be on the desired ellipsoid.
Params:
  • x – is the ellipsoid point x value.
  • y – is the ellipsoid point y value.
  • z – is the ellipsoid point z value.
/** Construct a GeoPoint from an (x,y,z) value. * The (x,y,z) tuple must be on the desired ellipsoid. * @param x is the ellipsoid point x value. * @param y is the ellipsoid point y value. * @param z is the ellipsoid point z value. */
public GeoPoint(final double x, final double y, final double z) { super(x, y, z); } @Override public void write(final OutputStream outputStream) throws IOException { SerializableObject.writeDouble(outputStream, getLatitude()); SerializableObject.writeDouble(outputStream, getLongitude()); SerializableObject.writeDouble(outputStream, x); SerializableObject.writeDouble(outputStream, y); SerializableObject.writeDouble(outputStream, z); }
Compute an arc distance between two points. Note: this is an angular distance, and not a surface distance, and is therefore independent of planet model. For surface distance, see PlanetModel.surfaceDistance(GeoPoint, GeoPoint)
Params:
  • v – is the second point.
Returns:the angle, in radians, between the two points.
/** Compute an arc distance between two points. * Note: this is an angular distance, and not a surface distance, and is therefore independent of planet model. * For surface distance, see {@link PlanetModel#surfaceDistance(GeoPoint, GeoPoint)} * @param v is the second point. * @return the angle, in radians, between the two points. */
public double arcDistance(final Vector v) { return Tools.safeAcos(dotProduct(v)/(magnitude() * v.magnitude())); }
Compute an arc distance between two points.
Params:
  • x – is the x part of the second point.
  • y – is the y part of the second point.
  • z – is the z part of the second point.
Returns:the angle, in radians, between the two points.
/** Compute an arc distance between two points. * @param x is the x part of the second point. * @param y is the y part of the second point. * @param z is the z part of the second point. * @return the angle, in radians, between the two points. */
public double arcDistance(final double x, final double y, final double z) { return Tools.safeAcos(dotProduct(x,y,z)/(magnitude() * Vector.magnitude(x,y,z))); }
Compute the latitude for the point.
Returns:the latitude.
/** Compute the latitude for the point. * @return the latitude. */
public double getLatitude() { double lat = this.latitude;//volatile-read once if (lat == Double.NEGATIVE_INFINITY) this.latitude = lat = Math.asin(z / magnitude()); return lat; }
Compute the longitude for the point.
Returns:the longitude value. Uses 0.0 if there is no computable longitude.
/** Compute the longitude for the point. * @return the longitude value. Uses 0.0 if there is no computable longitude. */
public double getLongitude() { double lon = this.longitude;//volatile-read once if (lon == Double.NEGATIVE_INFINITY) { if (Math.abs(x) < MINIMUM_RESOLUTION && Math.abs(y) < MINIMUM_RESOLUTION) this.longitude = lon = 0.0; else this.longitude = lon = Math.atan2(y,x); } return lon; }
Compute the linear magnitude of the point.
Returns:the magnitude.
/** Compute the linear magnitude of the point. * @return the magnitude. */
@Override public double magnitude() { double mag = this.magnitude;//volatile-read once if (mag == Double.NEGATIVE_INFINITY) { this.magnitude = mag = super.magnitude(); } return mag; }
Compute whether point matches another.
Params:
  • p – is the other point.
Returns:true if the same.
/** Compute whether point matches another. *@param p is the other point. *@return true if the same. */
public boolean isIdentical(final GeoPoint p) { return isIdentical(p.x, p.y, p.z); }
Compute whether point matches another.
Params:
  • x – is the x value
  • y – is the y value
  • z – is the z value
Returns:true if the same.
/** Compute whether point matches another. *@param x is the x value *@param y is the y value *@param z is the z value *@return true if the same. */
public boolean isIdentical(final double x, final double y, final double z) { return Math.abs(this.x - x) < MINIMUM_RESOLUTION && Math.abs(this.y - y) < MINIMUM_RESOLUTION && Math.abs(this.z - z) < MINIMUM_RESOLUTION; } @Override public String toString() { if (this.longitude == Double.NEGATIVE_INFINITY) { return super.toString(); } return "[lat="+getLatitude()+", lon="+getLongitude()+"("+super.toString()+")]"; } }