areameasurer.java
来自「world wind java sdk 源码」· Java 代码 · 共 554 行 · 第 1/2 页
JAVA
554 行
package gov.nasa.worldwind.util.measure;
import gov.nasa.worldwind.geom.*;
import gov.nasa.worldwind.globes.Globe;
import gov.nasa.worldwind.util.Logging;
import gov.nasa.worldwind.util.GeometryBuilder;
import java.util.ArrayList;
/**
* Utility class to compute approximations of projected and surface (terrain following) area on a globe.
*
* <p>To properly compute surface area the measurer must be provided with a list of positions that describe a
* closed path - one which last position is equal to the first.</p>
*
* <p>Segments which are longer then the current maxSegmentLength will be subdivided along lines following the current
* pathType - Polyline.LINEAR, Polyline.RHUMB_LINE or Polyline.GREAT_CIRCLE.</p>
*
* <p>Projected or non terrain following area is computed in a sinusoidal projection which is equivalent or equal area.
* Surface or terrain following area is approximated by sampling the path bounding sector with square cells along a
* grid. Cells which center is inside the path have their area estimated and summed according to the overall slope
* at the cell south-west corner.</p>
*
* @author Patrick Murris
* @version $Id: AreaMeasurer.java 7504 2008-11-11 20:51:38Z patrickmurris $
* @see MeasureTool
* @see LengthMeasurer
*/
public class AreaMeasurer extends LengthMeasurer implements MeasurableArea
{
private static final double DEFAULT_AREA_SAMPLING_STEPS = 32; // sampling grid max rows or cols
private ArrayList<? extends Position> subdividedPositions;
private Cell[][] sectorCells;
private Double[][] sectorElevations;
private double areaTerrainSamplingSteps = DEFAULT_AREA_SAMPLING_STEPS;
protected double surfaceArea = -1;
protected double projectedArea = -1;
public AreaMeasurer()
{
}
public AreaMeasurer(ArrayList<? extends Position> positions)
{
super(positions);
}
protected void clearCachedValues()
{
super.clearCachedValues();
this.subdividedPositions = null;
this.projectedArea = -1;
this.surfaceArea = -1;
}
public void setPositions(ArrayList<? extends Position> positions)
{
Sector oldSector = getBoundingSector();
super.setPositions(positions); // will call clearCachedData()
if (getBoundingSector() == null || !getBoundingSector().equals(oldSector))
{
this.sectorCells = null;
this.sectorElevations = null;
}
}
/**
* Get the sampling grid maximum number of rows or columns for terrain following surface area approximation.
*
* @return the sampling grid maximum number of rows or columns.
*/
public double getAreaTerrainSamplingSteps()
{
return this.areaTerrainSamplingSteps;
}
/**
* Set the sampling grid maximum number of rows or columns for terrain following surface area approximation.
*
* @param steps the sampling grid maximum number of rows or columns.
*/
public void setAreaTerrainSamplingSteps(double steps)
{
if (steps < 1)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", steps);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (this.areaTerrainSamplingSteps != steps)
{
this.areaTerrainSamplingSteps = steps;
this.surfaceArea = -1;
this.projectedArea = -1;
// Invalidate cached data
this.sectorCells = null;
this.sectorElevations = null;
}
}
/**
* Get the surface area approximation for the current path or shape.
*
* <p>If the measurer is set to follow terrain, the computed area will account for terrain deformations. Otherwise
* the area is that of the path once projected at sea level - elevation zero.</p>
*
* @param globe the globe to draw terrain information from.
* @return the current shape surface area or -1 if the position list does not describe a closed path or is too short.
*/
public double getArea(Globe globe)
{
return this.isFollowTerrain() ? getSurfaceArea(globe) : getProjectedArea(globe);
}
public double getSurfaceArea(Globe globe)
{
if (globe == null)
{
String message = Logging.getMessage("nullValue.GlobeIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (this.surfaceArea < 0)
this.surfaceArea = this.computeSurfaceAreaSampling(globe, this.areaTerrainSamplingSteps);
return this.surfaceArea;
}
public double getProjectedArea(Globe globe)
{
if (globe == null)
{
String message = Logging.getMessage("nullValue.GlobeIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (this.projectedArea < 0)
this.projectedArea = this.computeProjectedAreaGeometry(globe);
return this.projectedArea;
}
public double getPerimeter(Globe globe)
{
return getLength(globe);
}
public double getWidth(Globe globe)
{
if (globe == null)
{
String message = Logging.getMessage("nullValue.GlobeIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
Sector sector = getBoundingSector();
if (sector != null)
return globe.getRadiusAt(sector.getCentroid()) * sector.getDeltaLon().radians
* Math.cos(sector.getCentroid().getLatitude().radians);
return -1;
}
public double getHeight(Globe globe)
{
if (globe == null)
{
String message = Logging.getMessage("nullValue.GlobeIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
Sector sector = getBoundingSector();
if (sector != null)
return globe.getRadiusAt(sector.getCentroid()) * sector.getDeltaLat().radians;
return -1;
}
// Test for even/odd number of intersections with a constant latitude line going through the point.
// From Randolph Franklin see http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
// and http://local.wasp.uwa.edu.au/~pbourke/geometry/insidepoly/
// TODO: this is only valid for linear path type
/**
* Returns true if a given location (LatLon) is located inside a given polygon
*
* @param point the location
* @param positions the list of positions describing the polygon. Last one should be the same as the first one.
* @return true if the location is inside the polygon.
*/
public static boolean isLocationInside(LatLon point, ArrayList<? extends LatLon> positions)
{
if (point == null)
{
String message = Logging.getMessage("nullValue.LatLonIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
boolean result = false;
LatLon p1 = positions.get(0);
for (int i = 1; i < positions.size(); i++)
{
LatLon p2 = positions.get(i);
// Developped for clarity
// double lat = point.getLatitude().degrees;
// double lon = point.getLongitude().degrees;
// double lat1 = p1.getLatitude().degrees;
// double lon1 = p1.getLongitude().degrees;
// double lat2 = p2.getLatitude().degrees;
// double lon2 = p2.getLongitude().degrees;
// if ( ((lat2 <= lat && lat < lat1) || (lat1 <= lat && lat < lat2))
// && (lon < (lon1 - lon2) * (lat - lat2) / (lat1 - lat2) + lon2) )
// result = !result;
if ( ((p2.getLatitude().degrees <= point.getLatitude().degrees
&& point.getLatitude().degrees < p1.getLatitude().degrees) ||
(p1.getLatitude().degrees <= point.getLatitude().degrees
&& point.getLatitude().degrees < p2.getLatitude().degrees))
&& (point.getLongitude().degrees < (p1.getLongitude().degrees - p2.getLongitude().degrees)
* (point.getLatitude().degrees - p2.getLatitude().degrees)
/ (p1.getLatitude().degrees - p2.getLatitude().degrees) + p2.getLongitude().degrees) )
result = !result;
p1 = p2;
}
return result;
}
// *** Computing area ******************************************************************
private class Cell
{
Sector sector;
double projectedArea, surfaceArea;
public Cell(Sector sector, double projected, double surface)
{
this.sector = sector;
this.projectedArea = projected;
this.surfaceArea = surface;
}
}
// *** Projected area ***
// Tessellate the path in lat-lon space, then sum each triangle area.
protected double computeProjectedAreaGeometry(Globe globe)
{
Sector sector = getBoundingSector();
if (sector != null && this.isClosedShape())
{
// Subdivide long segments if needed
if (this.subdividedPositions == null)
this.subdividedPositions = subdividePositions(globe, getPositions(), getMaxSegmentLength()
, isFollowTerrain(), getPathType());
// First: tessellate polygon
int verticesCount = this.subdividedPositions.size() - 1; // trim last pos which is same as first
float[] verts = new float[verticesCount * 3];
// Prepare vertices
int idx = 0;
for (int i = 0; i < verticesCount; i++)
{
// Vertices coordinates are x=lon y=lat in radians, z = elevation zero
verts[idx++] = (float)this.subdividedPositions.get(i).getLongitude().radians;
verts[idx++] = (float)this.subdividedPositions.get(i).getLatitude().radians;
verts[idx++] = 0f;
}
// Tessellate
GeometryBuilder gb = new GeometryBuilder();
GeometryBuilder.IndexedTriangleArray ita = gb.tessellatePolygon2(0, verticesCount, verts);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?