Index: LatLng.java =================================================================== --- LatLng.java (revision 930863) +++ LatLng.java (working copy) @@ -30,6 +30,18 @@ */ public abstract class LatLng { + public final static int LONGITUDE_DEGREE_RANGE = 360; + public final static int LONGITUDE_DEGREE_MIN = -LONGITUDE_DEGREE_RANGE / 2; + public final static int LONGITUDE_DEGREE_MAX = LONGITUDE_DEGREE_RANGE / 2; + public final static int LATITUDE_DEGREE_RANGE = 180; + public final static int LATITUDE_DEGREE_MIN = -LATITUDE_DEGREE_RANGE / 2; + public final static int LATITUDE_DEGREE_MAX = LATITUDE_DEGREE_RANGE / 2; + public final static int HEADING_NORTH= 0; + public final static int HEADING_SOUTH= 180; + public final static int HEADING_EAST= 90; + public final static int HEADING_WEST= 270; + + public abstract boolean isNormalized(); public abstract boolean isFixedPoint(); @@ -69,7 +81,48 @@ ); } + public static LatLng computeDestination(LatLng startPoint, double distance, + double heading) { + return computeDestination(startPoint, distance, heading, + DistanceUnits.MILES); + } + /** + * Utility method for calculating a target point given a start, a heading (in degrees), a distance and a distance unit + * @param startPoint + * beginning point of the path + * @param distance + * distance to be travel from the starting point + * @param heading + * heading in degrees to follow during the travel + * @param distanceUnit + * unit of the distance to to travel + * @return arrival point of the described travel + */ + public static LatLng computeDestination(LatLng startPoint, double distance, double heading, DistanceUnits distanceUnit) { + double startPointLatitude = startPoint.getLat(); + double startPointLongitude = startPoint.getLng(); + double earthRadius = distanceUnit.earthRadius(); + double headingRad = Math.toRadians(heading); + double startPointLatitudeRad = Math.toRadians(startPointLatitude); + double startPointLongitudeRad = Math.toRadians(startPointLongitude); + + // Haversine formula (http://www.movable-type.co.uk/scripts/latlong.html) + double destinationLatitudeRad = Math.asin(Math.sin(startPointLatitudeRad) + * Math.cos(distance / earthRadius) + Math.cos(startPointLatitudeRad) + * Math.sin(distance / earthRadius) * Math.cos(headingRad)); + + double destinationLongitudeRad = startPointLongitudeRad + + Math.atan2(Math.sin(headingRad) * Math.sin(distance / earthRadius) + * Math.cos(startPointLatitudeRad), Math.cos(distance / earthRadius) + - Math.sin(startPointLatitudeRad) + * Math.sin(destinationLatitudeRad)); + + return new FloatLatLng(Math.toDegrees(destinationLatitudeRad), Math + .toDegrees(destinationLongitudeRad)); + } + + /** * The inverse of toCartesian(). Always returns a FixedLatLng. * @param pt */ Index: shape/LLRect.java =================================================================== --- shape/LLRect.java (revision 930863) +++ shape/LLRect.java (working copy) @@ -17,6 +17,7 @@ package org.apache.lucene.spatial.geometry.shape; +import org.apache.lucene.spatial.geometry.DistanceUnits; import org.apache.lucene.spatial.geometry.FloatLatLng; import org.apache.lucene.spatial.geometry.LatLng; @@ -74,14 +75,34 @@ * @param heightMi */ public static LLRect createBox(LatLng center, double widthMi, double heightMi) { - double d = widthMi; - LatLng ur = boxCorners(center, d, 45.0); // assume right angles - LatLng ll = boxCorners(center, d, 225.0); + double minLat; + double maxLat; + double minLng; + double maxLng; + double radius= Math.max(widthMi, heightMi); + + if (radius > center.arcDistance(new FloatLatLng(LatLng.LATITUDE_DEGREE_MAX, LatLng.HEADING_NORTH))) { + maxLat = LatLng.LATITUDE_DEGREE_MAX; + } else { + maxLat = LatLng.computeDestination(center, radius, LatLng.HEADING_NORTH).getLat(); + + } + if (radius > center.arcDistance(new FloatLatLng(LatLng.LATITUDE_DEGREE_MIN, LatLng.HEADING_NORTH))) { + minLat = LatLng.LATITUDE_DEGREE_MIN; + } else { + minLat = LatLng.computeDestination(center, radius, LatLng.HEADING_SOUTH).getLat(); + } - //System.err.println("boxCorners: ur " + ur.getLat() + ',' + ur.getLng()); - //System.err.println("boxCorners: cnt " + center.getLat() + ',' + center.getLng()); - //System.err.println("boxCorners: ll " + ll.getLat() + ',' + ll.getLng()); - return new LLRect(ll, ur); + if((radius > 2 * Math.PI * DistanceUnits.MILES.earthRadius() * Math.cos(Math.toRadians(minLat))) || + (radius > 2 * Math.PI * DistanceUnits.MILES.earthRadius() * Math.cos(Math.toRadians(maxLat)))) { + maxLng = LatLng.LONGITUDE_DEGREE_MAX; + minLng = LatLng.LONGITUDE_DEGREE_MIN; + } else { + maxLng = LatLng.computeDestination(new FloatLatLng(Math.max(Math.abs(minLat), Math.abs(maxLat)), center.getLng()), radius, LatLng.HEADING_EAST).getLng(); + minLng = LatLng.computeDestination(new FloatLatLng(Math.max(Math.abs(minLat), Math.abs(maxLat)), center.getLng()), radius, LatLng.HEADING_WEST).getLng(); + } + + return new LLRect((new FloatLatLng(minLat, minLng).normalize()), (new FloatLatLng(maxLat, maxLng)).normalize()); } /**