/*
Copyright (C) 2001, 2006 United States Government
as represented by the Administrator of the
National Aeronautics and Space Administration.
All Rights Reserved.
*/
package gov.nasa.worldwind.render;
import gov.nasa.worldwind.geom.*;
import gov.nasa.worldwind.globes.Globe;
import java.awt.*;
import java.awt.geom.*;
import java.awt.image.*;
import java.util.Iterator;
/**
* @author tag
* @version $Id: SurfacePolygon.java 3716 2007-12-04 23:44:30Z dcollins $
*/
public class SurfacePolygon extends SurfaceShape
{
/**
* A Renderable polygon shape defined by a list of LatLon
*
* @param positions the list of LatLon positions that makes the polygon
* @param color the interior fill color
* @param borderColor the border color
*/
public SurfacePolygon(Iterable<LatLon> positions, Color color, Color borderColor)
{
super(positions, color, borderColor, null);
}
/**
* A Renderable polygon shape defined by a list of LatLon
*
* @param positions the list of LatLon positions that makes the polygon
*/
public SurfacePolygon(Iterable<LatLon> positions)
{
super(positions, null, null, null);
}
public SurfacePolygon(Iterable<LatLon> positions, Color color, Color borderColor, Dimension textureSize)
{
super(positions, color, borderColor, textureSize);
}
private static final double TO_RADIANS = (Math.PI / 180);
/**
* Draw all or part of the shape that intersects a given Sector into the given BufferedImage
*/
protected final BufferedImage drawShape(Globe globe, Sector sector, BufferedImage image)
{
double rsw = globe.getRadiusAt(sector.getMinLatitude(), sector.getMinLongitude());
double rne = globe.getRadiusAt(sector.getMaxLatitude(), sector.getMaxLongitude());
double xsw = rsw * sector.getMinLongitude().radians;
double ysw = rsw * sector.getMinLatitude().radians;
double xne = rne * sector.getMaxLongitude().radians;
double yne = rne * sector.getMaxLatitude().radians;
double dy = yne - ysw;
double dx = xne - xsw;
// Note : WWJ-36 negate latScale to define path upside-down
// (will be drawn with a mirror transform - this gets paint patterns right)
double latScale = dy > 0 ? -(image.getHeight() - 1) / dy : 0;
double lonScale = dx > 0 ? (image.getWidth() - 1) / dx : 0;
// If we may cross +-180 degrees longitude, then offset
// all longitudes 180 degrees the other way
double lonOffset = 0;
if (sector.getMaxLongitude().getDegrees() == 180 && sector.getDeltaLonDegrees() < 180)
lonOffset = -180;
if (sector.getMinLongitude().getDegrees() == -180 && sector.getDeltaLonDegrees() < 180)
lonOffset = 180;
GeneralPath path = new GeneralPath();
Iterator<LatLon> positions = this.getPositions().iterator();
if (!positions.hasNext())
return image;
// Start position
LatLon pos = this.computeDrawLatLon(positions.next(), sector, lonOffset);
double r = globe.getRadiusAt(pos.getLatitude(), pos.getLongitude());
double x = lonScale * (r * pos.getLongitude().radians - r * TO_RADIANS * lonOffset - xsw);
double y = latScale * (r * pos.getLatitude().radians - ysw);
path.moveTo((float) x, (float) y);
while (positions.hasNext())
{
// Next position
LatLon posNext = this.computeDrawLatLon(positions.next(), sector, lonOffset);
// Compute number of necessary steps
int numIntervals = (int) Math.max(1d,
this.getNumEdgeIntervalsPerDegree() * LatLon.greatCircleDistance(pos, posNext).degrees);
double delta = 1d / numIntervals;
// Draw segments to next position
for (int i = 1; i < numIntervals; i++)
{
// In between steps
LatLon p = LatLon.interpolate(i * delta, pos, posNext);
r = globe.getRadiusAt(p.getLatitude(), p.getLongitude());
x = lonScale * (r * p.getLongitude().radians - r * TO_RADIANS * lonOffset - xsw);
y = latScale * (r * p.getLatitude().radians - ysw);
path.lineTo((float) x, (float) y);
}
// Set the last point directly to avoid any round-off error in the iteration above.
r = globe.getRadiusAt(posNext.getLatitude(), posNext.getLongitude());
x = lonScale * (r * posNext.getLongitude().radians - r * TO_RADIANS * lonOffset - xsw);
y = latScale * (r * posNext.getLatitude().radians - ysw);
path.lineTo((float) x, (float) y);
// Next
pos = posNext;
}
Graphics2D g2 = image.createGraphics();
// Set mirror Y transform
g2.setTransform(AffineTransform.getScaleInstance(1, -1));
// Set antiliasing hint
if (this.isAntiAlias())
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// Draw interior
if (this.isDrawInterior())
{
g2.setPaint(this.getPaint());
g2.fill(path);
}
// Draw border
if (this.isDrawBorder())
{
g2.setPaint(this.getBorderColor());
g2.setStroke(this.getStroke());
g2.draw(path);
}
return image;
}
/**
* Returns the drawing LatLon relative to a given Sector and a longitude offset Can go beyond +-180 degrees
* longitude if the offset is zero
*
* @param pos the real LatLon
* @param sector the drawing Sector
* @param lonOffset the current longitude offset in degrees
* @return the appropiate drawing LatLon
*/
private LatLon computeDrawLatLon(LatLon pos, Sector sector, double lonOffset)
{
int directionOffset;
directionOffset = sector.getMaxLongitude().degrees - pos.getLongitude().getDegrees() > 180 ?
360 : 0;
directionOffset = pos.getLongitude().getDegrees() - sector.getMinLongitude().getDegrees() > 180 ?
-360 : directionOffset;
return LatLon.fromDegrees(pos.getLatitude().getDegrees(),
pos.getLongitude().getDegrees() + directionOffset + lonOffset);
}
}