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 + -
显示快捷键?