/*
* Copyright (c) 2014 Oculus Info Inc.
* http://www.oculusinfo.com/
*
* Released under the MIT License.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.oculusinfo.geometry.geodesic;
import com.oculusinfo.math.algebra.AngleUtilities;
/**
* A rectangle in longitude/latitude space that takes world wrap into account
*
* @author nkronenfeld
*/
public class WrappingRectangle {
private double _epsilon; // double equality factor
private double _centerLon;
private double _minLon;
private double _minLat;
private double _maxLon;
private double _maxLat;
private WrappingRectangle (double centerLon, double minLon, double maxLon,
double minLat, double maxLat) {
_centerLon = centerLon;
_minLon = minLon;
_maxLon = maxLon;
_minLat = minLat;
_maxLat = maxLat;
}
public WrappingRectangle (Position a, Position b) {
_minLat = Math.min(a.getLatitude(), b.getLatitude());
_maxLat = Math.max(a.getLatitude(), b.getLatitude());
double alon = a.getLongitude();
double blon = AngleUtilities.intoRangeDegrees(alon, b.getLongitude());
_centerLon = (alon + blon) / 2.0;
_minLon = Math.min(alon, blon);
_maxLon = Math.max(alon, blon);
_epsilon = 1E-12;
}
/**
* Sets how close another WrappingRectangle has to be to this one to count
* as equal
*/
public void setPrecision (double precision) {
_epsilon = precision;
}
/**
* Indicates if this rectangle contains the given position. Elevation is
* ignored for containment purposes (perhaps TODO could be to allow for
* elevation min and max, if needed).
*/
public boolean contains (Position p) {
double lat = p.getLatitude();
if (lat > _maxLat || lat < _minLat)
return false;
double lon = AngleUtilities.intoRangeDegrees(_centerLon,
p.getLongitude());
if (lon > _maxLon || lon < _minLon)
return false;
return true;
}
/**
* Get a rectangle with the same center as this one, but expanded by the
* input border size.
*
* @param borderSize
* The size of the border to be added, in degrees.
*/
public WrappingRectangle addBorder (double borderSize) {
return new WrappingRectangle(_centerLon,
_minLon - borderSize,
_maxLon + borderSize,
_minLat - borderSize,
_maxLat + borderSize);
}
/**
* Gets a rectangle with the same center as this one, but scaled by the
* given amount.
*
* @param scale The scale factor
* @return The WrappingRectangle scaled by scale
*/
public WrappingRectangle scale (double scale) {
double halfWidth = (_maxLon-_minLon)/2.0;
double centerLat = (_maxLat+_minLat)/2.0;
double halfHeight = (_maxLat-_minLat)/2.0;
return new WrappingRectangle(_centerLon,
_centerLon-halfWidth*scale,
_centerLon+halfWidth*scale,
centerLat-halfHeight*scale,
centerLat+halfHeight*scale);
}
@Override
public boolean equals (Object obj) {
if (this == obj) return true;
if (null == obj) return false;
if (!(obj instanceof WrappingRectangle)) return false;
WrappingRectangle rect = (WrappingRectangle) obj;
if (Math.abs(_minLat-rect._minLat) > _epsilon) return false;
if (Math.abs(_maxLat-rect._maxLat) > _epsilon) return false;
double oCenter = AngleUtilities.intoRangeDegrees(_centerLon, rect._centerLon);
if (Math.abs(oCenter-_centerLon) > _epsilon) return false;
double oMinLon = AngleUtilities.intoRangeDegrees(_minLon, rect._minLon);
if (Math.abs(oMinLon-_minLon) > _epsilon) return false;
double oMaxLon = AngleUtilities.intoRangeDegrees(_maxLon, rect._maxLon);
if (Math.abs(oMaxLon-_maxLon) > _epsilon) return false;
return true;
}
@Override
public String toString () {
return String.format("[[%.5f, %.5f] to [%.5f, %.5f] (center=%.4f)]",
_minLon, _minLat, _maxLon, _maxLat, _centerLon);
}
}