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

src.gov.nasa.worldwind.geom.Sector Maven / Gradle / Ivy

Go to download

World Wind is a collection of components that interactively display 3D geographic information within Java applications or applets.

There is a newer version: 2.0.0-986
Show newest version
/*
 * Copyright (C) 2012 United States Government as represented by the Administrator of the
 * National Aeronautics and Space Administration.
 * All Rights Reserved.
 */
package gov.nasa.worldwind.geom;

import gov.nasa.worldwind.avlist.AVKey;
import gov.nasa.worldwind.cache.Cacheable;
import gov.nasa.worldwind.geom.coords.UTMCoord;
import gov.nasa.worldwind.globes.Globe;
import gov.nasa.worldwind.render.DrawContext;
import gov.nasa.worldwind.tracks.TrackPoint;
import gov.nasa.worldwind.util.Logging;

import java.awt.geom.*;
import java.util.*;

/**
 * Sector represents a rectangular region of latitude and longitude. The region is defined by four angles:
 * its minimum and maximum latitude, its minimum and maximum longitude. The angles are assumed to be normalized to +/-
 * 90 degrees latitude and +/- 180 degrees longitude. The minimums and maximums are relative to these ranges, e.g., -80
 * is less than 20. Behavior of the class is undefined for angles outside these ranges. Normalization is not performed
 * on the angles by this class, nor is it verified by the class' methods. See {@link Angle} for a description of
 * specifying angles. 

Sector instances are immutable.

* * @author Tom Gaskins * @version $Id: Sector.java 1920 2014-04-10 17:40:56Z tgaskins $ * @see Angle */ public class Sector implements Cacheable, Comparable, Iterable { /** A Sector of latitude [-90 degrees, + 90 degrees] and longitude [-180 degrees, + 180 degrees]. */ public static final Sector FULL_SPHERE = new Sector(Angle.NEG90, Angle.POS90, Angle.NEG180, Angle.POS180); public static final Sector EMPTY_SECTOR = new Sector(Angle.ZERO, Angle.ZERO, Angle.ZERO, Angle.ZERO); private final Angle minLatitude; private final Angle maxLatitude; private final Angle minLongitude; private final Angle maxLongitude; private final Angle deltaLat; private final Angle deltaLon; /** * Creates a new Sector and initializes it to the specified angles. The angles are assumed to be * normalized to +/- 90 degrees latitude and +/- 180 degrees longitude, but this method does not verify that. * * @param minLatitude the sector's minimum latitude in degrees. * @param maxLatitude the sector's maximum latitude in degrees. * @param minLongitude the sector's minimum longitude in degrees. * @param maxLongitude the sector's maximum longitude in degrees. * * @return the new Sector */ public static Sector fromDegrees(double minLatitude, double maxLatitude, double minLongitude, double maxLongitude) { return new Sector(Angle.fromDegrees(minLatitude), Angle.fromDegrees(maxLatitude), Angle.fromDegrees( minLongitude), Angle.fromDegrees(maxLongitude)); } /** * Creates a new Sector and initializes it to the specified angles. The angles are assumed to be * normalized to +/- 90 degrees latitude and +/- 180 degrees longitude, but this method does not verify that. * * @param minLatitude the sector's minimum latitude in degrees. * @param maxLatitude the sector's maximum latitude in degrees. * @param minLongitude the sector's minimum longitude in degrees. * @param maxLongitude the sector's maximum longitude in degrees. * * @return the new Sector */ public static Sector fromDegreesAndClamp(double minLatitude, double maxLatitude, double minLongitude, double maxLongitude) { if (minLatitude < -90) minLatitude = -90; if (maxLatitude > 90) maxLatitude = 90; if (minLongitude < -180) minLongitude = -180; if (maxLongitude > 180) maxLongitude = 180; return new Sector(Angle.fromDegrees(minLatitude), Angle.fromDegrees(maxLatitude), Angle.fromDegrees( minLongitude), Angle.fromDegrees(maxLongitude)); } /** * Creates a new Sector and initializes it to angles in the specified array. The array is assumed to * hold four elements containing the Sector's angles, and must be ordered as follows: minimum latitude, maximum * latitude, minimum longitude, and maximum longitude. Additionally, the angles are assumed to be normalized to +/- * 90 degrees latitude and +/- 180 degrees longitude, but this method does not verify that. * * @param array the array of angles in degrees. * * @return he new Sector * * @throws IllegalArgumentException if array is null or if its length is less than 4. */ public static Sector fromDegrees(double[] array) { if (array == null) { String message = Logging.getMessage("nullValue.ArrayIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } if (array.length < 4) { String message = Logging.getMessage("generic.ArrayInvalidLength", array.length); Logging.logger().severe(message); throw new IllegalArgumentException(message); } return fromDegrees(array[0], array[1], array[2], array[3]); } /** * Creates a new Sector and initializes it to the angles resulting from the given {@link * java.awt.geom.Rectangle2D} in degrees lat-lon coordinates where x corresponds to longitude and y to latitude. The * resulting geographic angles are assumed to be normalized to +/- 90 degrees latitude and +/- 180 degrees * longitude, but this method does not verify that. * * @param rectangle the sector's rectangle in degrees lat-lon coordinates. * * @return the new Sector */ public static Sector fromDegrees(java.awt.geom.Rectangle2D rectangle) { return fromDegrees(rectangle.getY(), rectangle.getMaxY(), rectangle.getX(), rectangle.getMaxX()); } /** * Creates a new Sector and initializes it to the specified angles. The angles are assumed to be * normalized to +/- \u03c0/2 radians latitude and +/- \u03c0 radians longitude, but this method does not verify * that. * * @param minLatitude the sector's minimum latitude in radians. * @param maxLatitude the sector's maximum latitude in radians. * @param minLongitude the sector's minimum longitude in radians. * @param maxLongitude the sector's maximum longitude in radians. * * @return the new Sector */ public static Sector fromRadians(double minLatitude, double maxLatitude, double minLongitude, double maxLongitude) { return new Sector(Angle.fromRadians(minLatitude), Angle.fromRadians(maxLatitude), Angle.fromRadians( minLongitude), Angle.fromRadians(maxLongitude)); } /** * Returns a geographic Sector which bounds the specified UTM rectangle. The UTM rectangle is located in specified * UTM zone and hemisphere. * * @param zone the UTM zone. * @param hemisphere the UTM hemisphere, either {@link gov.nasa.worldwind.avlist.AVKey#NORTH} or {@link * gov.nasa.worldwind.avlist.AVKey#SOUTH}. * @param minEasting the minimum UTM easting, in meters. * @param maxEasting the maximum UTM easting, in meters. * @param minNorthing the minimum UTM northing, in meters. * @param maxNorthing the maximum UTM northing, in meters. * * @return a Sector that bounds the specified UTM rectangle. * * @throws IllegalArgumentException if zone is outside the range 1-60, if hemisphere is * null, or if hemisphere is not one of {@link * gov.nasa.worldwind.avlist.AVKey#NORTH} or {@link gov.nasa.worldwind.avlist.AVKey#SOUTH}. */ public static Sector fromUTMRectangle(int zone, String hemisphere, double minEasting, double maxEasting, double minNorthing, double maxNorthing) { if (zone < 1 || zone > 60) { String message = Logging.getMessage("generic.ZoneIsInvalid", zone); Logging.logger().severe(message); throw new IllegalArgumentException(message); } if (!AVKey.NORTH.equals(hemisphere) && !AVKey.SOUTH.equals(hemisphere)) { String message = Logging.getMessage("generic.HemisphereIsInvalid", hemisphere); Logging.logger().severe(message); throw new IllegalArgumentException(message); } LatLon ll = UTMCoord.locationFromUTMCoord(zone, hemisphere, minEasting, minNorthing, null); LatLon lr = UTMCoord.locationFromUTMCoord(zone, hemisphere, maxEasting, minNorthing, null); LatLon ur = UTMCoord.locationFromUTMCoord(zone, hemisphere, maxEasting, maxNorthing, null); LatLon ul = UTMCoord.locationFromUTMCoord(zone, hemisphere, minEasting, maxNorthing, null); return boundingSector(Arrays.asList(ll, lr, ur, ul)); } public static Sector boundingSector(java.util.Iterator positions) { if (positions == null) { String message = Logging.getMessage("nullValue.TracksPointsIteratorNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } if (!positions.hasNext()) return EMPTY_SECTOR; TrackPoint position = positions.next(); double minLat = position.getLatitude(); double minLon = position.getLongitude(); double maxLat = minLat; double maxLon = minLon; while (positions.hasNext()) { TrackPoint p = positions.next(); double lat = p.getLatitude(); if (lat < minLat) minLat = lat; else if (lat > maxLat) maxLat = lat; double lon = p.getLongitude(); if (lon < minLon) minLon = lon; else if (lon > maxLon) maxLon = lon; } return Sector.fromDegrees(minLat, maxLat, minLon, maxLon); } public static Sector boundingSector(Iterable locations) { if (locations == null) { String message = Logging.getMessage("nullValue.PositionsListIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } if (!locations.iterator().hasNext()) return EMPTY_SECTOR; // TODO: should be returning null double minLat = Angle.POS90.getDegrees(); double minLon = Angle.POS180.getDegrees(); double maxLat = Angle.NEG90.getDegrees(); double maxLon = Angle.NEG180.getDegrees(); for (LatLon p : locations) { double lat = p.getLatitude().getDegrees(); if (lat < minLat) minLat = lat; if (lat > maxLat) maxLat = lat; double lon = p.getLongitude().getDegrees(); if (lon < minLon) minLon = lon; if (lon > maxLon) maxLon = lon; } return Sector.fromDegrees(minLat, maxLat, minLon, maxLon); } public static Sector[] splitBoundingSectors(Iterable locations) { if (locations == null) { String message = Logging.getMessage("nullValue.LocationInListIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } if (!locations.iterator().hasNext()) return null; double minLat = Angle.POS90.getDegrees(); double minLon = Angle.POS180.getDegrees(); double maxLat = Angle.NEG90.getDegrees(); double maxLon = Angle.NEG180.getDegrees(); LatLon lastLocation = null; for (LatLon ll : locations) { double lat = ll.getLatitude().getDegrees(); if (lat < minLat) minLat = lat; if (lat > maxLat) maxLat = lat; double lon = ll.getLongitude().getDegrees(); if (lon >= 0 && lon < minLon) minLon = lon; if (lon <= 0 && lon > maxLon) maxLon = lon; if (lastLocation != null) { double lastLon = lastLocation.getLongitude().getDegrees(); if (Math.signum(lon) != Math.signum(lastLon)) { if (Math.abs(lon - lastLon) < 180) { // Crossing the zero longitude line too maxLon = 0; minLon = 0; } } } lastLocation = ll; } if (minLat == maxLat && minLon == maxLon) return null; return new Sector[] { Sector.fromDegrees(minLat, maxLat, minLon, 180), // Sector on eastern hemisphere. Sector.fromDegrees(minLat, maxLat, -180, maxLon) // Sector on western hemisphere. }; } public static Sector boundingSector(LatLon pA, LatLon pB) { if (pA == null || pB == null) { String message = Logging.getMessage("nullValue.PositionsListIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } double minLat = pA.getLatitude().degrees; double minLon = pA.getLongitude().degrees; double maxLat = pA.getLatitude().degrees; double maxLon = pA.getLongitude().degrees; if (pB.getLatitude().degrees < minLat) minLat = pB.getLatitude().degrees; else if (pB.getLatitude().degrees > maxLat) maxLat = pB.getLatitude().degrees; if (pB.getLongitude().degrees < minLon) minLon = pB.getLongitude().degrees; else if (pB.getLongitude().degrees > maxLon) maxLon = pB.getLongitude().degrees; return Sector.fromDegrees(minLat, maxLat, minLon, maxLon); } /** * Returns a new Sector encompassing a circle centered at a given position, with a given radius in * meter. * * @param globe a Globe instance. * @param center the circle center position. * @param radius the circle radius in meter. * * @return the circle bounding sector. */ public static Sector boundingSector(Globe globe, LatLon center, double radius) { double halfDeltaLatRadians = radius / globe.getRadiusAt(center); double halfDeltaLonRadians = Math.PI * 2; if (center.getLatitude().cos() > 0) halfDeltaLonRadians = halfDeltaLatRadians / center.getLatitude().cos(); return new Sector( Angle.fromRadiansLatitude(center.getLatitude().radians - halfDeltaLatRadians), Angle.fromRadiansLatitude(center.getLatitude().radians + halfDeltaLatRadians), Angle.fromRadiansLongitude(center.getLongitude().radians - halfDeltaLonRadians), Angle.fromRadiansLongitude(center.getLongitude().radians + halfDeltaLonRadians)); } /** * Returns an array of Sectors encompassing a circle centered at a given position, with a given radius in meters. If * the geometry defined by the circle and radius spans the international dateline, this will return two sectors, one * for each side of the dateline. Otherwise, this will return a single bounding sector. This returns null if the * radius is zero. * * @param globe a Globe instance. * @param center the circle center location. * @param radius the circle radius in meters. * * @return the circle's bounding sectors, or null if the radius is zero. * * @throws IllegalArgumentException if either the globe or center is null, or if the radius is less than zero. */ public static Sector[] splitBoundingSectors(Globe globe, LatLon center, double radius) { if (globe == null) { String message = Logging.getMessage("nullValue.GlobeIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } if (center == null) { String message = Logging.getMessage("nullValue.CenterIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } if (radius < 0) { String message = Logging.getMessage("generic.ArgumentOutOfRange", "radius < 0"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } double halfDeltaLatRadians = radius / globe.getRadiusAt(center); double minLat = center.getLatitude().radians - halfDeltaLatRadians; double maxLat = center.getLatitude().radians + halfDeltaLatRadians; double minLon; double maxLon; // If the circle does not cross a pole, then compute the max and min longitude. if (minLat >= Angle.NEG90.radians && maxLat <= Angle.POS90.radians) { // We want to find the maximum and minimum longitude values on the circle. We will start with equation 5-6 // from "Map Projections - A Working Manual", page 31, and solve for the value of Az that will maximize // lon - lon0. // // Eq. 5-6: // lon = lon0 + arctan( h(lat0, c, az) ) // h(lat0, c, az) = sin(c) sin(az) / (cos(lat0) cos(c) - sin(lat1) sin(c) cos(Az)) // // Where (lat0, lon0) are the starting coordinates, c is the angular distance along the great circle from // the starting coordinate, Az is the azimuth, and lon is the end position longitude. All values are in // radians. // // lon - lon0 is maximized when h(lat0, c, Az) is maximized because arctan(x) -> 1 as x -> infinity. // // Taking the partial derivative of h with respect to Az we get: // h'(Az) = (sin(c) cos(c) cos(lat0) cos(Az) - sin^2(c) sin(lat0)) / (cos(lat0) cos(c) - sin(lat0) sin(c) cos(Az))^2 // // Setting h' = 0 to find maxima: // 0 = sin(c) cos(c) cos(lat0) cos(Az) - sin^2(c) sin(lat0) // // And solving for Az: // Az = arccos( tan(lat0) tan(c) ) // // +/- Az are bearings from North that will give positions of max and min longitude. // If halfDeltaLatRadians == 90 degrees, then tan is undefined. This can happen if the circle radius is one // quarter of the globe diameter, and the circle is centered on the equator. tan(center lat) is always // defined because the center lat is in the range -90 to 90 exclusive. If it were equal to 90, then the // circle would cover a pole. double az; if (Math.abs(Angle.POS90.radians - halfDeltaLatRadians) > 0.001) // Consider within 1/1000th of a radian to be equal az = Math.acos(Math.tan(halfDeltaLatRadians) * Math.tan(center.latitude.radians)); else az = Angle.POS90.radians; LatLon east = LatLon.greatCircleEndPosition(center, az, halfDeltaLatRadians); LatLon west = LatLon.greatCircleEndPosition(center, -az, halfDeltaLatRadians); minLon = Math.min(east.longitude.radians, west.longitude.radians); maxLon = Math.max(east.longitude.radians, west.longitude.radians); } else { // If the circle crosses the pole then it spans the full circle of longitude minLon = Angle.NEG180.radians; maxLon = Angle.POS180.radians; } LatLon ll = new LatLon( Angle.fromRadiansLatitude(minLat), Angle.normalizedLongitude(Angle.fromRadians(minLon))); LatLon ur = new LatLon( Angle.fromRadiansLatitude(maxLat), Angle.normalizedLongitude(Angle.fromRadians(maxLon))); Iterable locations = java.util.Arrays.asList(ll, ur); if (LatLon.locationsCrossDateLine(locations)) { return splitBoundingSectors(locations); } else { Sector s = boundingSector(locations); return (s != null && !s.equals(Sector.EMPTY_SECTOR)) ? new Sector[] {s} : null; } } /** * Creates a new Sector and initializes it to the specified angles. The angles are assumed to be * normalized to +/- 90 degrees latitude and +/- 180 degrees longitude, but this method does not verify that. * * @param minLatitude the sector's minimum latitude. * @param maxLatitude the sector's maximum latitude. * @param minLongitude the sector's minimum longitude. * @param maxLongitude the sector's maximum longitude. * * @throws IllegalArgumentException if any of the angles are null */ public Sector(Angle minLatitude, Angle maxLatitude, Angle minLongitude, Angle maxLongitude) { if (minLatitude == null || maxLatitude == null || minLongitude == null || maxLongitude == null) { String message = Logging.getMessage("nullValue.InputAnglesNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } this.minLatitude = minLatitude; this.maxLatitude = maxLatitude; this.minLongitude = minLongitude; this.maxLongitude = maxLongitude; this.deltaLat = Angle.fromDegrees(this.maxLatitude.degrees - this.minLatitude.degrees); this.deltaLon = Angle.fromDegrees(this.maxLongitude.degrees - this.minLongitude.degrees); } public Sector(Sector sector) { if (sector == null) { String message = Logging.getMessage("nullValue.SectorIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } this.minLatitude = new Angle(sector.getMinLatitude()); this.maxLatitude = new Angle(sector.getMaxLatitude()); this.minLongitude = new Angle(sector.getMinLongitude()); this.maxLongitude = new Angle(sector.getMaxLongitude()); this.deltaLat = Angle.fromDegrees(this.maxLatitude.degrees - this.minLatitude.degrees); this.deltaLon = Angle.fromDegrees(this.maxLongitude.degrees - this.minLongitude.degrees); } /** * Returns the sector's minimum latitude. * * @return The sector's minimum latitude. */ public final Angle getMinLatitude() { return minLatitude; } /** * Returns the sector's minimum longitude. * * @return The sector's minimum longitude. */ public final Angle getMinLongitude() { return minLongitude; } /** * Returns the sector's maximum latitude. * * @return The sector's maximum latitude. */ public final Angle getMaxLatitude() { return maxLatitude; } /** * Returns the sector's maximum longitude. * * @return The sector's maximum longitude. */ public final Angle getMaxLongitude() { return maxLongitude; } /** * Returns the angular difference between the sector's minimum and maximum latitudes: max - min * * @return The angular difference between the sector's minimum and maximum latitudes. */ public final Angle getDeltaLat() { return this.deltaLat;//Angle.fromDegrees(this.maxLatitude.degrees - this.minLatitude.degrees); } public final double getDeltaLatDegrees() { return this.deltaLat.degrees;//this.maxLatitude.degrees - this.minLatitude.degrees; } public final double getDeltaLatRadians() { return this.deltaLat.radians;//this.maxLatitude.radians - this.minLatitude.radians; } /** * Returns the angular difference between the sector's minimum and maximum longitudes: max - min. * * @return The angular difference between the sector's minimum and maximum longitudes */ public final Angle getDeltaLon() { return this.deltaLon;//Angle.fromDegrees(this.maxLongitude.degrees - this.minLongitude.degrees); } public final double getDeltaLonDegrees() { return this.deltaLon.degrees;//this.maxLongitude.degrees - this.minLongitude.degrees; } public final double getDeltaLonRadians() { return this.deltaLon.radians;//this.maxLongitude.radians - this.minLongitude.radians; } public boolean isWithinLatLonLimits() { return this.minLatitude.degrees >= -90 && this.maxLatitude.degrees <= 90 && this.minLongitude.degrees >= -180 && this.maxLongitude.degrees <= 180; } public boolean isSameSector(Iterable corners) { if (corners == null) { String message = Logging.getMessage("nullValue.LocationsListIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } if (!isSector(corners)) return false; Sector s = Sector.boundingSector(corners); return s.equals(this); } @SuppressWarnings({"RedundantIfStatement"}) public static boolean isSector(Iterable corners) { if (corners == null) { String message = Logging.getMessage("nullValue.LocationsListIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } LatLon[] latlons = new LatLon[5]; int i = 0; for (LatLon ll : corners) { if (i > 4 || ll == null) return false; latlons[i++] = ll; } if (!latlons[0].getLatitude().equals(latlons[1].getLatitude())) return false; if (!latlons[2].getLatitude().equals(latlons[3].getLatitude())) return false; if (!latlons[0].getLongitude().equals(latlons[3].getLongitude())) return false; if (!latlons[1].getLongitude().equals(latlons[2].getLongitude())) return false; if (i == 5 && !latlons[4].equals(latlons[0])) return false; return true; } /** * Returns the latitude and longitude of the sector's angular center: (minimum latitude + maximum latitude) / 2, * (minimum longitude + maximum longitude) / 2. * * @return The latitude and longitude of the sector's angular center */ public LatLon getCentroid() { Angle la = Angle.fromDegrees(0.5 * (this.getMaxLatitude().degrees + this.getMinLatitude().degrees)); Angle lo = Angle.fromDegrees(0.5 * (this.getMaxLongitude().degrees + this.getMinLongitude().degrees)); return new LatLon(la, lo); } /** * Computes the Cartesian coordinates of a Sector's center. * * @param globe The globe associated with the sector. * @param exaggeration The vertical exaggeration to apply. * * @return the Cartesian coordinates of the sector's center. * * @throws IllegalArgumentException if globe is null. */ public Vec4 computeCenterPoint(Globe globe, double exaggeration) { if (globe == null) { String msg = Logging.getMessage("nullValue.GlobeIsNull"); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } double lat = 0.5 * (this.minLatitude.degrees + this.maxLatitude.degrees); double lon = 0.5 * (this.minLongitude.degrees + this.maxLongitude.degrees); Angle cLat = Angle.fromDegrees(lat); Angle cLon = Angle.fromDegrees(lon); return globe.computePointFromPosition(cLat, cLon, exaggeration * globe.getElevation(cLat, cLon)); } /** * Computes the Cartesian coordinates of a Sector's corners. * * @param globe The globe associated with the sector. * @param exaggeration The vertical exaggeration to apply. * * @return an array of four Cartesian points. * * @throws IllegalArgumentException if globe is null. */ public Vec4[] computeCornerPoints(Globe globe, double exaggeration) { if (globe == null) { String msg = Logging.getMessage("nullValue.GlobeIsNull"); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } Vec4[] corners = new Vec4[4]; Angle minLat = this.minLatitude; Angle maxLat = this.maxLatitude; Angle minLon = this.minLongitude; Angle maxLon = this.maxLongitude; corners[0] = globe.computePointFromPosition(minLat, minLon, exaggeration * globe.getElevation(minLat, minLon)); corners[1] = globe.computePointFromPosition(minLat, maxLon, exaggeration * globe.getElevation(minLat, maxLon)); corners[2] = globe.computePointFromPosition(maxLat, maxLon, exaggeration * globe.getElevation(maxLat, maxLon)); corners[3] = globe.computePointFromPosition(maxLat, minLon, exaggeration * globe.getElevation(maxLat, minLon)); return corners; } /** * Returns a sphere that minimally surrounds the sector at a specified vertical exaggeration. * * @param globe the globe the sector is associated with * @param verticalExaggeration the vertical exaggeration to apply to the globe's elevations when computing the * sphere. * @param sector the sector to return the bounding sphere for. * * @return The minimal bounding sphere in Cartesian coordinates. * * @throws IllegalArgumentException if globe or sector is null */ static public Sphere computeBoundingSphere(Globe globe, double verticalExaggeration, Sector sector) { if (globe == null) { String msg = Logging.getMessage("nullValue.GlobeIsNull"); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } if (sector == null) { String msg = Logging.getMessage("nullValue.SectorIsNull"); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } LatLon center = sector.getCentroid(); double[] minAndMaxElevations = globe.getMinAndMaxElevations(sector); double minHeight = minAndMaxElevations[0] * verticalExaggeration; double maxHeight = minAndMaxElevations[1] * verticalExaggeration; Vec4[] points = new Vec4[9]; points[0] = globe.computePointFromPosition(center.getLatitude(), center.getLongitude(), maxHeight); points[1] = globe.computePointFromPosition(sector.getMaxLatitude(), sector.getMinLongitude(), maxHeight); points[2] = globe.computePointFromPosition(sector.getMinLatitude(), sector.getMaxLongitude(), maxHeight); points[3] = globe.computePointFromPosition(sector.getMinLatitude(), sector.getMinLongitude(), maxHeight); points[4] = globe.computePointFromPosition(sector.getMaxLatitude(), sector.getMaxLongitude(), maxHeight); points[5] = globe.computePointFromPosition(sector.getMaxLatitude(), sector.getMinLongitude(), minHeight); points[6] = globe.computePointFromPosition(sector.getMinLatitude(), sector.getMaxLongitude(), minHeight); points[7] = globe.computePointFromPosition(sector.getMinLatitude(), sector.getMinLongitude(), minHeight); points[8] = globe.computePointFromPosition(sector.getMaxLatitude(), sector.getMaxLongitude(), minHeight); return Sphere.createBoundingSphere(points); } /** * Returns a {@link gov.nasa.worldwind.geom.Box} that bounds the specified sector on the surface of the specified * {@link gov.nasa.worldwind.globes.Globe}. The returned box encloses the globe's surface terrain in the sector, * according to the specified vertical exaggeration and the globe's minimum and maximum elevations in the sector. If * the minimum and maximum elevation are equal, this assumes a maximum elevation of 10 + the minimum. If this fails * to compute a box enclosing the sector, this returns a unit box enclosing one of the boxes corners. * * @param globe the globe the extent relates to. * @param verticalExaggeration the globe's vertical surface exaggeration. * @param sector a sector on the globe's surface to compute a bounding box for. * * @return a box enclosing the globe's surface on the specified sector. * * @throws IllegalArgumentException if either the globe or sector is null. */ static public Box computeBoundingBox(Globe globe, double verticalExaggeration, Sector sector) { if (globe == null) { String msg = Logging.getMessage("nullValue.GlobeIsNull"); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } if (sector == null) { String msg = Logging.getMessage("nullValue.SectorIsNull"); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } double[] minAndMaxElevations = globe.getMinAndMaxElevations(sector); return computeBoundingBox(globe, verticalExaggeration, sector, minAndMaxElevations[0], minAndMaxElevations[1]); } /** * Returns a {@link gov.nasa.worldwind.geom.Box} that bounds the specified sector on the surface of the specified * {@link gov.nasa.worldwind.globes.Globe}. The returned box encloses the globe's surface terrain in the sector, * according to the specified vertical exaggeration, minimum elevation, and maximum elevation. If the minimum and * maximum elevation are equal, this assumes a maximum elevation of 10 + the minimum. If this fails to compute a box * enclosing the sector, this returns a unit box enclosing one of the boxes corners. * * @param globe the globe the extent relates to. * @param verticalExaggeration the globe's vertical surface exaggeration. * @param sector a sector on the globe's surface to compute a bounding box for. * @param minElevation the globe's minimum elevation in the sector. * @param maxElevation the globe's maximum elevation in the sector. * * @return a box enclosing the globe's surface on the specified sector. * * @throws IllegalArgumentException if either the globe or sector is null. */ public static Box computeBoundingBox(Globe globe, double verticalExaggeration, Sector sector, double minElevation, double maxElevation) { if (globe == null) { String msg = Logging.getMessage("nullValue.GlobeIsNull"); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } if (sector == null) { String msg = Logging.getMessage("nullValue.SectorIsNull"); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } // Compute the exaggerated minimum and maximum heights. double minHeight = minElevation * verticalExaggeration; double maxHeight = maxElevation * verticalExaggeration; if (minHeight == maxHeight) maxHeight = minHeight + 10; // Ensure the top and bottom heights are not equal. List points = new ArrayList(); for (LatLon ll : sector) { points.add(globe.computePointFromPosition(ll, minHeight)); points.add(globe.computePointFromPosition(ll, maxHeight)); } // A point at the centroid captures the maximum vertical dimension. LatLon centroid = sector.getCentroid(); points.add(globe.computePointFromPosition(centroid, maxHeight)); // If the sector spans the equator, then the curvature of all four edges need to be taken into account. The // extreme points along the top and bottom edges are located at their mid-points, and the extreme points along // the left and right edges are on the equator. Add points with the longitude of the sector's centroid but with // the sector's min and max latitude, and add points with the sector's min and max longitude but with latitude // at the equator. See WWJINT-225. if (sector.getMinLatitude().degrees < 0 && sector.getMaxLatitude().degrees > 0) { points.add(globe.computePointFromPosition(new LatLon(sector.getMinLatitude(), centroid.getLongitude()), maxHeight)); points.add(globe.computePointFromPosition(new LatLon(sector.getMaxLatitude(), centroid.getLongitude()), maxHeight)); points.add(globe.computePointFromPosition(new LatLon(Angle.ZERO, sector.getMinLongitude()), maxHeight)); points.add(globe.computePointFromPosition(new LatLon(Angle.ZERO, sector.getMaxLongitude()), maxHeight)); } // If the sector is located entirely in the southern hemisphere, then the curvature of its top edge needs to be // taken into account. The extreme point along the top edge is located at its mid-point. Add a point with the // longitude of the sector's centroid but with the sector's max latitude. See WWJINT-225. else if (sector.getMinLatitude().degrees < 0) { points.add(globe.computePointFromPosition(new LatLon(sector.getMaxLatitude(), centroid.getLongitude()), maxHeight)); } // If the sector is located entirely in the northern hemisphere, then the curvature of its bottom edge needs to // be taken into account. The extreme point along the bottom edge is located at its mid-point. Add a point with // the longitude of the sector's centroid but with the sector's min latitude. See WWJINT-225. else { points.add(globe.computePointFromPosition(new LatLon(sector.getMinLatitude(), centroid.getLongitude()), maxHeight)); } // If the sector spans 360 degrees of longitude then is a band around the entire globe. (If one edge is a pole // then the sector looks like a circle around the pole.) Add points at the min and max latitudes and longitudes // 0, 180, 90, and -90 to capture full extent of the band. if (sector.getDeltaLonDegrees() >= 360) { Angle minLat = sector.getMinLatitude(); points.add(globe.computePointFromPosition(minLat, Angle.ZERO, maxHeight)); points.add(globe.computePointFromPosition(minLat, Angle.POS90, maxHeight)); points.add(globe.computePointFromPosition(minLat, Angle.NEG90, maxHeight)); points.add(globe.computePointFromPosition(minLat, Angle.POS180, maxHeight)); Angle maxLat = sector.getMaxLatitude(); points.add(globe.computePointFromPosition(maxLat, Angle.ZERO, maxHeight)); points.add(globe.computePointFromPosition(maxLat, Angle.POS90, maxHeight)); points.add(globe.computePointFromPosition(maxLat, Angle.NEG90, maxHeight)); points.add(globe.computePointFromPosition(maxLat, Angle.POS180, maxHeight)); } else if (sector.getDeltaLonDegrees() > 180) { // Need to compute more points to ensure the box encompasses the full sector. Angle cLon = sector.getCentroid().getLongitude(); Angle cLat = sector.getCentroid().getLatitude(); // centroid latitude, longitude midway between min longitude and centroid longitude Angle lon = Angle.midAngle(sector.getMinLongitude(), cLon); points.add(globe.computePointFromPosition(cLat, lon, maxHeight)); // centroid latitude, longitude midway between centroid longitude and max longitude lon = Angle.midAngle(cLon, sector.getMaxLongitude()); points.add(globe.computePointFromPosition(cLat, lon, maxHeight)); // centroid latitude, longitude at min longitude and max longitude points.add(globe.computePointFromPosition(cLat, sector.getMinLongitude(), maxHeight)); points.add(globe.computePointFromPosition(cLat, sector.getMaxLongitude(), maxHeight)); } try { return Box.computeBoundingBox(points); } catch (Exception e) { return new Box(points.get(0)); // unit box around point } } /** * Returns a cylinder that minimally surrounds the specified sector at a specified vertical exaggeration. * * @param globe The globe associated with the sector. * @param verticalExaggeration the vertical exaggeration to apply to the minimum and maximum elevations when * computing the cylinder. * @param sector the sector to return the bounding cylinder for. * * @return The minimal bounding cylinder in Cartesian coordinates. * * @throws IllegalArgumentException if sector is null */ static public Cylinder computeBoundingCylinder(Globe globe, double verticalExaggeration, Sector sector) { if (globe == null) { String msg = Logging.getMessage("nullValue.GlobeIsNull"); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } if (sector == null) { String msg = Logging.getMessage("nullValue.SectorIsNull"); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } double[] minAndMaxElevations = globe.getMinAndMaxElevations(sector); return computeBoundingCylinder(globe, verticalExaggeration, sector, minAndMaxElevations[0], minAndMaxElevations[1]); } /** * Returns a cylinder that minimally surrounds the specified sector at a specified vertical exaggeration and minimum * and maximum elevations for the sector. * * @param globe The globe associated with the sector. * @param verticalExaggeration the vertical exaggeration to apply to the minimum and maximum elevations when * computing the cylinder. * @param sector the sector to return the bounding cylinder for. * @param minElevation the minimum elevation of the bounding cylinder. * @param maxElevation the maximum elevation of the bounding cylinder. * * @return The minimal bounding cylinder in Cartesian coordinates. * * @throws IllegalArgumentException if sector is null */ public static Cylinder computeBoundingCylinder(Globe globe, double verticalExaggeration, Sector sector, double minElevation, double maxElevation) { if (globe == null) { String msg = Logging.getMessage("nullValue.GlobeIsNull"); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } if (sector == null) { String msg = Logging.getMessage("nullValue.SectorIsNull"); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } // Compute the exaggerated minimum and maximum heights. double minHeight = minElevation * verticalExaggeration; double maxHeight = maxElevation * verticalExaggeration; if (minHeight == maxHeight) maxHeight = minHeight + 1; // ensure the top and bottom of the cylinder won't be coincident List points = new ArrayList(); for (LatLon ll : sector) { points.add(globe.computePointFromPosition(ll, minHeight)); points.add(globe.computePointFromPosition(ll, maxHeight)); } points.add(globe.computePointFromPosition(sector.getCentroid(), maxHeight)); if (sector.getDeltaLonDegrees() > 180) { // Need to compute more points to ensure the box encompasses the full sector. Angle cLon = sector.getCentroid().getLongitude(); Angle cLat = sector.getCentroid().getLatitude(); // centroid latitude, longitude midway between min longitude and centroid longitude Angle lon = Angle.midAngle(sector.getMinLongitude(), cLon); points.add(globe.computePointFromPosition(cLat, lon, maxHeight)); // centroid latitude, longitude midway between centroid longitude and max longitude lon = Angle.midAngle(cLon, sector.getMaxLongitude()); points.add(globe.computePointFromPosition(cLat, lon, maxHeight)); // centroid latitude, longitude at min longitude and max longitude points.add(globe.computePointFromPosition(cLat, sector.getMinLongitude(), maxHeight)); points.add(globe.computePointFromPosition(cLat, sector.getMaxLongitude(), maxHeight)); } try { return Cylinder.computeBoundingCylinder(points); } catch (Exception e) { return new Cylinder(points.get(0), points.get(0).add3(Vec4.UNIT_Y), 1); } } static public Cylinder computeBoundingCylinderOrig(Globe globe, double verticalExaggeration, Sector sector) { return Cylinder.computeVerticalBoundingCylinder(globe, verticalExaggeration, sector); } /** * Returns a cylinder that minimally surrounds the specified minimum and maximum elevations in the sector at a * specified vertical exaggeration. * * @param globe The globe associated with the sector. * @param verticalExaggeration the vertical exaggeration to apply to the minimum and maximum elevations when * computing the cylinder. * @param sector the sector to return the bounding cylinder for. * @param minElevation the minimum elevation of the bounding cylinder. * @param maxElevation the maximum elevation of the bounding cylinder. * * @return The minimal bounding cylinder in Cartesian coordinates. * * @throws IllegalArgumentException if sector is null */ public static Cylinder computeBoundingCylinderOrig(Globe globe, double verticalExaggeration, Sector sector, double minElevation, double maxElevation) { return Cylinder.computeVerticalBoundingCylinder(globe, verticalExaggeration, sector, minElevation, maxElevation); } public final boolean contains(Angle latitude, Angle longitude) { if (latitude == null || longitude == null) { String message = Logging.getMessage("nullValue.LatLonIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } return containsDegrees(latitude.degrees, longitude.degrees); } /** * Determines whether a latitude/longitude position is within the sector. The sector's angles are assumed to be * normalized to +/- 90 degrees latitude and +/- 180 degrees longitude. The result of the operation is undefined if * they are not. * * @param latLon the position to test, with angles normalized to +/- π latitude and +/- 2π longitude. * * @return true if the position is within the sector, false otherwise. * * @throws IllegalArgumentException if latlon is null. */ public final boolean contains(LatLon latLon) { if (latLon == null) { String message = Logging.getMessage("nullValue.LatLonIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } return this.contains(latLon.getLatitude(), latLon.getLongitude()); } /** * Determines whether a latitude/longitude postion expressed in radians is within the sector. The sector's angles * are assumed to be normalized to +/- 90 degrees latitude and +/- 180 degrees longitude. The result of the * operation is undefined if they are not. * * @param radiansLatitude the latitude in radians of the position to test, normalized +/- π. * @param radiansLongitude the longitude in radians of the position to test, normalized +/- 2π. * * @return true if the position is within the sector, false otherwise. */ public boolean containsRadians(double radiansLatitude, double radiansLongitude) { return radiansLatitude >= this.minLatitude.radians && radiansLatitude <= this.maxLatitude.radians && radiansLongitude >= this.minLongitude.radians && radiansLongitude <= this.maxLongitude.radians; } public boolean containsDegrees(double degreesLatitude, double degreesLongitude) { return degreesLatitude >= this.minLatitude.degrees && degreesLatitude <= this.maxLatitude.degrees && degreesLongitude >= this.minLongitude.degrees && degreesLongitude <= this.maxLongitude.degrees; } /** * Determines whether another sectror is fully contained within this one. The sector's angles are assumed to be * normalized to +/- 90 degrees latitude and +/- 180 degrees longitude. The result of the operation is undefined if * they are not. * * @param that the sector to test for containment. * * @return true if this sector fully contains the input sector, otherwise false. */ public boolean contains(Sector that) { if (that == null) return false; // Assumes normalized angles -- [-180, 180], [-90, 90] if (that.minLongitude.degrees < this.minLongitude.degrees) return false; if (that.maxLongitude.degrees > this.maxLongitude.degrees) return false; if (that.minLatitude.degrees < this.minLatitude.degrees) return false; //noinspection RedundantIfStatement if (that.maxLatitude.degrees > this.maxLatitude.degrees) return false; return true; } /** * Determines whether this sector intersects another sector's range of latitude and longitude. The sector's angles * are assumed to be normalized to +/- 90 degrees latitude and +/- 180 degrees longitude. The result of the * operation is undefined if they are not. * * @param that the sector to test for intersection. * * @return true if the sectors intersect, otherwise false. */ public boolean intersects(Sector that) { if (that == null) return false; // Assumes normalized angles -- [-180, 180], [-90, 90] if (that.maxLongitude.degrees < this.minLongitude.degrees) return false; if (that.minLongitude.degrees > this.maxLongitude.degrees) return false; if (that.maxLatitude.degrees < this.minLatitude.degrees) return false; //noinspection RedundantIfStatement if (that.minLatitude.degrees > this.maxLatitude.degrees) return false; return true; } /** * Determines whether the interiors of this sector and another sector intersect. The sector's angles are assumed to * be normalized to +/- 90 degrees latitude and +/- 180 degrees longitude. The result of the operation is undefined * if they are not. * * @param that the sector to test for intersection. * * @return true if the sectors' interiors intersect, otherwise false. * * @see #intersects(Sector) */ public boolean intersectsInterior(Sector that) { if (that == null) return false; // Assumes normalized angles -- [-180, 180], [-90, 90] if (that.maxLongitude.degrees <= this.minLongitude.degrees) return false; if (that.minLongitude.degrees >= this.maxLongitude.degrees) return false; if (that.maxLatitude.degrees <= this.minLatitude.degrees) return false; //noinspection RedundantIfStatement if (that.minLatitude.degrees >= this.maxLatitude.degrees) return false; return true; } /** * Determines whether this sector intersects the specified geographic line segment. The line segment is specified by * a begin location and an end location. The locations are are assumed to be connected by a linear path in * geographic space. This returns true if any location along that linear path intersects this sector, including the * begin and end locations. * * @param begin the line segment begin location. * @param end the line segment end location. * * @return true true if this sector intersects the line segment, otherwise false. * * @throws IllegalArgumentException if either the begin location or the end location is null. */ public boolean intersectsSegment(LatLon begin, LatLon end) { if (begin == null) { String message = Logging.getMessage("nullValue.BeginIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } if (end == null) { String message = Logging.getMessage("nullValue.EndIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } Vec4 segmentBegin = new Vec4(begin.getLongitude().degrees, begin.getLatitude().degrees, 0); Vec4 segmentEnd = new Vec4(end.getLongitude().degrees, end.getLatitude().degrees, 0); Vec4 tmp = segmentEnd.subtract3(segmentBegin); Vec4 segmentCenter = segmentBegin.add3(segmentEnd).divide3(2); Vec4 segmentDirection = tmp.normalize3(); double segmentExtent = tmp.getLength3() / 2.0; LatLon centroid = this.getCentroid(); Vec4 boxCenter = new Vec4(centroid.getLongitude().degrees, centroid.getLatitude().degrees, 0); double boxExtentX = this.getDeltaLonDegrees() / 2.0; double boxExtentY = this.getDeltaLatDegrees() / 2.0; Vec4 diff = segmentCenter.subtract3(boxCenter); if (Math.abs(diff.x) > (boxExtentX + segmentExtent * Math.abs(segmentDirection.x))) { return false; } if (Math.abs(diff.y) > (boxExtentY + segmentExtent * Math.abs(segmentDirection.y))) { return false; } //noinspection SuspiciousNameCombination Vec4 segmentPerp = new Vec4(segmentDirection.y, -segmentDirection.x, 0); return Math.abs(segmentPerp.dot3(diff)) <= (boxExtentX * Math.abs(segmentPerp.x) + boxExtentY * Math.abs(segmentPerp.y)); } /** * Returns a new sector whose angles are the extremes of the this sector and another. The new sector's minimum * latitude and longitude will be the minimum of the two sectors. The new sector's maximum latitude and longitude * will be the maximum of the two sectors. The sectors are assumed to be normalized to +/- 90 degrees latitude and * +/- 180 degrees longitude. The result of the operation is undefined if they are not. * * @param that the sector to join with this. * * @return A new sector formed from the extremes of the two sectors, or this if the incoming sector is * null. */ public final Sector union(Sector that) { if (that == null) return this; Angle minLat = this.minLatitude; Angle maxLat = this.maxLatitude; Angle minLon = this.minLongitude; Angle maxLon = this.maxLongitude; if (that.minLatitude.degrees < this.minLatitude.degrees) minLat = that.minLatitude; if (that.maxLatitude.degrees > this.maxLatitude.degrees) maxLat = that.maxLatitude; if (that.minLongitude.degrees < this.minLongitude.degrees) minLon = that.minLongitude; if (that.maxLongitude.degrees > this.maxLongitude.degrees) maxLon = that.maxLongitude; return new Sector(minLat, maxLat, minLon, maxLon); } public final Sector union(Angle latitude, Angle longitude) { if (latitude == null || longitude == null) return this; Angle minLat = this.minLatitude; Angle maxLat = this.maxLatitude; Angle minLon = this.minLongitude; Angle maxLon = this.maxLongitude; if (latitude.degrees < this.minLatitude.degrees) minLat = latitude; if (latitude.degrees > this.maxLatitude.degrees) maxLat = latitude; if (longitude.degrees < this.minLongitude.degrees) minLon = longitude; if (longitude.degrees > this.maxLongitude.degrees) maxLon = longitude; return new Sector(minLat, maxLat, minLon, maxLon); } public static Sector union(Sector sectorA, Sector sectorB) { if (sectorA == null || sectorB == null) { if (sectorA == sectorB) return sectorA; return sectorB == null ? sectorA : sectorB; } return sectorA.union(sectorB); } public static Sector union(Iterable sectors) { if (sectors == null) { String msg = Logging.getMessage("nullValue.SectorListIsNull"); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } Angle minLat = Angle.POS90; Angle maxLat = Angle.NEG90; Angle minLon = Angle.POS180; Angle maxLon = Angle.NEG180; for (Sector s : sectors) { if (s == null) continue; for (LatLon p : s) { if (p.getLatitude().degrees < minLat.degrees) minLat = p.getLatitude(); if (p.getLatitude().degrees > maxLat.degrees) maxLat = p.getLatitude(); if (p.getLongitude().degrees < minLon.degrees) minLon = p.getLongitude(); if (p.getLongitude().degrees > maxLon.degrees) maxLon = p.getLongitude(); } } return new Sector(minLat, maxLat, minLon, maxLon); } public final Sector intersection(Sector that) { if (that == null) return this; Angle minLat, maxLat; minLat = (this.minLatitude.degrees > that.minLatitude.degrees) ? this.minLatitude : that.minLatitude; maxLat = (this.maxLatitude.degrees < that.maxLatitude.degrees) ? this.maxLatitude : that.maxLatitude; if (minLat.degrees > maxLat.degrees) return null; Angle minLon, maxLon; minLon = (this.minLongitude.degrees > that.minLongitude.degrees) ? this.minLongitude : that.minLongitude; maxLon = (this.maxLongitude.degrees < that.maxLongitude.degrees) ? this.maxLongitude : that.maxLongitude; if (minLon.degrees > maxLon.degrees) return null; return new Sector(minLat, maxLat, minLon, maxLon); } public final Sector intersection(Angle latitude, Angle longitude) { if (latitude == null || longitude == null) return this; if (!this.contains(latitude, longitude)) return null; return new Sector(latitude, latitude, longitude, longitude); } public Sector[] subdivide() { Angle midLat = Angle.average(this.minLatitude, this.maxLatitude); Angle midLon = Angle.average(this.minLongitude, this.maxLongitude); Sector[] sectors = new Sector[4]; sectors[0] = new Sector(this.minLatitude, midLat, this.minLongitude, midLon); sectors[1] = new Sector(this.minLatitude, midLat, midLon, this.maxLongitude); sectors[2] = new Sector(midLat, this.maxLatitude, this.minLongitude, midLon); sectors[3] = new Sector(midLat, this.maxLatitude, midLon, this.maxLongitude); return sectors; } public Sector[] subdivide(int div) { double dLat = this.deltaLat.degrees / div; double dLon = this.deltaLon.degrees / div; Sector[] sectors = new Sector[div * div]; int idx = 0; for (int row = 0; row < div; row++) { for (int col = 0; col < div; col++) { sectors[idx++] = Sector.fromDegrees( this.minLatitude.degrees + dLat * row, this.minLatitude.degrees + dLat * row + dLat, this.minLongitude.degrees + dLon * col, this.minLongitude.degrees + dLon * col + dLon); } } return sectors; } /** * Returns an approximation of the distance in model coordinates between the surface geometry defined by this sector * and the specified model coordinate point. The returned value represents the shortest distance between the * specified point and this sector's corner points or its center point. The draw context defines the globe and the * elevations that are used to compute the corner points and the center point. * * @param dc The draw context defining the surface geometry. * @param point The model coordinate point to compute a distance to. * * @return The distance between this sector's surface geometry and the specified point, in model coordinates. * * @throws IllegalArgumentException if any argument is null. */ public double distanceTo(DrawContext dc, Vec4 point) { if (dc == null) { String message = Logging.getMessage("nullValue.DrawContextIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } if (point == null) { String message = Logging.getMessage("nullValue.PointIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } Vec4[] corners = this.computeCornerPoints(dc.getGlobe(), dc.getVerticalExaggeration()); Vec4 centerPoint = this.computeCenterPoint(dc.getGlobe(), dc.getVerticalExaggeration()); // Get the distance for each of the sector's corners and its center. double d1 = point.distanceTo3(corners[0]); double d2 = point.distanceTo3(corners[1]); double d3 = point.distanceTo3(corners[2]); double d4 = point.distanceTo3(corners[3]); double d5 = point.distanceTo3(centerPoint); // Find the minimum distance. double minDistance = d1; if (minDistance > d2) minDistance = d2; if (minDistance > d3) minDistance = d3; if (minDistance > d4) minDistance = d4; if (minDistance > d5) minDistance = d5; return minDistance; } /** * Returns a four element array containing the Sector's angles in degrees. The returned array is ordered as follows: * minimum latitude, maximum latitude, minimum longitude, and maximum longitude. * * @return four-element array containing the Sector's angles. */ public double[] toArrayDegrees() { return new double[] { this.minLatitude.degrees, this.maxLatitude.degrees, this.minLongitude.degrees, this.maxLongitude.degrees }; } /** * Returns a {@link java.awt.geom.Rectangle2D} corresponding to this Sector in degrees lat-lon coordinates where x * corresponds to longitude and y to latitude. * * @return a {@link java.awt.geom.Rectangle2D} corresponding to this Sector in degrees lat-lon coordinates. */ public Rectangle2D toRectangleDegrees() { return new Rectangle2D.Double(this.getMinLongitude().degrees, this.getMinLatitude().degrees, this.getDeltaLonDegrees(), this.getDeltaLatDegrees()); } /** * Returns a string indicating the sector's angles. * * @return A string indicating the sector's angles. */ @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("("); sb.append(this.minLatitude.toString()); sb.append(", "); sb.append(this.minLongitude.toString()); sb.append(")"); sb.append(", "); sb.append("("); sb.append(this.maxLatitude.toString()); sb.append(", "); sb.append(this.maxLongitude.toString()); sb.append(")"); return sb.toString(); } /** * Retrieve the size of this object in bytes. This implementation returns an exact value of the object's size. * * @return the size of this object in bytes */ public long getSizeInBytes() { return 4 * minLatitude.getSizeInBytes(); // 4 angles } /** * Compares this sector to a specified sector according to their minimum latitude, minimum longitude, maximum * latitude, and maximum longitude, respectively. * * @param that the Sector to compareTo with this. * * @return -1 if this sector compares less than that specified, 0 if they're equal, and 1 if it compares greater. * * @throws IllegalArgumentException if that is null */ public int compareTo(Sector that) { if (that == null) { String msg = Logging.getMessage("nullValue.SectorIsNull"); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } if (this.getMinLatitude().compareTo(that.getMinLatitude()) < 0) return -1; if (this.getMinLatitude().compareTo(that.getMinLatitude()) > 0) return 1; if (this.getMinLongitude().compareTo(that.getMinLongitude()) < 0) return -1; if (this.getMinLongitude().compareTo(that.getMinLongitude()) > 0) return 1; if (this.getMaxLatitude().compareTo(that.getMaxLatitude()) < 0) return -1; if (this.getMaxLatitude().compareTo(that.getMaxLatitude()) > 0) return 1; if (this.getMaxLongitude().compareTo(that.getMaxLongitude()) < 0) return -1; if (this.getMaxLongitude().compareTo(that.getMaxLongitude()) > 0) return 1; return 0; } /** * Creates an iterator over the four corners of the sector, starting with the southwest position and continuing * counter-clockwise. * * @return an iterator for the sector. */ public Iterator iterator() { return new Iterator() { private int position = 0; public boolean hasNext() { return this.position < 4; } public LatLon next() { if (this.position > 3) throw new NoSuchElementException(); LatLon p; switch (this.position) { case 0: p = new LatLon(Sector.this.getMinLatitude(), Sector.this.getMinLongitude()); break; case 1: p = new LatLon(Sector.this.getMinLatitude(), Sector.this.getMaxLongitude()); break; case 2: p = new LatLon(Sector.this.getMaxLatitude(), Sector.this.getMaxLongitude()); break; default: p = new LatLon(Sector.this.getMaxLatitude(), Sector.this.getMinLongitude()); break; } ++this.position; return p; } public void remove() { throw new UnsupportedOperationException(); } }; } /** * Returns the coordinates of the sector as a list, in the order minLat, maxLat, minLon, maxLon. * * @return the list of sector coordinates. */ public List asList() { ArrayList list = new ArrayList(4); for (LatLon ll : this) { list.add(ll); } return list; } /** * Returns the coordinates of the sector as an array of values in degrees, in the order minLat, maxLat, minLon, * maxLon. * * @return the array of sector coordinates. */ public double[] asDegreesArray() { return new double[] { this.getMinLatitude().degrees, this.getMaxLatitude().degrees, this.getMinLongitude().degrees, this.getMaxLongitude().degrees }; } /** * Returns the coordinates of the sector as an array of values in radians, in the order minLat, maxLat, minLon, * maxLon. * * @return the array of sector coordinates. */ public double[] asRadiansArray() { return new double[] { this.getMinLatitude().radians, this.getMaxLatitude().radians, this.getMinLongitude().radians, this.getMaxLongitude().radians }; } /** * Returns a list of the Lat/Lon coordinates of a Sector's corners. * * @return an array of the four corner locations, in the order SW, SE, NE, NW */ public LatLon[] getCorners() { LatLon[] corners = new LatLon[4]; corners[0] = new LatLon(this.minLatitude, this.minLongitude); corners[1] = new LatLon(this.minLatitude, this.maxLongitude); corners[2] = new LatLon(this.maxLatitude, this.maxLongitude); corners[3] = new LatLon(this.maxLatitude, this.minLongitude); return corners; } /** * Tests the equality of the sectors' angles. Sectors are equal if all of their corresponding angles are equal. * * @param o the sector to compareTo with this. * * @return true if the four corresponding angles of each sector are equal, false * otherwise. */ @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; final gov.nasa.worldwind.geom.Sector sector = (gov.nasa.worldwind.geom.Sector) o; if (!maxLatitude.equals(sector.maxLatitude)) return false; if (!maxLongitude.equals(sector.maxLongitude)) return false; if (!minLatitude.equals(sector.minLatitude)) return false; //noinspection RedundantIfStatement if (!minLongitude.equals(sector.minLongitude)) return false; return true; } /** * Computes a hash code from the sector's four angles. * * @return a hash code incorporating the sector's four angles. */ @Override public int hashCode() { int result; result = minLatitude.hashCode(); result = 29 * result + maxLatitude.hashCode(); result = 29 * result + minLongitude.hashCode(); result = 29 * result + maxLongitude.hashCode(); return result; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy