gov.nasa.worldwind.render.ContourLine Maven / Gradle / Ivy
The 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.render;
import gov.nasa.worldwind.geom.*;
import gov.nasa.worldwind.globes.Globe;
import gov.nasa.worldwind.util.*;
import java.awt.*;
import java.util.*;
import java.util.List;
/**
* Renders a contour line on the terrain at a given elevation. The contour line extent can be bounded by a
* Sector
.
*
* @author Patrick Murris
* @version $Id: ContourLine.java 1171 2013-02-11 21:45:02Z dcollins $
*/
public class ContourLine implements Renderable
{
private double elevation;
private Sector sector;
private Color color = Color.CYAN;
private double lineWidth = 1;
private boolean enabled = true;
private ArrayList renderables = new ArrayList();
private boolean viewClippingEnabled = false;
protected Object globeStateKey;
// Geometry update support.
TimedExpirySupport expirySupport = new TimedExpirySupport(1000, 2000);
// Segments connection criteria
protected int maxConnectingDistance = 10; // meters
public ContourLine()
{
this(0, Sector.FULL_SPHERE);
}
public ContourLine(double elevation)
{
this(elevation, Sector.FULL_SPHERE);
}
@SuppressWarnings( {"UnusedDeclaration"})
public ContourLine(Sector sector)
{
this(0, sector);
}
public ContourLine(double elevation, Sector sector)
{
if (sector == null)
{
String message = Logging.getMessage("nullValue.SectorIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
this.elevation = elevation;
this.sector = sector;
}
/**
* Get the contour line current elevation.
*
* @return the contour line current elevation.
*/
public double getElevation()
{
return this.elevation;
}
/**
* Set the contour line elevation.
*
* @param elevation the contour line elevation.
*/
public void setElevation(double elevation)
{
if (this.elevation != elevation)
{
this.elevation = elevation;
this.update();
}
}
/**
* Get the contour line current bounding sector.
*
* @return the contour line current bounding sector.
*/
public Sector getSector()
{
return this.sector;
}
/**
* Set the contour line bounding sector.
*
* @param sector the contour line bounding sector.
*/
public void setSector(Sector sector)
{
if (sector == null)
{
String message = Logging.getMessage("nullValue.SectorIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (!this.sector.equals(sector))
{
this.sector = sector;
this.update();
}
}
/**
* Get the contour line color.
*
* @return the contour line color.
*/
public Color getColor()
{
return this.color;
}
/**
* Set the contour line color.
*
* @param color the contour line color.
*/
public void setColor(Color color)
{
if (color == null)
{
String msg = Logging.getMessage("nullValue.ColorIsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
if (!this.color.equals(color))
{
this.color = color;
for (Renderable r : this.getRenderables())
{
if (r instanceof Polyline)
((Polyline) r).setColor(color);
}
}
}
/**
* Get the contour line width.
*
* @return the contour line width.
*/
public double getLineWidth()
{
return this.lineWidth;
}
/**
* Set the contour line width.
*
* @param width the contour line width.
*/
public void setLineWidth(double width)
{
if (this.lineWidth != width)
{
this.lineWidth = width;
for (Renderable r : this.getRenderables())
{
if (r instanceof Polyline)
((Polyline) r).setLineWidth(width);
}
}
}
public boolean isEnabled()
{
return this.enabled;
}
public void setEnabled(boolean state)
{
this.enabled = state;
}
/**
* Indicates whether view volume clipping is performed.
*
* @return true
if view volume clipping is performed, otherwise false
(the default).
*/
public boolean isViewClippingEnabled()
{
return viewClippingEnabled;
}
/**
* Set whether view volume clipping is performed.
*
* @param viewClippingEnabled true
if view clipping should be performed, otherwise false
* (the default).
*/
@SuppressWarnings( {"UnusedDeclaration"})
public void setViewClippingEnabled(boolean viewClippingEnabled)
{
this.viewClippingEnabled = viewClippingEnabled;
}
/** Update the contour line according to the current terrain geometry. */
public void update()
{
this.expirySupport.setExpired(true);
}
public List getRenderables()
{
return this.renderables;
}
public void render(DrawContext dc)
{
if (dc == null)
{
String message = Logging.getMessage("nullValue.DrawContextIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (!this.isEnabled())
return;
if (!this.getSector().intersects(dc.getVisibleSector()))
return;
if (!this.isValid(dc))
{
makeContourLine(dc);
this.expirySupport.restart(dc);
this.globeStateKey = dc.getGlobe().getGlobeStateKey(dc);
}
for (Renderable r : this.getRenderables())
{
r.render(dc);
}
}
protected boolean isValid(DrawContext dc)
{
if (this.expirySupport.isExpired(dc))
return false;
return this.globeStateKey != null && this.globeStateKey.equals(dc.getGlobe().getStateKey(dc));
}
/**
* Update the renderable list with appropriate renderables to display the contour line.
*
* @param dc the current DrawContext
.
*/
protected void makeContourLine(DrawContext dc)
{
if (dc == null)
{
String message = Logging.getMessage("nullValue.DrawContextIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
this.getRenderables().clear();
// Get intersection points with terrain
double ve = dc.getVerticalExaggeration();
Intersection[] interArray = dc.getSurfaceGeometry().intersect(this.getElevation() * ve, this.getSector());
if (interArray != null)
{
ArrayList inter = new ArrayList(
Arrays.asList(interArray));
// Filter intersection segment list
if (isViewClippingEnabled())
inter = filterIntersectionsOnViewFrustum(dc, inter);
inter = filterIntersections(dc, inter);
// Create polyline segments
makePolylinesConnected(dc, inter, this.maxConnectingDistance);
}
}
/**
* Filters the given intersection segments list according to the current view frustum.
*
* @param dc the current DrawContext
* @param list the list of Intersection
to be filtered.
*
* @return the filtered list.
*/
protected ArrayList filterIntersectionsOnViewFrustum(DrawContext dc, ArrayList list)
{
Frustum vf = dc.getView().getFrustumInModelCoordinates();
int i = 0;
while (i < list.size())
{
if (vf.contains(list.get(i).getIntersectionPoint())
|| vf.contains(list.get(i + 1).getIntersectionPoint()))
// Keep segment
i += 2;
else
{
// Remove segment
list.remove(i);
list.remove(i);
}
}
return list;
}
/**
* Filters the given intersection segments list according to some criteria - here the inclusion inside the bounding
* sector.
*
* @param dc the current DrawContext
* @param list the list of Intersection
to be filtered.
*
* @return the filtered list.
*/
protected ArrayList filterIntersections(DrawContext dc, ArrayList list)
{
if (getSector().equals(Sector.FULL_SPHERE))
return list;
Globe globe = dc.getGlobe();
Sector s = getSector();
int i = 0;
while (i < list.size())
{
if (s.contains(globe.computePositionFromPoint(list.get(i).getIntersectionPoint()))
&& s.contains(globe.computePositionFromPoint(list.get(i + 1).getIntersectionPoint())))
// Keep segment
i += 2;
else
{
// Remove segment
list.remove(i);
list.remove(i);
}
}
return list;
}
/**
* Add a set of Polyline
objects to the contour line renderable list by connecting as much as possible
* the segments from the given Intersection
array.
*
* @param dc the current DrawContext
.
* @param inter the list of Intersection
to sort out.
* @param tolerance how far in meter can two points be considered connected.
*
* @return the number of Polyline
objects added.
*/
protected int makePolylinesConnected(DrawContext dc, ArrayList inter, int tolerance)
{
if (inter == null)
return 0;
Globe globe = dc.getGlobe();
Vec4 start, end, p;
Polyline line;
int tolerance2 = tolerance * tolerance; // distance squared in meters
int count = 0;
while (inter.size() > 0)
{
ArrayList positions = new ArrayList();
// Start with first segment
start = inter.remove(0).getIntersectionPoint();
end = inter.remove(0).getIntersectionPoint();
positions.add(globe.computePositionFromPoint(start));
positions.add(globe.computePositionFromPoint(end));
// Try to connect remaining segments
for (int i = 0; i < inter.size();)
{
// Try segment start point
p = inter.get(i).getIntersectionPoint();
if (p.distanceToSquared3(start) < tolerance2)
{
// Connect segment to start
inter.remove(i);
start = inter.remove(i).getIntersectionPoint();
positions.add(0, globe.computePositionFromPoint(start));
i = 0;
continue;
}
if (p.distanceToSquared3(end) < tolerance2)
{
// Connect segment to end
inter.remove(i);
end = inter.remove(i).getIntersectionPoint();
positions.add(globe.computePositionFromPoint(end));
i = 0;
continue;
}
// Try segment end point
p = inter.get(i + 1).getIntersectionPoint();
if (p.distanceToSquared3(start) < tolerance2)
{
// Connect segment to start
inter.remove(i + 1);
start = inter.remove(i).getIntersectionPoint();
positions.add(0, globe.computePositionFromPoint(start));
i = 0;
continue;
}
if (p.distanceToSquared3(end) < tolerance2)
{
// Connect segment to end
inter.remove(i + 1);
end = inter.remove(i).getIntersectionPoint();
positions.add(globe.computePositionFromPoint(end));
i = 0;
continue;
}
// Next segment
i += 2;
}
// Create polyline
line = new Polyline(positions);
line.setNumSubsegments(0);
line.setFollowTerrain(true);
line.setColor(this.getColor());
line.setLineWidth(this.getLineWidth());
this.getRenderables().add(line);
count++;
}
return count;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy