/*
 * 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;

Circular area with a center and cutoff angle that represents the latitude and longitude distance from the center where the planet will be cut. The resulting area is a circle for spherical planets and an ellipse otherwise.
@lucene.experimental
/** * Circular area with a center and cutoff angle that represents the latitude and longitude distance * from the center where the planet will be cut. The resulting area is a circle for spherical * planets and an ellipse otherwise. * * @lucene.experimental */
class GeoStandardCircle extends GeoBaseCircle {
Center of circle
/** Center of circle */
protected final GeoPoint center;
Cutoff angle of circle (not quite the same thing as radius)
/** Cutoff angle of circle (not quite the same thing as radius) */
protected final double cutoffAngle;
The plane describing the circle (really an ellipse on a non-spherical world)
/** The plane describing the circle (really an ellipse on a non-spherical world) */
protected final SidedPlane circlePlane;
A point that is on the world and on the circle plane
/** A point that is on the world and on the circle plane */
protected final GeoPoint[] edgePoints;
Notable points for a circle -- there aren't any
/** Notable points for a circle -- there aren't any */
protected static final GeoPoint[] circlePoints = new GeoPoint[0];
Constructor.
Params:
  • planetModel – is the planet model.
  • lat – is the center latitude.
  • lon – is the center longitude.
  • cutoffAngle – is the cutoff angle for the circle.
/** Constructor. *@param planetModel is the planet model. *@param lat is the center latitude. *@param lon is the center longitude. *@param cutoffAngle is the cutoff angle for the circle. */
public GeoStandardCircle(final PlanetModel planetModel, final double lat, final double lon, final double cutoffAngle) { super(planetModel); if (lat < -Math.PI * 0.5 || lat > Math.PI * 0.5) throw new IllegalArgumentException("Latitude out of bounds"); if (lon < -Math.PI || lon > Math.PI) throw new IllegalArgumentException("Longitude out of bounds"); if (cutoffAngle < 0.0 || cutoffAngle > Math.PI) throw new IllegalArgumentException("Cutoff angle out of bounds"); if (cutoffAngle < Vector.MINIMUM_RESOLUTION) throw new IllegalArgumentException("Cutoff angle cannot be effectively zero"); this.center = new GeoPoint(planetModel, lat, lon); // In an ellipsoidal world, cutoff distances make no sense, unfortunately. Only membership // can be used to make in/out determination. this.cutoffAngle = cutoffAngle; // Compute two points on the circle, with the right angle from the center. We'll use these // to obtain the perpendicular plane to the circle. double upperLat = lat + cutoffAngle; double upperLon = lon; if (upperLat > Math.PI * 0.5) { upperLon += Math.PI; if (upperLon > Math.PI) upperLon -= 2.0 * Math.PI; upperLat = Math.PI - upperLat; } double lowerLat = lat - cutoffAngle; double lowerLon = lon; if (lowerLat < -Math.PI * 0.5) { lowerLon += Math.PI; if (lowerLon > Math.PI) lowerLon -= 2.0 * Math.PI; lowerLat = -Math.PI - lowerLat; } final GeoPoint upperPoint = new GeoPoint(planetModel, upperLat, upperLon); final GeoPoint lowerPoint = new GeoPoint(planetModel, lowerLat, lowerLon); if (Math.abs(cutoffAngle - Math.PI) < Vector.MINIMUM_RESOLUTION) { // Circle is the whole world this.circlePlane = null; this.edgePoints = new GeoPoint[0]; } else { // Construct normal plane final Plane normalPlane = Plane.constructNormalizedZPlane(upperPoint, lowerPoint, center); // Construct a sided plane that goes through the two points and whose normal is in the normalPlane. this.circlePlane = SidedPlane.constructNormalizedPerpendicularSidedPlane(center, normalPlane, upperPoint, lowerPoint); if (circlePlane == null) throw new IllegalArgumentException("Couldn't construct circle plane, probably too small? Cutoff angle = "+cutoffAngle+"; upperPoint = "+upperPoint+"; lowerPoint = "+lowerPoint); final GeoPoint recomputedIntersectionPoint = circlePlane.getSampleIntersectionPoint(planetModel, normalPlane); if (recomputedIntersectionPoint == null) throw new IllegalArgumentException("Couldn't construct intersection point, probably circle too small? Plane = "+circlePlane); this.edgePoints = new GeoPoint[]{recomputedIntersectionPoint}; } }
Constructor for deserialization.
Params:
  • planetModel – is the planet model.
  • inputStream – is the input stream.
/** * Constructor for deserialization. * @param planetModel is the planet model. * @param inputStream is the input stream. */
public GeoStandardCircle(final PlanetModel planetModel, final InputStream inputStream) throws IOException { this(planetModel, SerializableObject.readDouble(inputStream), SerializableObject.readDouble(inputStream), SerializableObject.readDouble(inputStream)); } @Override public void write(final OutputStream outputStream) throws IOException { SerializableObject.writeDouble(outputStream, center.getLatitude()); SerializableObject.writeDouble(outputStream, center.getLongitude()); SerializableObject.writeDouble(outputStream, cutoffAngle); } @Override public double getRadius() { return cutoffAngle; } @Override public GeoPoint getCenter() { return center; } @Override protected double distance(final DistanceStyle distanceStyle, final double x, final double y, final double z) { return distanceStyle.computeDistance(this.center, x, y, z); } @Override protected void distanceBounds(final Bounds bounds, final DistanceStyle distanceStyle, final double distanceValue) { // TBD: Compute actual bounds based on distance getBounds(bounds); } @Override protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) { return distanceStyle.computeDistance(planetModel, circlePlane, x, y, z); } @Override public boolean isWithin(final double x, final double y, final double z) { if (circlePlane == null) { return true; } // Fastest way of determining membership return circlePlane.isWithin(x, y, z); } @Override public GeoPoint[] getEdgePoints() { return edgePoints; } @Override public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) { if (circlePlane == null) { return false; } return circlePlane.intersects(planetModel, p, notablePoints, circlePoints, bounds); } @Override public boolean intersects(GeoShape geoShape) { if (circlePlane == null) { return false; } return geoShape.intersects(circlePlane, circlePoints); } @Override public int getRelationship(GeoShape geoShape) { if (circlePlane == null) { //same as GeoWorld if (geoShape.getEdgePoints().length > 0) { return WITHIN; } return OVERLAPS; } return super.getRelationship(geoShape); } @Override public void getBounds(Bounds bounds) { super.getBounds(bounds); if (circlePlane == null) { // Entire world; should already be covered return; } bounds.addPoint(center); bounds.addPlane(planetModel, circlePlane); } @Override public boolean equals(Object o) { if (!(o instanceof GeoStandardCircle)) return false; GeoStandardCircle other = (GeoStandardCircle) o; return super.equals(other) && other.center.equals(center) && other.cutoffAngle == cutoffAngle; } @Override public int hashCode() { int result = super.hashCode(); result = 31 * result + center.hashCode(); long temp = Double.doubleToLongBits(cutoffAngle); result = 31 * result + (int) (temp ^ (temp >>> 32)); return result; } @Override public String toString() { return "GeoStandardCircle: {planetmodel=" + planetModel+", center=" + center + ", radius=" + cutoffAngle + "(" + cutoffAngle * 180.0 / Math.PI + ")}"; } }