Package com.bbn.openmap.tools.roads

Source Code of com.bbn.openmap.tools.roads.RoadFinder$RoadVector

/* **********************************************************************
*
*    Use, duplication, or disclosure by the Government is subject to
*        restricted rights as set forth in the DFARS.
*
*          BBN Technologies
*           A Division of
*          BBN Corporation
*         10 Moulton Street
*        Cambridge, MA 02138
*           (617) 873-3000
*
*     Copyright 1998 by BBN Technologies, A Division of
*     BBN Corporation, all rights reserved.
*
* **********************************************************************
*
* $Source: /cvs/distapps/openmap/src/openmap/com/bbn/openmap/tools/roads/RoadFinder.java,v $
* $RCSfile: RoadFinder.java,v $
* $Revision: 1.1.2.5 $
* $Date: 2005/08/12 21:47:05 $
* $Author: dietrick $
*
* **********************************************************************
*/

package com.bbn.openmap.tools.roads;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.PathIterator;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.bbn.openmap.LatLonPoint;
import com.bbn.openmap.event.ProjectionEvent;
import com.bbn.openmap.event.ProjectionListener;
import com.bbn.openmap.omGraphics.OMGeometry;
import com.bbn.openmap.omGraphics.OMLine;
import com.bbn.openmap.omGraphics.OMPoint;
import com.bbn.openmap.omGraphics.OMText;
import com.bbn.openmap.proj.Projection;
import com.bbn.openmap.util.quadtree.QuadTree;

/**
* Gives road access to a shape or vpf layer.
*/
public class RoadFinder implements RoadServices, ProjectionListener, RoadLayer {

    protected RoadClasses roadClasses = new RoadClasses();

    protected RoadClass defaultRoadClass;

    protected LayerView layer;

    protected Intersections intersections = new Intersections();

    protected RoadVector roads = new RoadVector();

    protected Vector removedRoads = new Vector();

    /**
     * how far (in lat-lon space) from lat,lon point to look in quad
     * tree for nearest road *
     */
    protected float halo;

    Logger logger = Logger.getLogger(this.getClass().getName());
    boolean drawIntersections = false;
    boolean drawResults = false;
    boolean doLoopCheck = false;
    protected QuadTree interQuadTree;
    protected Map graphicToRoad;
    boolean doInterp = true;

    /** list of extra graphics to display */
    List toDraw = new ArrayList();
    boolean showLines = true;
    int roadsMade = 0;

    public RoadFinder(LayerView layer, boolean drawIntersections,
            boolean drawResults) {
        initRoadClasses();

        this.drawIntersections = drawIntersections;
        this.drawResults = drawResults;
        logger.info("drawIntersections is " + drawIntersections);
        logger.info("drawResults is " + drawResults);
        this.layer = layer;
    }

    protected void initRoadClasses() {
        roadClasses.put(new RoadClass("1", Color.magenta, 2, 25.0f));
        defaultRoadClass = findRoadClass("1");
    }

    /**
     * Implemented for ProtectionListener
     */
    public void projectionChanged(ProjectionEvent e) {
        try {
            getData();
        } catch (Exception ee) {
            logger.warning("Got exception " + ee);
            ee.printStackTrace();
        }
    }

    /**
     * Take the shape data on the layer and use it to populate our
     * roads and intersections.
     *
     * Clears lists of roads and intersections first, and after
     * calculating the roads, tells the RoadLayer what extra graphics
     * to display, if any.
     */
    protected synchronized void getData() throws Exception {
        logger.info("get Data called.");
        intersections.clear();
        removedRoads.setSize(0);
        roads.clear();
        toDraw.clear();
        getRoads();
        checkIntegrity();
        logger.info("showing " + toDraw.size() + " extra graphics.");
        layer.setExtraGraphics(toDraw);
        halo = 0.05f * (getProjection().getScale() / 20000f);
    }

    /**
     * Take the shape data on the layer and use it to populate our
     * roads and intersections.
     *
     */
    protected void getRoads() throws Exception {
        roadsMade = 0;
        List rectangle = layer.getGraphicList();
        int[] xPoints = new int[1024];
        int[] yPoints = new int[1024];
        interQuadTree = new QuadTree();
        graphicToRoad = new HashMap();

        int height = getProjection().getHeight();
        int width = getProjection().getWidth();
        int skipped = 0;

        synchronized (rectangle) {
            double[] points = new double[6];
            if (logger.isLoggable(Level.INFO))
                logger.info("iterating over rectangle contents.");

            int num = 0;
            int made = 0;
            for (Iterator iter = rectangle.iterator(); iter.hasNext();) {
                double lastXOff = 0;
                double lastYOff = 0;

                num++;
                OMGeometry graphic = (OMGeometry) iter.next();

                if (logger.isLoggable(Level.FINE))
                    logger.fine("examining " + graphic);

                Shape shape = graphic.getShape();
                if (shape == null)
                    continue;

                PathIterator path = shape.getPathIterator(new AffineTransform());
                int segment = 0;
                int itemsInPath = 0;
                boolean pathValid = true;

                for (; !path.isDone() && pathValid; path.next()) {
                    int type = path.currentSegment(points);
                    itemsInPath++;
                    boolean offScreen = false;
                    if (points[0] < 0 || points[0] >= width) {
                        // logger.warning("skipping x point " +
                        // points[0] + " b/c it's off the map.");
                        offScreen = true;
                    }
                    if (points[1] < 0 || points[1] >= height) {
                        // logger.warning("skipping y point " +
                        // points[1] + " b/c it's off the map.");
                        offScreen = true;
                    }

                    switch (type) {
                    case PathIterator.SEG_CLOSE:
                        logger.warning("got close");
                        break;
                    case PathIterator.SEG_CUBICTO:
                        logger.warning("got cubic to");
                        break;
                    case PathIterator.SEG_LINETO:
                        if (offScreen) {
                            if (segment > 0) {
                                // BOZO
                                // should reexamine whether this is
                                // legal - there should be
                                // a one-to-one mapping between
                                // graphic and road object,
                                // but this will throw away the
                                // original entry

                                if (doInterp) {
                                    Point interpPt = interp(xPoints[segment - 1],
                                            yPoints[segment - 1],
                                            points[0],
                                            points[1],
                                            width,
                                            height);
                                    xPoints[segment] = interpPt.x;
                                    yPoints[segment++] = interpPt.y;

                                    makeRoad(shape,
                                            graphic,
                                            made++,
                                            xPoints,
                                            yPoints,
                                            segment);
                                    lastXOff = 0;
                                    lastYOff = 0;
                                    segment = 0;
                                }
                            } else {
                                lastXOff = points[0];
                                lastYOff = points[1];
                            }
                        } else { // onscreen
                            if (lastXOff != 0 || lastYOff != 0) {
                                Point interpPt = interp(points[0],
                                        points[1],
                                        lastXOff,
                                        lastYOff,
                                        width,
                                        height);
                                xPoints[segment] = interpPt.x;
                                yPoints[segment++] = interpPt.y;
                            }

                            xPoints[segment] = (int) points[0];
                            yPoints[segment++] = (int) points[1];
                            lastXOff = 0;
                            lastYOff = 0;
                        }

                        if (logger.isLoggable(Level.FINE))
                            logger.fine(" line to " + points[0] + ", "
                                    + points[1]);

                        break;
                    case PathIterator.SEG_MOVETO:
                        if (offScreen) {
                            lastXOff = points[0];
                            lastYOff = points[1];
                        } else {
                            if (segment == 0) {
                                xPoints[segment] = (int) points[0];
                                yPoints[segment++] = (int) points[1];
                            } else {
                                // we got a second move to in the list
                                // - this is not valid
                                pathValid = false;
                                logger.info("got invalid path.");
                            }

                            lastXOff = 0;
                            lastYOff = 0;
                        }

                        if (logger.isLoggable(Level.FINE))
                            logger.fine(" moving to " + points[0] + ", "
                                    + points[1]);

                        break;
                    case PathIterator.SEG_QUADTO:
                        logger.warning("got quad to");
                        break;
                    default:
                        logger.warning("got another type : " + type);
                        break;
                    }
                }

                if (segment < 2) {
                    skipped++;
                    logger.fine("Skipping line that doesn't have an end point");
                } else {
                    if (logger.isLoggable(Level.INFO))
                        logger.info("items in path " + itemsInPath);

                    makeRoad(shape, graphic, made++, xPoints, yPoints, segment);
                }
                segment = 0;
            }

            if (logger.isLoggable(Level.INFO))
                logger.info("num items " + num + " skipped " + skipped);
        }
    }

    /**
     * find a point between x1,y1 and x2, y2 that is within the
     * visible map
     *
     * @param width of visible map
     * @param height of visible map
     * @return Point between x1,y1 and x2, y2
     */
    protected Point interp(double x1, double y1, double x2, double y2,
                           int width, int height) {
        double deltaY = y2 - y1;
        double deltaX = x2 - x1;
        double slope = deltaY / deltaX;
        double newX = x2;
        double newY = y2;

        if (newX < 0) {
            newX = 0;
            newY = Math.round(slope * (newX - x1) + y1);
        } else if (newX >= width) {
            newX = width - 1;
            newY = Math.round(slope * (newX - x1) + y1);
        }

        if (newY < 0) {
            newY = 0;
            newX = Math.round(x1 + (newY - y1) / slope);
        } else if (newY >= height) {
            newY = height - 1;
            newX = Math.round(x1 + (newY - y1) / slope);
        }

        int intX = (int) newX;
        int intY = (int) newY;

        if (intX < 0) {
            logger.warning("new x is " + intX);
            intX = 0;
        }
        if (intX >= width) {
            logger.warning("new x is " + intX);
            intX = width - 1;
        }
        if (intY < 0) {
            logger.warning("new y is " + intY);
            intY = 0;
        }
        if (intY >= height) {
            logger.warning("new y is " + intY);
            intY = height - 1;
        }

        if (logger.isLoggable(Level.INFO)) {
            logger.info("from " + x1 + "," + y1 + " to " + x2 + "," + y2 + "w "
                    + width + " h " + height + " interp " + intX + "," + intY);
        }

        return new Point(intX, intY);
    }

    /**
     * Makes a road object given the points on the shape that are
     * within the visible box
     *
     * Stores it in a quadTree
     */
    protected void makeRoad(Shape shape, OMGeometry graphic, int num,
                            int[] xPoints, int[] yPoints, int segment) {
        createRoadFromPoints(num, xPoints, yPoints, segment);
    }

    /**
     * Makes a road object given the points on the shape that are
     * within the visible box
     *
     * @param nPoints in the xpoints and ypoints arrays
     */
    protected RoadObject createRoadFromPoints(int id, int[] xpoints,
                                              int[] ypoints, int nPoints) {
        RoadPoint[] roadPoints = new RoadPoint[nPoints - 2];
        Intersection from = findIntersection(xpoints[0], ypoints[0]);
        int fromBefore = from.getRoadCount();
        Intersection to = findIntersection(xpoints[nPoints - 1],
                ypoints[nPoints - 1]);
        int toBefore = to.getRoadCount();

        if (from == null) {
            logger.warning("no from intersection for " + xpoints[0] + ", "
                    + ypoints[0]);
        }
        if (to == null) {
            logger.warning("no to intersection for " + xpoints[nPoints - 1]
                    + ", " + ypoints[nPoints - 1]);
        }

        String name = "road";
        Road road = createRoad(id, name + "-" + id, from, to, defaultRoadClass);
        if (fromBefore + 1 != from.getRoadCount())
            logger.severe("huh? " + from + " had " + fromBefore
                    + " roads before and now " + from.getRoadCount());
        if (toBefore + 1 != to.getRoadCount())
            logger.severe("huh? " + to + " had " + toBefore
                    + " roads before and now " + to.getRoadCount());
        int width = roadsMade % 5;
        roadsMade++;

        if (logger.isLoggable(Level.INFO)) {
            logger.info("road # " + roadsMade + " " + road + " has " + nPoints
                    + " points");
        }

        if (!showLines && drawIntersections) {
            OMPoint point = new YellowPoint(xpoints[0], ypoints[0], 10);
            toDraw.add(point);
        }

        for (int i = 1; i < nPoints - 1; i++) {
            roadPoints[i - 1] = new RoadPoint(road, createLatLonPoint(xpoints[i],
                    ypoints[i]), this);
            if (drawIntersections) {
                if (showLines) {
                    OMLine line = new YellowLine(xpoints[i - 1], ypoints[i - 1], xpoints[i], ypoints[i], width);
                    toDraw.add(line);
                    toDraw.add(new OMText((xpoints[i - 1] - xpoints[i]) / 2
                            + xpoints[i - 1], (ypoints[i - 1] - ypoints[i]) / 2
                            + ypoints[i - 1] - 5, "" + roadsMade, 0));
                } else {
                    OMPoint point = new YellowPoint(xpoints[i], ypoints[i], 10);
                    toDraw.add(point);
                }
            }
        }

        if (drawIntersections) {
            if (showLines) {
                OMLine line = new YellowLine(xpoints[nPoints - 2], ypoints[nPoints - 2], xpoints[nPoints - 1], ypoints[nPoints - 1], width);
                toDraw.add(line);
                toDraw.add(new OMText((xpoints[nPoints - 2] - xpoints[nPoints - 1])
                        / 2 + xpoints[nPoints - 2], (ypoints[nPoints - 2] - ypoints[nPoints - 1])
                        / 2 + ypoints[nPoints - 2] - 5, "" + roadsMade, 0));
                line.addArrowHead(true);
            } else {
                OMPoint point = new YellowPoint(xpoints[nPoints - 1], ypoints[nPoints - 1], 10);
                toDraw.add(point);
            }
        }

        if (to == from && nPoints == 2) {
            deleteRoad(road);
            return null;
        }

        road.setRoadPoints(roadPoints);

        if (!road.getFirstIntersection().equals(from))
            logger.severe("huh? " + road + " first inter "
                    + road.getFirstIntersection() + " not " + from);

        if (!road.getSecondIntersection().equals(to))
            logger.severe("huh? " + road + " second inter "
                    + road.getSecondIntersection() + " not " + to);

        if (road.getPoints().length < 2)
            logger.warning("Error : somehow made a road " + road
                    + " with too few points.");
        else if (logger.isLoggable(Level.INFO)) {
            // logger.info("made " + road);
        }

        return road;
    }

    /** a yellow point for displaying intersections */
    protected class YellowPoint extends OMPoint {
        public YellowPoint(int x, int y, int radius) {
            super(x, y, radius);
        }

        public void render(Graphics g) {
            setGraphicsColor(g, Color.YELLOW);
            draw(g);
        }
    }

