/* * Copyright 2011 by Mark Coletti, Keith Sullivan, Sean Luke, and * George Mason University Mason University Licensed under the Academic * Free License version 3.0 * * See the file "LICENSE" for more information * * $Id$ * */ package sim.util.geo; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.CoordinateArrays; import com.vividsolutions.jts.geom.LineString; import com.vividsolutions.jts.planargraph.DirectedEdge; import com.vividsolutions.jts.planargraph.Edge; import com.vividsolutions.jts.planargraph.Node; import com.vividsolutions.jts.planargraph.PlanarGraph; import java.util.Iterator; import sim.field.geo.GeomVectorField; import sim.field.network.Network; import sim.util.Bag; /** A JTS PlanarGraph * * Planar graph useful for exploiting network topology. * * @see sim.app.geo.networkworld and sim.app.geo.campusworld * */ public class GeomPlanarGraph extends PlanarGraph { public GeomPlanarGraph() { super(); } /** populate network with lines from a GeomVectorField * * @param field containing line segments * * Assumes that 'field' contains co-planar linear objects * */ public void createFromGeomField(GeomVectorField field) { Bag geometries = field.getGeometries(); for (int i = 0; i < geometries.numObjs; i++) { if (((MasonGeometry) geometries.get(i)).geometry instanceof LineString) { addLineString((MasonGeometry)geometries.get(i)); } } // Abandoned work on rectifying non-planar data. MAC 8/24/10. // field.clear(); // // Collection<LineString> lines = lineMerger.getMergedLineStrings(); // // PrecisionModel pm = new PrecisionModel(PrecisionModel.FLOATING); // // GeometryNoder noder = new GeometryNoder(pm); // // Collection<LineString> more_lines = noder.node(lines); // // // Now add nodes and edges to graph // // for (LineString line : more_lines) // { // addLineString(line); // field.addGeometry(new MasonGeometry(line)); //} } /** Add the given line to the graph * * @param wrappedLine is MasonGeometry wrapping a JTS line * * @note Some code copied from JTS PolygonizeGraph.addEdge() and hacked * to fit */ private void addLineString(MasonGeometry wrappedLine) { LineString line = (LineString) wrappedLine.geometry; if (line.isEmpty()) { return; } Coordinate[] linePts = CoordinateArrays.removeRepeatedPoints(line.getCoordinates()); if (linePts.length < 2) { return; } Coordinate startPt = linePts[0]; Coordinate endPt = linePts[linePts.length - 1]; Node nStart = getNode(startPt); // nodes added as necessary side-effect Node nEnd = getNode(endPt); GeomPlanarGraphEdge edge = new GeomPlanarGraphEdge(line); GeomPlanarGraphDirectedEdge de0 = new GeomPlanarGraphDirectedEdge(nStart, nEnd, linePts[1], true); GeomPlanarGraphDirectedEdge de1 = new GeomPlanarGraphDirectedEdge(nEnd, nStart, linePts[linePts.length - 2], false); edge.setDirectedEdges(de0, de1); edge.setAttributes(wrappedLine.getAttributes()); add(edge); } /** get the node corresponding to the coordinate * * @param startPt * @return graph node associated with point * * Will create a new Node if one does not exist. * * @note Some code copied from JTS PolygonizeGraph.getNode() and hacked to fit */ private Node getNode(Coordinate pt) { Node node = findNode(pt); if (node == null) { node = new Node(pt); // ensure node is only added once to graph add(node); } return node; } /** Create a MASON Network from this planar graph * * XXX Unfortunately we need this since JTS planar graphs do not support * shortest distance and other common graph traversals. */ public Network getNetwork() { Network network = new Network(false); // false == not directed for (Iterator it = dirEdges.iterator(); it.hasNext();) { Object object = it.next(); GeomPlanarGraphDirectedEdge edge = (GeomPlanarGraphDirectedEdge) object; network.addEdge(edge.getFromNode(), edge.getToNode(), edge); } return network; } }