Package com.bbn.openmap.geo

Source Code of com.bbn.openmap.geo.ConvexHull$PivotAngleComparator

//**********************************************************************
//
//<copyright>
//
//BBN Technologies, a Verizon Company
//10 Moulton Street
//Cambridge, MA 02138
//(617) 873-8000
//
//Copyright (C) BBNT Solutions LLC. All rights reserved.
//
//</copyright>
//**********************************************************************
//
//$Source:
///cvs/darwars/ambush/aar/src/com/bbn/ambush/mission/MissionHandler.java,v
//$
//$RCSfile: ConvexHull.java,v $
//$Revision: 1.1.2.4 $
//$Date: 2008/04/15 21:51:22 $
//$Author: dietrick $
//
//**********************************************************************

package com.bbn.openmap.geo;

import java.io.Serializable;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Stack;
import java.util.TreeSet;

/**
* This class contains static methods that can be used to create convex hull
* GeoRegions from arrays of Geos. The only algorithm implemented is Graham's,
* where the highest point is selected (called the pivot), the other points are
* sorted according to their relative azimuths from the pivot, and then a path
* is created around the other points. Any right turn encountered traversing the
* points means that point should be skipped when creating the convex hull.
*
* @author dietrick
*/
public class ConvexHull {
    private ConvexHull() {}

    /**
     * Using Graham's scan.
     *
     * @param geos
     * @return GeoRegion outlining the convex hull of the geos
     */
    public static final GeoRegion getRegion(Geo[] geos) {
        Geo[] regionGeos = hull(geos);
        return new GeoRegion.Impl(regionGeos);
    }

    /**
     * Using Graham's scan.
     *
     * @param geos
     * @return a convex hull of the geos
     */
    public static final Geo[] hull(Geo[] geos) {
        return hull(geos, 0);
    }

    /**
     * Using Graham's scan.
     *
     * @param geos
     * @param tolerance the distance between points where they would be
     *        considered equals, in radians.
     * @return a convex hull of the geos
     */
    public static final Geo[] hull(Geo[] geos, double tolerance) {
        Geo pivot = findHighest(geos);
        TreeSet sortedGeos = new TreeSet(new PivotAngleComparator(pivot));
        for (int i = 0; i < geos.length; i++) {
            Geo g = geos[i];
            if (g != pivot) {
                sortedGeos.add(g);
            }
        }

        Stack hullStack = new Stack();
        hullStack.push(pivot);

        Geo gCross, midCross = null;
        Geo geo = null, endGeo = null, midGeo = null;

        Iterator sortedGeoIt = sortedGeos.iterator();
        if (sortedGeoIt.hasNext()) {
            midGeo = (Geo) sortedGeoIt.next();

            while (midGeo.distance(pivot) == 0 && sortedGeoIt.hasNext()) {
                midGeo = (Geo) sortedGeoIt.next();
            }
        }

        Geo lastGeoRead = midGeo;

        while (sortedGeoIt.hasNext() && midGeo != null) {
            geo = (Geo) sortedGeoIt.next();
            double dist = geo.distance(lastGeoRead);
            if (dist <= tolerance) {
                // Debug.output("Skipping duplicate geo");
                continue;
            }

            endGeo = (Geo) hullStack.peek();

            midCross = endGeo.crossNormalize(midGeo);
            gCross = midGeo.crossNormalize(geo);
            Geo i = gCross.crossNormalize(midCross).antipode();

            // Debug.output("Evaluating:\n\tendGeo: " + endGeo + "\n\tmidGeo: "
            // + midGeo + "\n\tto " + geo
            // + "\n ****** intersection point: " + i);

            if (midGeo.distance(i) < Math.PI / 2) {
                // Debug.output("+++++++++++++ midGeo to hull");

                // left turn, OK for hull
                hullStack.push(midGeo);
                endGeo = midGeo;
                midGeo = geo;

            } else {

                // right turn, need to backtrack
                while (hullStack.size() > 1) {

                    // Debug.output("-------- midGeo dropped");

                    midGeo = (Geo) hullStack.pop();
                    endGeo = (Geo) hullStack.peek();

                    midCross = endGeo.crossNormalize(midGeo);
                    gCross = midGeo.crossNormalize(geo);
                    i = gCross.crossNormalize(midCross).antipode();

                    // Debug.output("Evaluating:\n\tendGeo: " + endGeo
                    // + "\n\tmidGeo: " + midGeo + "\n\tto " + geo
                    // + "\n ****** intersection point: " + i);

                    if (midGeo.distance(i) < Math.PI / 2) {

                        // Debug.output("+++++++++++++ midGeo to hull");

                        hullStack.push(midGeo);
                        midGeo = geo;
                        break;
                    }
                }
            }

            lastGeoRead = geo;
        }

        if (midGeo != null) {
            hullStack.push(midGeo);
        }

        hullStack.push(pivot);

        Geo[] regionGeos = new Geo[hullStack.size()];

        int i = 0;
        // Need to reverse order to get inside of poly on the right side of
        // line.
        while (!hullStack.isEmpty()) {
            regionGeos[i++] = (Geo) hullStack.pop();
        }

        return regionGeos;
    }

    protected static Geo findHighest(Geo[] geos) {
        Geo ret = null;
        double highest = Double.NEGATIVE_INFINITY;
        for (int i = 0; i < geos.length; i++) {
            double lat = geos[i].getLatitude();
            if (lat > highest) {
                highest = lat;
                ret = geos[i];
            }
        }
        return ret;
    }

    // XXX: does this need to be serializable?
    protected static final class PivotAngleComparator implements Comparator,
            Serializable {
        private Geo pivot;

        public PivotAngleComparator(Geo pivot) {
            this.pivot = pivot;
        }

        public int compare(Object obj1, Object obj2) {
            double ang1 = Double.MAX_VALUE, ang2 = Double.MAX_VALUE;
            int ret = 0;

            if (obj1 instanceof Geo) {
                ang1 = Math.toDegrees(pivot.azimuth((Geo) obj1));
            }

            if (obj2 instanceof Geo) {
                ang2 = Math.toDegrees(pivot.azimuth((Geo) obj2));
            }

            // ts1 is the one being tested/added to the TreeSet, so we
            // want later items with the same time being added after
            // previous items in the file with the same time.

            if (ang1 < ang2) {
                ret = 1;
            } else if (ang1 >= ang2) {
                ret = -1;
            }

            return ret;
        }

        public Geo getPivot() {
            return pivot;
        }

        public boolean equals(Object obj) {
            if (obj instanceof PivotAngleComparator) {
                return pivot.equals(((PivotAngleComparator) obj).pivot);
            } else {
                return false;
            }
        }

        public int hashCode() {
            return pivot.hashCode();
        }
    }

}
TOP

Related Classes of com.bbn.openmap.geo.ConvexHull$PivotAngleComparator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.