    /** a yellow line for display routes between intersections */
    protected class YellowLine extends OMLine {
        int width;

        public YellowLine(int x, int y, int x2, int y2, int width) {
            super(x, y, x2, y2);
            this.width = width;
        }

        public void render(Graphics g) {
            float[] dash1 = new float[width + 1];
            dash1[0] = 10.f;

            for (int i = 1; i < width; i++) {
                dash1[i] = 2.0f;
            }

            BasicStroke dashed = new BasicStroke(5.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10.0f, dash1, 0.0f);
            ((Graphics2D) g).setStroke(dashed);
            setGraphicsColor(g, Color.YELLOW);
            draw(g);
        }
    }

    protected LatLonPoint createLatLonPoint(int x, int y) {
        return getProjection().inverse(x, y);
    }

    protected Intersection findIntersection(LatLonPoint loc, String name) {
        if (name == null)
            return findIntersection(loc);
        Intersection intersection = intersections.get(name);
        if (intersection != null) {
            LatLonPoint foundLoc = intersection.getLocation();
            float distance = (Math.abs(foundLoc.getLatitude()
                    - loc.getLatitude()) + Math.abs(foundLoc.getLongitude()
                    - loc.getLongitude()));
            if (distance * Intersection.GRID > 0.1f) {
                intersection = findIntersection(loc); // Ignore the
                // name, it's
                // too far away.
                System.out.println("Using " + intersection.getName()
                        + " instead of " + name + " distance = " + distance);
                return intersection;
            }
        } else {
            intersection = new Intersection(loc, name, this);
            intersections.put(intersection);
            interQuadTree.put(intersection.getLatitude(),
                    intersection.getLongitude(),
                    intersection);
        }
        return intersection;
    }

    protected Intersection findIntersection(int x, int y) {
        LatLonPoint fromLoc = createLatLonPoint(x, y);
        Intersection from = findIntersection(fromLoc);
        return from;
    }

    protected Intersection findIntersection(LatLonPoint loc) {
        String name = Intersection.getLatLonPointName(loc);
        Intersection intersection = intersections.get(name);
        if (intersection == null) {
            if (logger.isLoggable(Level.FINE))
                logger.fine("making new intersection for " + loc);
            intersection = new Intersection(loc, name, this);
            interQuadTree.put(intersection.getLatitude(),
                    intersection.getLongitude(),
                    intersection);
            intersections.put(intersection);
        } else {
            if (logger.isLoggable(Level.FINE))
                logger.fine("found existing intersection for " + loc + " with "
                        + intersection.getRoadCount()
                        + " roads coming out of it.");
        }
        return intersection;
    }

    protected void deleteIntersection(Intersection intersection) {
        if (intersection.getRoadCount() > 0)
            throw new IllegalArgumentException("Attempt to delete connected intersection");
        intersections.remove(intersection);
    }

    /**
     * called from Intersection Implemented for RoadLayer interface
     */
    public Road createRoad(Intersection from) {
        return createRoad(-1, null, from, null, null);
    }

    protected Road createRoad(int id, String name, Intersection from,
                              Intersection to, RoadClass cl_ss) {
        if (id < 0)
            id = findUnusedRoadID();
        if (name == null)
            name = "Road_" + id;
        if (from == null)
            from = findIntersection(to.getLocation(), to.getName() + ".drag");
        if (to == null)
            to = findIntersection(from.getLocation(), from.getName() + ".drag");
        if (cl_ss == null)
            cl_ss = defaultRoadClass;
        Road road = new Road(id, name, from, to, cl_ss, this);
        road.setModified(true);
        from.addRoad(road);
        to.addRoad(road);
        roads.add(road);
        return road;
    }

    public void deleteRoad(Road road) {
        Intersection intersection1 = road.getFirstIntersection();
        Intersection intersection2 = road.getSecondIntersection();
        intersection1.removeRoad(road);
        intersection2.removeRoad(road);
        if (intersection1.getRoadCount() == 0)
            deleteIntersection(intersection1);
        if (intersection2.getRoadCount() == 0)
            deleteIntersection(intersection2);
        if (intersection1.getRoadCount() == 2
                && intersection1.getRoad(0).getRoadClass() == intersection1.getRoad(1)
                        .getRoadClass())
            joinRoads(intersection1);
        if (intersection2.getRoadCount() == 2
                && intersection2.getRoad(0).getRoadClass() == intersection2.getRoad(1)
                        .getRoadClass())
            joinRoads(intersection2);
        removedRoads.addElement(road);
        roads.remove(road);
    }

