package maps.gml; import java.util.List; import java.util.ArrayList; import java.util.Map; import java.util.HashMap; import java.util.Iterator; import java.util.ListIterator; import java.util.Collections; import java.awt.geom.Rectangle2D; import rescuecore2.misc.geometry.Point2D; import rescuecore2.misc.geometry.GeometryTools2D; /** Abstract base class for shapes in GML space. */ public abstract class GMLShape extends GMLObject { private List<GMLDirectedEdge> edges; private Map<GMLDirectedEdge, Integer> neighbours; private List<GMLCoordinates> points; private Rectangle2D bounds; private Point2D centroid; /** Construct a GMLShape. @param id The ID of the shape. */ protected GMLShape(int id) { super(id); this.edges = new ArrayList<GMLDirectedEdge>(); neighbours = new HashMap<GMLDirectedEdge, Integer>(); bounds = null; centroid = null; } /** Construct a GMLShape. @param id The ID of the shape. @param edges The edges of the shape. */ protected GMLShape(int id, List<GMLDirectedEdge> edges) { this(id); this.edges.addAll(edges); points = getUnderlyingCoordinates(); } /** Construct a GMLShape. @param id The ID of the shape. @param edges The edges of the shape. @param neighbours The neighbours of each edge. */ protected GMLShape(int id, List<GMLDirectedEdge> edges, List<Integer> neighbours) { this(id, edges); Iterator<GMLDirectedEdge> it = edges.iterator(); Iterator<Integer> ix = neighbours.iterator(); while (it.hasNext() && ix.hasNext()) { setNeighbour(it.next(), ix.next()); } points = getUnderlyingCoordinates(); } /** Get the edges of this shape. @return The edges. */ public List<GMLDirectedEdge> getEdges() { return new ArrayList<GMLDirectedEdge>(edges); } /** Set the list of edges. @param newEdges The new edge list. */ public void setEdges(List<GMLDirectedEdge> newEdges) { edges.clear(); neighbours.clear(); edges.addAll(newEdges); bounds = null; centroid = null; points = getUnderlyingCoordinates(); } /** Reorder the list of edges. This will not clear the neighbour map or the bounds. @param newEdges The reordered edge list. */ public void reorderEdges(List<GMLDirectedEdge> newEdges) { edges.clear(); edges.addAll(newEdges); points = getUnderlyingCoordinates(); centroid = null; neighbours.keySet().retainAll(newEdges); } /** Replace a GMLDirectedEdge with a set of new edges. The neighbour of the old edge will be set for each of the new edges. @param oldEdge The edge to replace. @param newEdges The new edges. */ public void replaceEdge(GMLDirectedEdge oldEdge, GMLDirectedEdge... newEdges) { ListIterator<GMLDirectedEdge> it = edges.listIterator(); while (it.hasNext()) { if (it.next() == oldEdge) { it.remove(); for (GMLDirectedEdge e : newEdges) { it.add(e); } } } bounds = null; centroid = null; points = getUnderlyingCoordinates(); } /** Remove an edge from this shape. @param edge The underlying edge to remove. */ public void removeEdge(GMLEdge edge) { for (Iterator<GMLDirectedEdge> it = edges.iterator(); it.hasNext();) { GMLDirectedEdge dEdge = it.next(); if (dEdge.getEdge().equals(edge)) { it.remove(); neighbours.remove(dEdge); } } bounds = null; centroid = null; points = getUnderlyingCoordinates(); } /** Get the ID of the neighbour through a particular edge. @param edge The edge to look up. @return The ID of the neighbour through that edge or null. */ public Integer getNeighbour(GMLDirectedEdge edge) { return neighbours.get(edge); } /** Set the ID of the neighbour through a particular edge. @param edge The edge to set the neighbour of. @param neighbour The new neighbour ID for that edge. This may be null. */ public void setNeighbour(GMLDirectedEdge edge, Integer neighbour) { if (neighbour == null) { neighbours.remove(edge); } else { neighbours.put(edge, neighbour); } } /** Find out if an edge has a neighbour. @param edge The edge to look up. @return True if there is a neighbour through that edge or false otherwise. */ public boolean hasNeighbour(GMLDirectedEdge edge) { return neighbours.containsKey(edge); } /** Get the ID of the neighbour through a particular edge. @param edge The edge to look up. @return The ID of the neighbour through that edge or null. */ public Integer getNeighbour(GMLEdge edge) { return getNeighbour(findDirectedEdge(edge)); } /** Set the ID of the neighbour through a particular edge. @param edge The edge to set the neighbour of. @param neighbour The new neighbour ID for that edge. This may be null. */ public void setNeighbour(GMLEdge edge, Integer neighbour) { setNeighbour(findDirectedEdge(edge), neighbour); } /** Find out if an edge has a neighbour. @param edge The edge to look up. @return True if there is a neighbour through that edge or false otherwise. */ public boolean hasNeighbour(GMLEdge edge) { return neighbours.containsKey(findDirectedEdge(edge)); } /** Get the coordinates of the edges that make up this shape. @return The underlying edge coordinates. */ public List<GMLCoordinates> getUnderlyingCoordinates() { List<GMLCoordinates> result = new ArrayList<GMLCoordinates>(); for (GMLDirectedEdge next : edges) { result.add(next.getStartCoordinates()); } return result; } /** Get the nodes of the edges that make up this shape. @return The underlying nodes. */ public List<GMLNode> getUnderlyingNodes() { List<GMLNode> result = new ArrayList<GMLNode>(); for (GMLDirectedEdge next : edges) { result.add(next.getStartNode()); } return result; } /** Get the coordinates of the apexes of this shape. @return The apex coordinates. */ public List<GMLCoordinates> getCoordinates() { return Collections.unmodifiableList(points); } /** Set the coordinates of the apexes of this shape. @param newPoints The new apex coordinates. */ public void setCoordinates(List<GMLCoordinates> newPoints) { points.clear(); points.addAll(newPoints); bounds = null; centroid = null; } /** Get the x coordinate of the centroid of this shape. @return The x coordinate of the centroid. */ public double getCentreX() { return getCentroid().getX(); } /** Get the y coordinate of the centroid of this shape. @return The y coordinate of the centroid. */ public double getCentreY() { return getCentroid().getY(); } /** Get the bounds of this shape. @return The bounds of the shape. */ public Rectangle2D getBounds() { if (bounds == null) { bounds = GMLTools.getBounds(getCoordinates()); } return bounds; } /** Get the centroid of this shape. @return The centroid of the shape. */ public Point2D getCentroid() { if (centroid == null) { centroid = GeometryTools2D.computeCentroid(GMLTools.coordinatesAsPoints(getCoordinates())); } return centroid; } private GMLDirectedEdge findDirectedEdge(GMLEdge e) { for (GMLDirectedEdge next : edges) { if (next.getEdge().equals(e)) { return next; } } throw new IllegalArgumentException(this + ": Edge " + e + " not found"); } }