content: package tim.prune.data;
  
  /**
   * Class to hold the extents of a track, in 2d and in 3d,
   * and to calculate a square area with a default border
   */
  public class TrackExtents
  {
  	/** Track object */
  	private final Track _track;
  	/** X and Y ranges */
  	private final DoubleRange _xRange;
  	private final DoubleRange _yRange;
  
  	/** Border multiplier */
  	private static final double BORDER_MULTIPLIER = 1.1; // 10% border
  
  	/**
  	 * Constructor
  	 * @param inTrack track object to take extents from
  	 */
  	public TrackExtents(Track inTrack)
  	{
  		_track  = inTrack;
  		_xRange = inTrack.getXRange().copy();
  		_yRange = inTrack.getYRange().copy();
  	}
  
  
  	/**
  	 * Make the x and y ranges square with a default border around
  	 */
  	public void applySquareBorder()
  	{
  		// Find the middle of the x and y
  		final double midXvalue = _xRange.getMidValue();
  		final double midYvalue = _yRange.getMidValue();
  		// Find x and y range, take maximum
  		double xyRange = Math.max(_xRange.getRange(), _yRange.getRange()) * BORDER_MULTIPLIER;
  		if (getHorizontalDistanceMetres() < 10.0)
  		{
  			// all the points are near enough on the same spot, expand scale to avoid dividing by zero
  			xyRange = 0.1;
  		}
  
  		// Apply these new min and max to the ranges
  		_xRange.addValue(midXvalue - xyRange / 2.0);
  		_xRange.addValue(midXvalue + xyRange / 2.0);
  		_yRange.addValue(midYvalue - xyRange / 2.0);
  		_yRange.addValue(midYvalue + xyRange / 2.0);
  	}
  
  	/** @return x range */
  	public DoubleRange getXRange() {
  		return _xRange;
  	}
  
  	/** @return y range */
  	public DoubleRange getYRange() {
  		return _yRange;
  	}
  
  	/** @return altitude range */
  	public DoubleRange getAltitudeRange()
  	{
  		final int numPoints = _track.getNumPoints();
  		DoubleRange altRange = new DoubleRange();
  		for (int i=0; i<numPoints; i++)
  		{
  			DataPoint p = _track.getPoint(i);
  			if (p != null && p.hasAltitude()) {
  				altRange.addValue(p.getAltitude().getMetricValue());
  			}
  		}
  		return altRange;
  	}
  
  	/**
  	 * @return the greater of the N/S and E/W extent of the track, in metres (including border)
  	 */
  	public double getHorizontalDistanceMetres()
  	{
  		DoubleRange lonRange = _track.getLonRange();
  		DoubleRange latRange = _track.getLatRange();
  
  		// Find horizontal and vertical extents of enclosing rectangle
  		DataPoint southPoint = new DataPoint(Latitude.make(latRange.getMinimum()),
  			Longitude.make(lonRange.getMidValue()));
  		DataPoint northPoint = new DataPoint(Latitude.make(latRange.getMaximum()),
  			Longitude.make(lonRange.getMidValue()));
  		double nsDist = Distance.convertRadiansToDistance(
  			DataPoint.calculateRadiansBetween(northPoint, southPoint), UnitSetLibrary.UNITS_METRES); // both in m
  		// Same again for bottom and top, take maximum
  		DataPoint westPoint = new DataPoint(Latitude.make(latRange.getMidValue()),
  			Longitude.make(lonRange.getMinimum()));
  		DataPoint eastPoint = new DataPoint(Latitude.make(latRange.getMidValue()),
  			Longitude.make(lonRange.getMaximum()));
  		double ewDist = Distance.convertRadiansToDistance(
  			DataPoint.calculateRadiansBetween(westPoint, eastPoint), UnitSetLibrary.UNITS_METRES); // both in m
  		final double horizDistance = Math.max(nsDist, ewDist) * BORDER_MULTIPLIER;
  
  		return horizDistance;
  	}
  }
  