    /**
     * Split a road into two roads at one of its corners. An
     * intersection is created where the corner was and the segments
     * before the corner become the segments of the original road. The
     * segments after the corner become the segments of a new road
     * between the new intersection and the
     */
    public Intersection splitRoad(Road road, RoadPoint rp) {
        RoadPoint[] pointsBefore = road.getPointsBefore(rp);
        RoadPoint[] pointsAfter = road.getPointsAfter(rp);
        Intersection newIntersection = findIntersection(rp.getLocation(), null);
        Intersection firstIntersection = road.getFirstIntersection();
        Intersection secondIntersection = road.getSecondIntersection();
        road.setIntersections(firstIntersection, newIntersection);
        road.setRoadPoints(pointsBefore);
        secondIntersection.removeRoad(road);
        newIntersection.addRoad(road);
        Road newRoad = createRoad(-1,
                null,
                newIntersection,
                secondIntersection,
                road.getRoadClass());
        newRoad.setRoadPoints(pointsAfter);
        return newIntersection;
    }

    /**
     * Join two roads into one. The roads must be the only two roads
     * at the intersection and must be of the same class. If the roads
     * are not distinct, then we quietly delete the road and remove
     * the intersection. The roads might not be distinct if they form
     * an isolated loop (such as a racetrack). Thus situation is
     * particularly problematic if the road has no inner points. The
     * RoadPoints of both roads are concatenated with a new RoadPoint
     * where the intersection was between them. This code is a little
     * complicated because the RoadPoints must be assembled in a valid
     * order. The order used is to start from the other intersection
     * of the first road to the given intersection and from the given
     * intersection of the second road to the other intersection of
     * the second road.
     */
    public void joinRoads(Intersection intersection) {
        if (intersection.getRoadCount() != 2)
            throw new IllegalArgumentException("Illegal intersection conversion");
        Road road0 = intersection.getRoad(0);
        Road road1 = intersection.getRoad(1);
        if (road0 == road1) {
            roads.remove(road1);
            intersections.remove(intersection);
            return;
        }
        if (road0.getRoadClass() != road1.getRoadClass())
            throw new IllegalArgumentException("Illegal intersection conversion");
        intersections.remove(intersection);
        roads.remove(road1);
        RoadPoint[] road0Points = road0.getRoadPoints();
        RoadPoint[] road1Points = road1.getRoadPoints();
        RoadPoint[] innerPoints = new RoadPoint[road0Points.length
                + road1Points.length + 1];
        int j = 0;
        Intersection firstIntersection;
        if (intersection == road0.getFirstIntersection()) {
            firstIntersection = road0.getSecondIntersection();
            for (int i = road0Points.length; --i >= 0;)
                innerPoints[j++] = road0Points[i];
        } else {
            firstIntersection = road0.getFirstIntersection();
            System.arraycopy(road0Points, 0, innerPoints, j, road0Points.length);
            j += road0Points.length;
        }
        Intersection otherIntersection = road1.getOtherIntersection(intersection);
        otherIntersection.removeRoad(road1);
        road0.setIntersections(firstIntersection, otherIntersection);
        otherIntersection.addRoad(road0);
        innerPoints[j++] = new RoadPoint(road0, intersection.getLocation(), this);
        if (intersection == road1.getFirstIntersection()) {
            System.arraycopy(road1Points, 0, innerPoints, j, road1Points.length);
            j += road1Points.length;
        } else {
            for (int i = road1Points.length; --i >= 0;)
                innerPoints[j++] = road1Points[i];
        }
        road0.setRoadPoints(innerPoints);
        road0.setName(mergeRoadNames(road0.getName(), road1.getName()));
    }

    protected String mergeRoadNames(String name0, String name1) {
        return name0 + "+" + name1;
    }

    public RoadClass findRoadClass(Object className) {
        RoadClass cl_ss = (RoadClass) roadClasses.get(className);
        if (cl_ss == null)
            return defaultRoadClass;
        return cl_ss;
    }

    public int findUnusedRoadID() {
        return roads.findUnusedID();
    }

    /**
     * Displays a Route between two points on the map.
     * <p>
     *
     * @param start start from start point on map
     * @param end to end point on map
     * @param route the Route to travel from start to end
     * @param segments as side effect, populated with PathSegments
     *        between returned WayPoints
     * @return List of WayPoints
     */
    public List displayPathOnRoad(Point start, Point end, Route route,
                                  List segments) {
        List newPoints;
        try {
            if (route == null) {
                OMPoint point = new RedPoint(start.x, start.y, 5);
                toDraw.add(point);
                point = new RedPoint(end.x, end.y, 5);
                toDraw.add(point);

                return null;
            }

            if (drawResults) {
                OMPoint point = new YellowPoint(start.x, start.y, 10);
                toDraw.add(point);
                point = new YellowPoint(end.x, end.y, 10);
                toDraw.add(point);
            }

            newPoints = new ArrayList();
            populatePointsAndSegments(route, newPoints, segments);

            if (drawResults) {
                Point last = null;
                Point first = null;
                for (Iterator iter = newPoints.iterator(); iter.hasNext();) {
                    Point pt = (Point) iter.next();
                    if (last != null) {
                        OMLine line = new BlueLine(last.x, last.y, pt.x, pt.y);
                        toDraw.add(line);
                    }
                    if (first == null)
                        first = pt;
                    last = pt;
                }

                // draw line from start to beginning intersection
                OMLine line = new YellowLine(start.x, start.y, first.x, first.y, 10);
                toDraw.add(line);
                line = new YellowLine(last.x, last.y, end.x, end.y, 10);
                toDraw.add(line);
            }
        } catch (Exception e) {
            logger.warning("Got exception " + e);
            e.printStackTrace();
            return null;
        }

        return newPoints;

    }

    /**
     * Finds closest intersection to start and end find path from
     * start intersection to end intersection
     * <p>
     *
     * This method works on screen coordinates.
     *
     * @param start from start point on map
     * @param end to end point on map
     * @param segments as side effect, populated with PathSegments
     *        between returned WayPoints
     * @return List of WayPoints
     */
    public List getPathOnRoad(Point start, Point end, List segments) {
        List newPoints;
        try {
            Route bestRoute = getRouteBetweenPoints(start, end);
            newPoints = displayPathOnRoad(start, end, bestRoute, segments);
        } catch (Exception e) {
            logger.warning("Got exception " + e);
            e.printStackTrace();
            return null;
        }

        return newPoints;
    }

    /**
     * a red point for displaying when we can't find a route between
     * two points
     */
    protected class RedPoint extends OMPoint {
        public RedPoint(int x, int y, int radius) {
            super(x, y, radius);
        }

        public void render(Graphics g) {
            setGraphicsColor(g, Color.RED);
            draw(g);
        }
    }

    /** a blue line to indicate the found route */
    protected class BlueLine extends OMLine {
        int width;

        public BlueLine(int x, int y, int x2, int y2) {
            super(x, y, x2, y2);
            this.width = 5;
        }

        public void render(Graphics g) {
            float[] dash1 = new float[width + 1];
            dash1[0] = 10.f;

            for (int i = 1; i < width; i++) {
                dash1[i] = 2.0f;
            }

            BasicStroke dashed = new BasicStroke(5.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10.0f, dash1, 0.0f);
            ((Graphics2D) g).setStroke(dashed);
            setGraphicsColor(g, Color.BLUE);
            draw(g);
        }
    }

    /**
     * Returns best Route between two points specified by latitude and
     * longitude.
     * <p>
     *
     * This method works on latitude/longitude coordinates.
     *
     * @return Route between two points
     */
    public Route getPathOnRoad(LatLonPoint start, LatLonPoint end) {
        Intersection startTemp = findClosestIntersection(start);
        Intersection endTemp = findClosestIntersection(end);

        Route bestRoute = null;

        if (startTemp != null && endTemp != null) {
            if (roadClasses == null)
                logger.warning("huh? road classes is null???");

            bestRoute = Route.getBestRoute(startTemp,
                    endTemp,
                    roadClasses.getBestConvoySpeed(),
                    roadClasses.getWorstConvoySpeed());
        }

        if (bestRoute == null) {
            if (logger.isLoggable(Level.INFO))
                logger.info("no route from " + startTemp + " to " + endTemp);
        } else {
            if (logger.isLoggable(Level.INFO))
                logger.info("route from " + startTemp + " to " + endTemp
                        + " is " + bestRoute);
        }

        // post condition check
        if (logger.isLoggable(Level.INFO) && bestRoute != null) {
            float length = 0;
            for (int i = 0; i < bestRoute.getRoads().length; i++) {
                Road road = bestRoute.getRoads()[i];
                length += road.getLengthInKilometers();
            }

            logger.info("best route from " + bestRoute.getOriginIntersection()
                    + " - start " + start + " to "
                    + bestRoute.getDestinationIntersection() + " - end " + end
                    + " was " + length + " kilometers.");
        }

        return bestRoute;
    }

    /**
     * Returns best Route between two points
     *
     * This method works on latitude/longitude coordinates.
     *
     * @return Route between two points
     */
    public Route getRouteBetweenPoints(Point start, Point end) {
        return getPathOnRoad(createLatLonPoint(start.x, start.y),
                createLatLonPoint(end.x, end.y));
    }

    /**
     * Look in intersection Quad Tree for closest intersection to
     * point x,y
     *
     * @return Intersection closest
     */
    protected Intersection findClosestIntersection(int x, int y) {
        return findClosestIntersection(createLatLonPoint(x, y));
    }

    /**
     * Look in intersection Quad Tree for closest intersection to
     * point at specified latitude and longitude.
     * <p>
     *
     * @return Intersection closest
     */
    protected Intersection findClosestIntersection(LatLonPoint latLon) {
        Intersection inter = (Intersection) interQuadTree.get(latLon.getLatitude(),
                latLon.getLongitude());
        if (inter == null)
            logger.warning("no intersection at " + latLon);

        return inter;
    }

    /**
     * Iterates over route, populating points and segments lists.
     * Worries about sequence order of from and to points, i.e. end of
     * one road should be the start of the next. This is not
     * guaranteed by the route, so we have to check.
     *
     * @param bestRoute route to iterate over.
     * @param newPoints populated with points on the route.
     * @param segments populated with Segments.
     */
    protected void populatePointsAndSegments(Route bestRoute, List newPoints,
                                             List segments) {
        Projection proj = getProjection();

        Intersection origin = bestRoute.getOriginIntersection();
        // Intersection dest = bestRoute.getDestinationIntersection();

        if (logger.isLoggable(Level.INFO))
            logger.info("adding " + bestRoute.roads.length + " new roads.");

        Road road = null;

        Intersection from = origin, to = null;

        Set loopSet = new HashSet();
        if (doLoopCheck)
            loopSet.add(origin);
        Set ptSet = new HashSet();
        for (int i = 0; i < bestRoute.roads.length; i++) {
            road = bestRoute.roads[i];

            if (!from.equals(road.getFirstIntersection())
                    && !from.equals(road.getSecondIntersection())) {
                logger.severe("huh? " + from
                        + " is not an intersection on road " + road);
            }

            Point pt = createPoint(proj.forward(from.getLocation()));

            if (doLoopCheck) {
                if (ptSet.contains(pt)) {
                    logger.warning("pt set has duplicate at " + pt);
                }
                ptSet.add(pt);
            }

            newPoints.add(pt);
            to = road.getOtherIntersection(from);
            if (doLoopCheck) {
                if (loopSet.contains(to)) {
                    logger.warning("road has a cycle at " + to);
                }
                loopSet.add(to);
            }

            // check to see if we need to reverse the order of the
            // road points,
            // which may not be ordered the same as the previous road

            boolean reverse = from.equals(road.getSecondIntersection());
            Segment path = getPathSegment(proj, road, reverse);

            if (logger.isLoggable(Level.INFO))
                logger.info("created path " + path);

            segments.add(path);

            from = to;
        }

        Point pt = createPoint(proj.forward(to.getLocation()));
        if (ptSet.contains(pt)) {
            logger.warning("pt set has duplicate at " + pt);
        }

        newPoints.add(pt);

        if (logger.isLoggable(Level.INFO))
            logger.info(" now " + newPoints.size() + " points and "
                    + segments.size() + " segments.");
    }

    /**
     * Converts a road into a path segment - reverse parameter
     * guarantees the ordering of the points is consistent across
     * multiple path segments in the whole route.
     *
     * @return PathSegment converted from a road
     */
    protected Segment getPathSegment(Projection proj, Road road, boolean reverse) {
        RoadPoint[] roadPoints = road.getRoadPoints();

        List newPoints = new ArrayList();
        if (reverse) {
            for (int i = roadPoints.length - 1; i > 0; i--) {
                newPoints.add(createPoint(proj.forward(roadPoints[i].getLocation())));
            }
        } else {
            for (int i = 0; i < roadPoints.length; i++) {
                newPoints.add(createPoint(proj.forward(roadPoints[i].getLocation())));
            }
        }

        return createSegment(newPoints);
    }

    /**
     * Allows subclasses to redefine segments
     */
    protected Segment createSegment(List newPoints) {
        return new Segment(newPoints);
    }

    /**
     * Allows subclasses to redefine points returned
     */
    protected Point createPoint(Point pt) {
        return new Point(pt);
    }

    public Projection getProjection() {
        return layer.getProjection();
    }

    /**
     * Check the integrity of our data structures.
     *
     * Scan the known intersections. Note intersections with no roads.
     * Scan the roads of the intersection: Each road has two
     * intersections. If the road has already been encountered, then
     * we recorded its "other" intersection and that must match this
     * intersection. If it doesn't match, record an error. If it does
     * match reset its recorded other intersection to be a special
     * marker indicating that both ends of the road have been
     * accounted for. If the road has not already been encountered,
     * then record its "other" intersection. Scan the known roads.
     * Every road should accounted for in the "other" intersection
     * table and should be marked as having both intersections
     * accounted for. Note the roads which were not found in the first
     * scan and the roads which were found, but for which both
     * intersections were not found. Remark every road. Finally scan
     * the other intersection table for entries which were not marked
     * as being in the roads vector.
     */
    protected void checkIntegrity() {
        // CharArrayWriter errorWriter = new CharArrayWriter();
        // PrintWriter errors = new PrintWriter(errorWriter);
        PrintStream errors = System.err;
        Hashtable otherIntersections = new Hashtable();
        Object bothIntersections = new Object();
        Object inRoadsVector = new Object();
        for (Enumeration e = intersections.elements(); e.hasMoreElements();) {
            Intersection intersection = (Intersection) e.nextElement();
            int nRoads = intersection.getRoadCount();
            if (nRoads == 0) {
                errors.println("Dangling intersection");
                errors.println("  Intersection = " + intersection);
                continue;
            }
            for (int i = 0; i < nRoads; i++) {
                Road road = intersection.getRoad(i);
                Object other = otherIntersections.get(road);
                if (other == null) {
                    otherIntersections.put(road,
                            road.getOtherIntersection(intersection));
                } else if (other == intersection) {
                    otherIntersections.put(road, bothIntersections);
                } else {
                    errors.println("Misconnected");
                    errors.println("          Road = " + road);
                    errors.println("    Road.Other = " + other);
                    errors.println("  Intersection = " + intersection);
                }
            }
        }

        for (Enumeration e = roads.elements(); e.hasMoreElements();) {
            Road road = (Road) e.nextElement();
            Object other = otherIntersections.get(road);
            if (other == null) {
                errors.println("Road not found in intersections");
                errors.println("          Road = " + road);
            } else if (other != bothIntersections) {
                errors.println("Road incompletely connected");
                errors.println("          Road = " + road);
                errors.println("    Road.Other = " + other);
            } else if (other == inRoadsVector) {
                errors.println("Road doubly listed");
                errors.println("          Road = " + road);
            }
            otherIntersections.put(road, inRoadsVector);
        }
        for (Enumeration e = otherIntersections.keys(); e.hasMoreElements();) {
            Road road = (Road) e.nextElement();
            Object other = otherIntersections.get(road);
            if (other != inRoadsVector) {
                errors.println("Road not listed");
                errors.println("          Road = " + road);
            }
        }
        // String errString = errorWriter.toString();
        // if (errString.equals(""))
        // return;
        // JTextArea text = new JTextArea(errString);
        // JScrollPane scrollPane = new JScrollPane(text);
        // final JFrame dialog = new JFrame("Errors");
        // JButton ok = new JButton("OK");
        // ok.addActionListener(new ActionListener() {
        // public void actionPerformed(ActionEvent e) {
        // dialog.dispose();
        // }
        // });
        // dialog.getContentPane().add(scrollPane,
        // BorderLayout.CENTER);
        // dialog.getContentPane().add(ok, BorderLayout.SOUTH);
        // dialog.setSize(new java.awt.Dimension(640, 480));
        // dialog.setVisible(true);
    }

    static class RoadVector {
        Road[] roads = new Road[0];
        private int look = 0;
        private int roadCount = 0;

        public void clear() {
            for (int i = 0; i < roads.length; i++)
                roads[i] = null;
            look = 0;
            roadCount = 0;
        }

        public void add(Road r) {
            int id = r.getID();
            if (id >= roads.length) {
                Road[] oldRoads = roads;
                roads = new Road[id + 100 + roads.length];
                System.arraycopy(oldRoads, 0, roads, 0, oldRoads.length);
                for (int i = oldRoads.length; i < roads.length; i++)
                    roads[i] = null;
            }
            if (roads[id] == null)
                roadCount++;
            roads[id] = r;
        }

        public void remove(Road r) {
            int id = r.getID();
            if (roads[id] != null) {
                roads[id] = null;
                if (id < look)
                    look = id;
                --roadCount;
            }
        }

        public int findUnusedID() {
            while (look < roads.length && roads[look] != null) {
                look++;
            }
            return look;
        }

        public Road elementAt(int n) {
            return roads[n];
        }

        public Enumeration elements() {
            return new Enumeration() {
                private int i = 0;

                public boolean hasMoreElements() {
                    for (; i < roads.length; i++) {
                        if (roads[i] != null)
                            return true;
                    }
                    return false;
                }

                public Object nextElement() {
                    return roads[i++];
                }
            };
        }

        public int size() {
            return roadCount;
        }
    }

    public static class Intersections {
        private Hashtable intersections = new Hashtable();

        public void put(Intersection intersection) {
            int suffix = 0;
            String name = intersection.getName();
            while (intersections.containsKey(name)) {
                suffix++;
                name = intersection.getName() + "," + suffix;
            }
            intersection.setName(name);
            intersections.put(name, intersection);
        }

        public void remove(Intersection intersection) {
            intersections.remove(intersection.getName());
        }

        public Intersection get(String name) {
            return (Intersection) intersections.get(name);
        }

        public Enumeration elements() {
            return intersections.elements();
        }

        public boolean contains(Intersection intersection) {
            return intersections.get(intersection.getName()) == intersection;
        }

        public void clear() {
            intersections.clear();
        }

        public int size() {
            return intersections.size();
        }
    };

    public static class RoadClasses extends Hashtable {
        float bestConvoySpeed = 0.0f;
        float worstConvoySpeed = Float.MAX_VALUE;

        public void put(RoadClass roadClass) {
            put(roadClass.getName(), roadClass);
            if (roadClass.getConvoySpeed() > bestConvoySpeed)
                bestConvoySpeed = roadClass.getConvoySpeed();
            if (roadClass.getConvoySpeed() < worstConvoySpeed)
                worstConvoySpeed = roadClass.getConvoySpeed();
        }

        public float getBestConvoySpeed() {
            return bestConvoySpeed;
        }

        public float getWorstConvoySpeed() {
            return worstConvoySpeed;
        }
    };

    /** BOZO remove me */
    public boolean isEditing() {
        return false;
    }
}
TOP

Related Classes of com.bbn.openmap.tools.roads.RoadFinder$RoadVector

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.