Unverified Commit 342fea6f authored by Juon Kawakami's avatar Juon Kawakami 🥗
Browse files

init

parent 54f6cedf
package maps.convert.osm2gml;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.ListIterator;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import java.util.HashSet;
import java.util.Map;
import java.util.HashMap;
import java.awt.geom.Rectangle2D;
import java.awt.geom.Path2D;
import java.awt.geom.Area;
import java.awt.Shape;
import rescuecore2.misc.geometry.Point2D;
//import rescuecore2.log.Logger;
import maps.gml.GMLCoordinates;
import maps.gml.GMLTools;
/**
Abstract base class for temporary data structures during conversion.
*/
public abstract class TemporaryObject {
private List<DirectedEdge> edges;
private Map<DirectedEdge, TemporaryObject> neighbours;
private Path2D path;
private Rectangle2D bounds;
/**
Construct a new TemporaryObject.
@param edges The edges of the object in counter-clockwise order.
*/
protected TemporaryObject(List<DirectedEdge> edges) {
this.edges = new ArrayList<DirectedEdge>(edges);
neighbours = new HashMap<DirectedEdge, TemporaryObject>();
}
/**
Get the edges of this object.
@return The edges.
*/
public List<DirectedEdge> getEdges() {
return Collections.unmodifiableList(edges);
}
/**
Get the neighbour through a particular edge.
@param edge The edge to look up.
@return The neighbour through that edge or null.
*/
public TemporaryObject getNeighbour(DirectedEdge edge) {
return neighbours.get(edge);
}
/**
Set the neighbour through a particular edge.
@param edge The edge to set the neighbour of.
@param neighbour The new neighbour for that edge.
*/
public void setNeighbour(DirectedEdge edge, TemporaryObject neighbour) {
neighbours.put(edge, neighbour);
}
/**
Set the neighbour through a particular edge.
@param edge The edge to set the neighbour of.
@param neighbour The new neighbour for that edge.
*/
public void setNeighbour(Edge edge, TemporaryObject neighbour) {
neighbours.put(findDirectedEdge(edge), neighbour);
}
/**
Turn the edges into a list of coordinates.
@return A list of GMLCoordinates.
*/
public List<GMLCoordinates> makeGMLCoordinates() {
List<GMLCoordinates> result = new ArrayList<GMLCoordinates>();
for (DirectedEdge next : edges) {
Point2D p = next.getStartCoordinates();
result.add(new GMLCoordinates(p.getX(), p.getY()));
}
return result;
}
/**
Get the bounds of this object.
@return The bounds.
*/
public Rectangle2D getBounds() {
if (bounds == null) {
bounds = GMLTools.getBounds(makeGMLCoordinates());
}
return bounds;
}
/**
Get the Shape of this object.
@return The shape.
*/
public Shape getShape() {
if (path == null) {
path = new Path2D.Double();
Iterator<DirectedEdge> it = edges.iterator();
DirectedEdge d = it.next();
path.moveTo(d.getStartCoordinates().getX(), d.getStartCoordinates().getY());
path.lineTo(d.getEndCoordinates().getX(), d.getEndCoordinates().getY());
while (it.hasNext()) {
d = it.next();
path.lineTo(d.getEndCoordinates().getX(), d.getEndCoordinates().getY());
}
}
return path;
}
/**
Check if this object is a duplicate of another. Objects are duplicates if they contain the same list of directed edges, possibly offset.
@param other The other object to check against.
@return True if this object is a duplicate of other, false otherwise.
*/
public boolean isDuplicate(TemporaryObject other) {
List<DirectedEdge> myEdges = getEdges();
List<DirectedEdge> otherEdges = other.getEdges();
if (myEdges.size() != otherEdges.size()) {
return false;
}
Iterator<DirectedEdge> it = myEdges.iterator();
DirectedEdge start = it.next();
// See if we can find an equivalent edge in other
Iterator<DirectedEdge> ix = otherEdges.iterator();
DirectedEdge otherStart = null;
while (ix.hasNext()) {
DirectedEdge test = ix.next();
if (test.equals(start)) {
// Found!
otherStart = test;
break;
}
}
if (otherStart == null) {
// Edge not found in other so can't be a duplicate
return false;
}
// Check that edges are equivalent
// Walk through the edge lists starting at the beginning for me and at the equivalent edge in other. When we reach the end of other go back to the start.
while (ix.hasNext()) {
DirectedEdge a = it.next();
DirectedEdge b = ix.next();
if (!a.equals(b)) {
return false;
}
}
ix = otherEdges.iterator();
while (it.hasNext()) {
DirectedEdge a = it.next();
DirectedEdge b = ix.next();
if (!a.equals(b)) {
return false;
}
}
return true;
}
/**
Check if this object is a entirely inside another.
@param other The other object to check against.
@return True if this object is entirely inside the other, false otherwise.
*/
public boolean isEntirelyInside(TemporaryObject other) {
if (!this.getBounds().intersects(other.getBounds())) {
return false;
}
Area a = new Area(getShape());
Area b = new Area(other.getShape());
Area intersection = new Area(a);
intersection.intersect(b);
return a.equals(intersection);
}
/**
Replace an edge with a set of replacement edges.
@param edge The edge to replace.
@param replacements The set of replacement edges. These can be in any order.
*/
protected void replaceEdge(Edge edge, Collection<Edge> replacements) {
// Logger.debug(this + " replacing edge " + edge + " with " + replacements);
// Logger.debug("Old edge list: " + edges);
if (replacements.isEmpty()) {
// Just remove the edge
for (Iterator<DirectedEdge> it = edges.iterator(); it.hasNext();) {
DirectedEdge next = it.next();
if (next.getEdge().equals(edge)) {
it.remove();
}
}
}
else {
for (ListIterator<DirectedEdge> it = edges.listIterator(); it.hasNext();) {
DirectedEdge next = it.next();
if (next.getEdge().equals(edge)) {
it.remove();
Set<Edge> replacementsSet = new HashSet<Edge>(replacements);
// Create directed edges for the replacements
Node start = next.getStartNode();
Node end = next.getEndNode();
while (!start.equals(end)) {
DirectedEdge newEdge = findNewEdge(start, replacementsSet);
replacementsSet.remove(newEdge.getEdge());
it.add(newEdge);
start = newEdge.getEndNode();
}
break;
}
}
}
// Logger.debug("New edge list: " + edges);
bounds = null;
path = null;
}
private DirectedEdge findNewEdge(Node from, Set<Edge> candidates) {
for (Edge next : candidates) {
if (next.getStart().equals(from)) {
return new DirectedEdge(next, true);
}
if (next.getEnd().equals(from)) {
return new DirectedEdge(next, false);
}
}
return null;
}
private DirectedEdge findDirectedEdge(Edge e) {
for (DirectedEdge next : edges) {
if (next.getEdge().equals(e)) {
return next;
}
}
throw new IllegalArgumentException("Edge " + e + " not found");
}
}
package maps.convert.osm2gml;
import java.awt.Color;
import java.awt.Shape;
import java.awt.Polygon;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import java.util.List;
import rescuecore2.misc.gui.ScreenTransform;
import rescuecore2.misc.gui.ShapeDebugFrame;
import maps.gml.GMLCoordinates;
/**
A ShapeInfo that knows how to draw TemporaryObjects.
*/
public class TemporaryObjectInfo extends ShapeDebugFrame.ShapeInfo {
private TemporaryObject shape;
private Color outlineColour;
private Color fillColour;
private Rectangle2D bounds;
/**
Create a new TemporaryObjectInfo.
@param shape The shape to draw.
@param name The name of the shape.
@param outlineColour The colour to draw the outline of the shape. This may be null to indicate that the outline should not be painted.
@param fillColour The colour to draw the interior of the shape. This may be null to indicate that the interior should not be painted.
*/
public TemporaryObjectInfo(TemporaryObject shape, String name, Color outlineColour, Color fillColour) {
super(shape, name);
this.shape = shape;
this.outlineColour = outlineColour;
this.fillColour = fillColour;
if (shape != null) {
bounds = shape.getBounds();
}
}
@Override
public Shape paint(Graphics2D g, ScreenTransform transform) {
if (shape == null) {
return null;
}
List<GMLCoordinates> coordinates = shape.makeGMLCoordinates();
int n = coordinates.size();
int[] xs = new int[n];
int[] ys = new int[n];
int i = 0;
for (GMLCoordinates next : coordinates) {
xs[i] = transform.xToScreen(next.getX());
ys[i] = transform.yToScreen(next.getY());
++i;
}
Polygon p = new Polygon(xs, ys, n);
if (fillColour != null) {
g.setColor(fillColour);
g.fill(p);
}
if (outlineColour != null) {
g.setColor(outlineColour);
g.draw(p);
}
return p;
}
@Override
public void paintLegend(Graphics2D g, int width, int height) {
if (outlineColour != null) {
g.setColor(outlineColour);
g.drawRect(0, 0, width - 1, height - 1);
}
if (fillColour != null) {
g.setColor(fillColour);
g.fillRect(0, 0, width, height);
}
}
@Override
public Rectangle2D getBoundsShape() {
return bounds;
}
@Override
public java.awt.geom.Point2D getBoundsPoint() {
return null;
}
}
package maps.convert.osm2gml;
import java.util.List;
/**
A temporary road during conversion.
*/
public class TemporaryRoad extends TemporaryObject {
/**
Construct a new TemporaryRoad.
@param edges The edges of the road in counter-clockwise order.
*/
public TemporaryRoad(List<DirectedEdge> edges) {
super(edges);
}
}
package maps.convert.osm2gml.buildings;
import maps.gml.GMLShape;
import maps.gml.GMLMap;
/**
Interface for objects that know how to fill spaces with buildings.
*/
public interface BuildingSpaceFiller {
/**
Populate a space with buildings.
@param space The space to fill.
@param map The GMLMap to populate.
*/
void createBuildings(GMLShape space, GMLMap map);
}
package maps.convert.osm2gml.buildings;
import maps.gml.GMLShape;
import maps.gml.GMLMap;
import maps.gml.GMLNode;
import maps.gml.GMLDirectedEdge;
import maps.convert.osm2gml.buildings.row.RowFiller;
import maps.convert.osm2gml.buildings.row.RectangularDuplexRowFiller;
import java.util.Random;
import java.util.Comparator;
//import rescuecore2.misc.gui.ShapeDebugFrame;
/**
A BuildingSpaceFiller that fills a space with row housing.
*/
public class RowHousingBuildingSpaceFiller implements BuildingSpaceFiller {
// private ShapeDebugFrame debug;
private double sizeOf1m;
private Random random;
/**
Construct a new RowHousingBuildingSpaceFiller.
@param sizeOf1m The size of 1m in GMLMap units.
@param random The random number generator to use.
*/
public RowHousingBuildingSpaceFiller(double sizeOf1m, Random random/*, ShapeDebugFrame debug*/) {
// this.debug = debug;
this.sizeOf1m = sizeOf1m;
this.random = random;
}
@Override
public void createBuildings(GMLShape space, GMLMap map) {
// Sort the edges of the space by length
/*
List<GMLDirectedEdge> allEdges = space.getEdges();
Collections.sort(allEdges, new EdgeLengthComparator());
Set<GMLFace> newFaces = new HashSet<GMLFace>();
RowFiller filler = createRandomFiller();
for (GMLDirectedEdge next : allEdges) {
Set<GMLFace> edgeFaces = filler.fillRow(next, map);
// debug.show("Next row faces", ConvertTools.createGMLDebugShapes(edgeFaces));
// Remove new faces that overlap with existing ones
for (Iterator<GMLFace> it = edgeFaces.iterator(); it.hasNext();) {
GMLFace newFace = it.next();
boolean good = true;
for (GMLFace testFace : map.getFaces()) {
if (testFace == newFace) {
continue;
}
if (newFace.intersects(testFace)) {
good = false;
break;
}
}
if (good) {
newFaces.add(newFace);
}
else {
map.removeFace(newFace);
it.remove();
}
}
// debug.show("Pruned next row faces", ConvertTools.createGMLDebugShapes(edgeFaces));
}
debug.show("All new faces", ConvertTools.createGMLDebugShapes(newFaces));
*/
}
private RowFiller createRandomFiller() {
if (random.nextBoolean()) {
return RectangularDuplexRowFiller.makeWideFiller(sizeOf1m, random);
}
else {
return RectangularDuplexRowFiller.makeLongFiller(sizeOf1m, random);
}
}
private static final class EdgeLengthComparator implements Comparator<GMLDirectedEdge>, java.io.Serializable {
public int compare(GMLDirectedEdge e1, GMLDirectedEdge e2) {
GMLNode start1 = e1.getStartNode();
GMLNode end1 = e1.getEndNode();
GMLNode start2 = e2.getStartNode();
GMLNode end2 = e2.getEndNode();
double dx1 = end1.getX() - start1.getX();
double dy1 = end1.getY() - start1.getY();
double dx2 = end2.getX() - start2.getX();
double dy2 = end2.getY() - start2.getY();
double l1 = Math.hypot(dx1, dy1);
double l2 = Math.hypot(dx2, dy2);
if (l1 < l2) {
return 1;
}
if (l2 < l1) {
return -1;
}
return 0;
}
}
}
package maps.convert.osm2gml.buildings;
//CHECKSTYLE:OFF:JavadocType|JavadocVariable
public enum RowHousingType {
T_SHAPE_ROW,
T_SHAPE_DUPLEX,
RECTANGULAR_DUPLEX, // Done
THIN_DUPLEX,
SQUARE_DETACHED,
L_SHAPE_DETACHED,
//CHECKSTYLE:ON:JavadocType|JavadocVariable
}
package maps.convert.osm2gml.buildings.row;
import java.util.Set;
import java.util.HashSet;
import java.util.Random;
import maps.gml.GMLDirectedEdge;
import maps.gml.GMLShape;
import maps.gml.GMLMap;
/**
A RowFiller that creates rectangular duplex units.
*/
public final class RectangularDuplexRowFiller implements RowFiller {
private static final double WIDE_LOT_WIDTH_M = 25;
private static final double WIDE_BUILDING_WIDTH_M = 21;
private static final double WIDE_BUILDING_DEPTH_M = 7;
private static final double WIDE_MIN_OFFSET_M = 2;
private static final double WIDE_MAX_OFFSET_M = 10;
private static final double LONG_LOT_WIDTH_M = 18;
private static final double LONG_BUILDING_WIDTH_M = 15;
private static final double LONG_BUILDING_DEPTH_M = 15;
private static final double LONG_MIN_OFFSET_M = 1;
private static final double LONG_MAX_OFFSET_M = 4;
private static final int MIN_RUN_LENGTH = 1;
private static final int MAX_RUN_LENGTH = 5;
private final double lotWidth;
private final double buildingWidth;
private final double buildingDepth;
private final double minOffset;
private final double maxOffset;
private final Random random;
private RectangularDuplexRowFiller(double sizeOf1m, Random random, double lotWidth, double buildingWidth, double buildingDepth, double minOffset, double maxOffset) {
this.lotWidth = lotWidth * sizeOf1m;
this.buildingWidth = buildingWidth * sizeOf1m;
this.buildingDepth = buildingDepth * sizeOf1m;
this.minOffset = minOffset * sizeOf1m;
this.maxOffset = maxOffset * sizeOf1m;
this.random = random;
}
/**
Create a filler that creates wide buildings.
@param sizeOf1m The size of 1m in GML units.
@param random The random number generator to use.
@return A new RectangularDuplexRowFiller.
*/
public static RectangularDuplexRowFiller makeWideFiller(double sizeOf1m, Random random) {
return new RectangularDuplexRowFiller(sizeOf1m, random, WIDE_LOT_WIDTH_M, WIDE_BUILDING_WIDTH_M, WIDE_BUILDING_DEPTH_M, WIDE_MIN_OFFSET_M, WIDE_MAX_OFFSET_M);
}
/**
Create a filler that creates longer, narrower buildings.
@param sizeOf1m The size of 1m in GML units.
@param random The random number generator to use.
@return A new RectangularDuplexRowFiller.
*/
public static RectangularDuplexRowFiller makeLongFiller(double sizeOf1m, Random random) {
return new RectangularDuplexRowFiller(sizeOf1m, random, LONG_LOT_WIDTH_M, LONG_BUILDING_WIDTH_M, LONG_BUILDING_DEPTH_M, LONG_MIN_OFFSET_M, LONG_MAX_OFFSET_M);
}
@Override
public Set<GMLShape> fillRow(GMLDirectedEdge edge, GMLMap map) {
Set<GMLShape> result = new HashSet<GMLShape>();
/*
Line2D edgeLine = ConvertTools.gmlDirectedEdgeToLine(edge);
Vector2D normal = edgeLine.getDirection().getNormal().normalised();
// Create lots along the edge
double edgeLength = edgeLine.getDirection().getLength();
int lots = (int)(edgeLength / LOT_WIDTH);
double trueLotWidth = edgeLength / lots;
System.out.println("Creating " + lots + " lots");
double offset = getRandomOffset();
int runLength = getRandomRunLength();
for (int i = 0; i < lots; ++i) {
if (runLength-- == 0) {
offset = getRandomOffset();
runLength = getRandomRunLength();
}
double d1 = i;
double d2 = i + 1;
d1 /= lots;
d2 /= lots;
Point2D topRight = edgeLine.getPoint(d1);
Point2D topLeft = edgeLine.getPoint(d2);
Set<GMLFace> faces = createBuildingInLot(edgeLine, topRight, topLeft, normal, offset, map);
result.addAll(faces);
}
*/
return result;
}
/*
private Set<GMLFace> createBuildingInLot(Line2D edgeLine, Point2D lotTopRight, Point2D lotTopLeft, Vector2D edgeNormal, double depthOffset, GMLMap map) {
// Create the building by moving in from the sides of the lot boundary
Line2D topLine = new Line2D(lotTopRight, lotTopLeft);
double lotWidth = topLine.getDirection().getLength();
double widthSlack = ((lotWidth - BUILDING_WIDTH) / lotWidth) / 2;
Point2D topRight = topLine.getPoint(widthSlack);
Point2D topMiddle = topLine.getPoint(0.5);
Point2D topLeft = topLine.getPoint(1.0 - widthSlack);
// Offset from the top of the boundary
topRight = topRight.plus(edgeNormal.scale(depthOffset));
topMiddle = topMiddle.plus(edgeNormal.scale(depthOffset));
topLeft = topLeft.plus(edgeNormal.scale(depthOffset));
// Find the other end of the building
Point2D bottomRight = topRight.plus(edgeNormal.scale(BUILDING_DEPTH));
Point2D bottomMiddle = topMiddle.plus(edgeNormal.scale(BUILDING_DEPTH));
Point2D bottomLeft = topLeft.plus(edgeNormal.scale(BUILDING_DEPTH));
// Create new nodes and directed edges for the building
GMLNode n1 = map.ensureNodeNear(topRight);
GMLNode n2 = map.ensureNodeNear(topMiddle);
GMLNode n3 = map.ensureNodeNear(topLeft);
GMLNode n4 = map.ensureNodeNear(bottomLeft);
GMLNode n5 = map.ensureNodeNear(bottomMiddle);
GMLNode n6 = map.ensureNodeNear(bottomRight);
// Create two new buildings
List<GMLDirectedEdge> edges1 = new ArrayList<GMLDirectedEdge>();
// List<GMLDirectedEdge> edges2 = new ArrayList<GMLDirectedEdge>();
// edges1.add(map.ensureDirectedEdge(topRight, topMiddle));
// edges1.add(map.ensureDirectedEdge(topMiddle, bottomMiddle));
// edges1.add(map.ensureDirectedEdge(bottomMiddle, bottomRight));
// edges1.add(map.ensureDirectedEdge(bottomRight, topRight));
// edges2.add(map.ensureDirectedEdge(topMiddle, topLeft));
// edges2.add(map.ensureDirectedEdge(topLeft, bottomLeft));
// edges2.add(map.ensureDirectedEdge(bottomLeft, bottomMiddle));
// edges2.add(map.ensureDirectedEdge(bottomMiddle, topMiddle));
edges1.add(map.ensureDirectedEdge(topRight, topLeft));
edges1.add(map.ensureDirectedEdge(topLeft, bottomLeft));
edges1.add(map.ensureDirectedEdge(bottomLeft, bottomRight));
edges1.add(map.ensureDirectedEdge(bottomRight, topRight));
// TO DO: Make the entrance faces
Set<GMLFace> result = new HashSet<GMLFace>();
result.add(map.createFace(edges1, FaceType.BUILDING));
// result.add(map.createFace(edges2, FaceType.BUILDING));
return result;
}
private double getRandomOffset() {
double d = random.nextDouble();
double range = MAX_OFFSET - MIN_OFFSET;
return MIN_OFFSET + (d * range);
}
private int getRandomRunLength() {
return MIN_RUN_LENGTH + random.nextInt(MAX_RUN_LENGTH - MIN_RUN_LENGTH + 1);
}
*/
}
package maps.convert.osm2gml.buildings.row;
import java.util.Set;
import maps.gml.GMLShape;
import maps.gml.GMLDirectedEdge;
import maps.gml.GMLMap;
/**
Interface for a building generator that works by row.
*/
public interface RowFiller {
/**
Generate buildings along an edge.
@param edge The edge to populate.
@param map The map.
@return The set of new faces.
*/
Set<GMLShape> fillRow(GMLDirectedEdge edge, GMLMap map);
}
package maps.convert.osm2gml.buildings.row;
import java.util.Set;
import java.util.HashSet;
import java.util.Random;
import maps.gml.GMLDirectedEdge;
import maps.gml.GMLShape;
import maps.gml.GMLMap;
/**
A RowFiller that creates long, thin duplex units.
*/
public class ThinDuplexRowFiller implements RowFiller {
private static final double BUILDING_WIDTH_M = 10;
private static final double BUILDING_DEPTH_M = 20;
private static final double MIN_OFFSET_M = 2;
private static final double MAX_OFFSET_M = 3;
private static final int MIN_RUN_LENGTH = 1;
private static final int MAX_RUN_LENGTH = 5;
private final double buildingWidth;
private final double buildingDepth;
private final double minOffset;
private final double maxOffset;
private final Random random;
/**
Construct a ThinDuplexRowFiller.
@param sizeOf1m The size of 1m.
@param random A random number generator.
*/
public ThinDuplexRowFiller(double sizeOf1m, Random random) {
buildingWidth = BUILDING_WIDTH_M * sizeOf1m;
buildingDepth = BUILDING_DEPTH_M * sizeOf1m;
minOffset = MIN_OFFSET_M * sizeOf1m;
maxOffset = MAX_OFFSET_M * sizeOf1m;
this.random = random;
}
@Override
public Set<GMLShape> fillRow(GMLDirectedEdge edge, GMLMap map) {
Set<GMLShape> result = new HashSet<GMLShape>();
/*
Line2D edgeLine = ConvertTools.gmlDirectedEdgeToLine(edge);
Vector2D normal = edgeLine.getDirection().getNormal().normalised();
// Create buildings along the edge until we run out of room
double edgeLength = edgeLine.getDirection().getLength();
double offset = getRandomOffset();
int runLength = getRandomRunLength();
double d = 0;
while (d < 1) {
if (runLength-- == 0) {
offset = getRandomOffset();
runLength = getRandomRunLength();
}
double d1 = d;
double d2 = d + (BUILDING_WIDTH / edgeLength);
Point2D topRight = edgeLine.getPoint(d1);
Point2D topLeft = edgeLine.getPoint(d2);
result.addAll(createBuildingInLot(edgeLine, topRight, topLeft, normal, offset, map));
d = d2;
}
*/
return result;
}
/*
private Set<GMLFace> createBuildingInLot(Line2D edgeLine, Point2D topRight, Point2D topLeft, Vector2D edgeNormal, double depthOffset, GMLMap map) {
// Offset from the top of the boundary
topRight = topRight.plus(edgeNormal.scale(depthOffset));
topLeft = topLeft.plus(edgeNormal.scale(depthOffset));
// Find the other end of the building
Point2D bottomRight = topRight.plus(edgeNormal.scale(BUILDING_DEPTH));
Point2D bottomLeft = topLeft.plus(edgeNormal.scale(BUILDING_DEPTH));
// Create new nodes and directed edges for the lot
GMLNode n1 = map.ensureNodeNear(topRight);
GMLNode n2 = map.ensureNodeNear(topLeft);
GMLNode n3 = map.ensureNodeNear(bottomLeft);
GMLNode n4 = map.ensureNodeNear(bottomRight);
List<GMLDirectedEdge> edges = new ArrayList<GMLDirectedEdge>();
GMLDirectedEdge e1 = map.ensureDirectedEdge(topRight, topLeft);
GMLDirectedEdge e2 = map.ensureDirectedEdge(topLeft, bottomLeft);
GMLDirectedEdge e3 = map.ensureDirectedEdge(bottomLeft, bottomRight);
GMLDirectedEdge e4 = map.ensureDirectedEdge(bottomRight, topRight);
edges.add(e1);
edges.add(e2);
edges.add(e3);
edges.add(e4);
GMLFace buildingFace = map.createFace(edges, FaceType.BUILDING);
// Make the entrance face
Set<GMLFace> result = new HashSet<GMLFace>();
result.add(buildingFace);
return result;
}
private double getRandomOffset() {
double d = random.nextDouble();
double range = MAX_OFFSET - MIN_OFFSET;
return MIN_OFFSET + (d * range);
}
private int getRandomRunLength() {
return MIN_RUN_LENGTH + random.nextInt(MAX_RUN_LENGTH - MIN_RUN_LENGTH + 1);
}
*/
}
package maps.gml;
import java.util.List;
/**
A building in GML space.
*/
public class GMLBuilding extends GMLShape {
private int floors;
private int code;
private int importance;
private int capacity;
/**
Construct a GMLBuilding.
@param id The ID of the building.
*/
public GMLBuilding(int id) {
super(id);
}
/**
Construct a GMLBuilding.
@param id The ID of the building.
@param edges The edges of the building.
*/
public GMLBuilding(int id, List<GMLDirectedEdge> edges) {
super(id, edges);
}
/**
Construct a GMLBuilding.
@param id The ID of the building.
@param edges The edges of the building.
@param neighbours The neighbours of each edge.
*/
public GMLBuilding(int id, List<GMLDirectedEdge> edges, List<Integer> neighbours) {
super(id, edges, neighbours);
}
@Override
public String toString() {
return "GMLBuilding " + getID();
}
/**
Set the capacity of this building.
@param newCapacity The new capacity of the building.
*/
public void setCapacity(int newCapacity) {
capacity = newCapacity;
}
/**
Get the capacity of this building.
@return The the capacity of building.
*/
public int getCapacity() {
return capacity;
}
/**
Set the number of floors in this building.
@param newFloors The new number of floors.
*/
public void setFloors(int newFloors) {
floors = newFloors;
}
/**
Get the number of floors in this building.
@return The number of floors.
*/
public int getFloors() {
return floors;
}
/**
Set the building code of this building.
@param newCode The new building code.
*/
public void setCode(int newCode) {
code = newCode;
}
/**
Get the building code of this building.
@return The building code.
*/
public int getCode() {
return code;
}
/**
Set the importance of this building.
@param newImportance The new importance.
*/
public void setImportance(int newImportance) {
importance = newImportance;
}
/**
Get the importance of this building.
@return The importance.
*/
public int getImportance() {
return importance;
}
}
package maps.gml;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.util.Locale;
/**
A set of GML coordinates. These coordinates are in m.
*/
public class GMLCoordinates {
private static final NumberFormat FORMAT = new DecimalFormat("#0.000", DecimalFormatSymbols.getInstance(Locale.US));
private double x;
private double y;
/**
Create a new GMLCoordinates object.
@param x The X coordinate.
@param y The Y coordinate.
*/
public GMLCoordinates(double x, double y) {
this.x = x;
this.y = y;
}
/**
Copy constructor.
@param other The GMLCoordinates to copy.
*/
public GMLCoordinates(GMLCoordinates other) {
this.x = other.x;
this.y = other.y;
}
/**
Create a new GMLCoordinates object from a String of the form "x,y".
@param s The String to read.
@throws IllegalArgumentException If the string is invalid.
*/
public GMLCoordinates(String s) {
int index = s.indexOf(",");
if (index == -1) {
throw new IllegalArgumentException("'" + s + "' is not of the form 'x,y'");
}
try {
this.x = Double.parseDouble(s.substring(0, index));
this.y = Double.parseDouble(s.substring(index + 1));
}
catch (NumberFormatException e) {
throw new IllegalArgumentException("'" + s + "' is not of the form 'x,y'", e);
}
}
/**
Get the X coordinate.
@return The X coordinate.
*/
public double getX() {
return x;
}
/**
Get the Y coordinate.
@return The y coordinate.
*/
public double getY() {
return y;
}
/**
Set the X coordinate.
@param newX The new X coordinate.
*/
public void setX(double newX) {
x = newX;
}
/**
Set the Y coordinate.
@param newY The new Y coordinate.
*/
public void setY(double newY) {
y = newY;
}
@Override
public String toString() {
StringBuilder result = new StringBuilder();
result.append(FORMAT.format(x));
result.append(",");
result.append(FORMAT.format(y));
return result.toString();
}
}
package maps.gml;
import java.util.List;
import java.util.ArrayList;
/**
A GMLDirectedEdge is an edge with an orientation.
*/
public class GMLDirectedEdge {
private GMLEdge edge;
private boolean forward;
/**
Construct a directed GML edge.
@param edge The underlying edge.
@param forward True if this directed edge is aligned with the underlying edge direction, false otherwise.
*/
public GMLDirectedEdge(GMLEdge edge, boolean forward) {
this.edge = edge;
this.forward = forward;
}
/**
Construct a directed GML edge.
@param edge The underlying edge.
@param start The start node.
*/
public GMLDirectedEdge(GMLEdge edge, GMLNode start) {
this.edge = edge;
this.forward = start.equals(edge.getStart());
}
/**
Get the underlying edge.
@return The underlying edge.
*/
public GMLEdge getEdge() {
return edge;
}
/**
Is this directed edge in the direction of the underlying edge?
@return True if this directed edge is aligned with the underlying edge direction, false otherwise.
*/
public boolean isForward() {
return forward;
}
/**
Reverse the direction of this edge.
*/
public void reverse() {
forward = !forward;
}
/**
Get the node at the start of the underlying edge.
@return The start node.
*/
public GMLNode getStartNode() {
return forward ? edge.getStart() : edge.getEnd();
}
/**
Get the node at the end of the underlying edge.
@return The end node.
*/
public GMLNode getEndNode() {
return forward ? edge.getEnd() : edge.getStart();
}
/**
Get the points of the underlying edge in the right order for this directed edge.
@return The points of the underlying edge in the right order.
*/
public List<GMLCoordinates> getPoints() {
List<GMLCoordinates> result = new ArrayList<GMLCoordinates>();
result.add(getStartNode().getCoordinates());
result.add(getEndNode().getCoordinates());
return result;
}
/**
Get the coordinates of the start of this edge.
@return The coordinates of the start of this edge.
*/
public GMLCoordinates getStartCoordinates() {
if (forward) {
return edge.getStart().getCoordinates();
}
else {
return edge.getEnd().getCoordinates();
}
}
/**
Get the coordinates of the end of this edge.
@return The coordinates of the end of this edge.
*/
public GMLCoordinates getEndCoordinates() {
if (forward) {
return edge.getEnd().getCoordinates();
}
else {
return edge.getStart().getCoordinates();
}
}
@Override
public String toString() {
return "GMLDirectedEdge" + (forward ? "" : " backwards") + " along " + edge;
}
@Override
public int hashCode() {
return edge.hashCode();
}
@Override
public boolean equals(Object o) {
if (o instanceof GMLDirectedEdge) {
GMLDirectedEdge e = (GMLDirectedEdge)o;
return this.forward == e.forward && this.edge.equals(e.edge);
}
return false;
}
}
package maps.gml;
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
/**
A GML edge. An edge is a line between two nodes.
*/
public class GMLEdge extends GMLObject {
private GMLNode start;
private GMLNode end;
private boolean passable;
private List<GMLCoordinates> points;
/**
Construct a new GMLEdge.
@param id The ID of this object.
@param start The start node.
@param end The end node.
@param passable True if this directed edge is passable.
*/
public GMLEdge(int id, GMLNode start, GMLNode end, boolean passable) {
super(id);
this.start = start;
this.end = end;
this.passable = passable;
points = new ArrayList<GMLCoordinates>();
points.add(start.getCoordinates());
points.add(end.getCoordinates());
}
/**
Get the points along the edge.
@return The coordinates along the edge.
*/
public List<GMLCoordinates> getPoints() {
return Collections.unmodifiableList(points);
}
/**
Set the points along the edge.
@param newPoints The new coordinates along the edge.
*/
public void setPoints(List<GMLCoordinates> newPoints) {
points.clear();
points.addAll(newPoints);
}
/**
Get the start node.
@return The start node.
*/
public GMLNode getStart() {
return start;
}
/**
Set the start node.
@param s The new start node.
*/
public void setStart(GMLNode s) {
start = s;
}
/**
Get the end node.
@return The end node.
*/
public GMLNode getEnd() {
return end;
}
/**
Set the end node.
@param e The new end node.
*/
public void setEnd(GMLNode e) {
end = e;
}
/**
Find out if this edge is passable.
@return True iff the edge is passable.
*/
public boolean isPassable() {
return passable;
}
/**
Set the passable flag on this edge.
@param b The new passable flag.
*/
public void setPassable(boolean b) {
passable = b;
}
@Override
public String toString() {
StringBuilder result = new StringBuilder();
result.append("GMLEdge ");
result.append(getID());
result.append(" from ");
result.append(start);
result.append(" to ");
result.append(end);
if (!passable) {
result.append(" (impassable)");
}
return result.toString();
}
}
package maps.gml;
/**
Exceptions related to GML.
*/
public class GMLException extends Exception {
/**
Construct a GMLException with no error message.
*/
public GMLException() {
super();
}
/**
Construct a GMLException with an error message.
@param msg The error message.
*/
public GMLException(String msg) {
super(msg);
}
/**
Construct a GMLException with an underlying cause.
@param cause The cause.
*/
public GMLException(Throwable cause) {
super(cause);
}
/**
Construct a GMLException with an error message and underlying cause.
@param msg The error message.
@param cause The cause.
*/
public GMLException(String msg, Throwable cause) {
super(msg, cause);
}
}
package maps.gml;
import java.util.Map;
import java.util.HashMap;
import java.util.Collection;
import java.util.Collections;
import java.util.Arrays;
import java.util.Set;
import java.util.HashSet;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
import maps.CoordinateConversion;
import rescuecore2.misc.geometry.Point2D;
import rescuecore2.misc.geometry.Line2D;
import rescuecore2.misc.geometry.GeometryTools2D;
import rescuecore2.misc.collections.LazyMap;
/**
A GML map. All coordinates are specified in m.
*/
public class GMLMap implements maps.Map {
private double minX;
private double maxX;
private double minY;
private double maxY;
private boolean boundsKnown;
private Map<Integer, GMLNode> nodes;
private Map<Integer, GMLEdge> edges;
private Map<Integer, GMLBuilding> buildings;
private Map<Integer, GMLRoad> roads;
private Map<Integer, GMLSpace> spaces;
private Set<GMLShape> allShapes;
private Set<GMLObject> allObjects;
private Map<GMLNode, Collection<GMLEdge>> attachedEdges;
private Map<GMLEdge, Collection<GMLShape>> attachedShapes;
private int nextID;
/**
Construct an empty GML map.
*/
public GMLMap() {
nodes = new HashMap<Integer, GMLNode>();
edges = new HashMap<Integer, GMLEdge>();
buildings = new HashMap<Integer, GMLBuilding>();
roads = new HashMap<Integer, GMLRoad>();
spaces = new HashMap<Integer, GMLSpace>();
allShapes = new HashSet<GMLShape>();
allObjects = new HashSet<GMLObject>();
attachedEdges = new LazyMap<GMLNode, Collection<GMLEdge>>() {
@Override
public Collection<GMLEdge> createValue() {
return new HashSet<GMLEdge>();
}
};
attachedShapes = new LazyMap<GMLEdge, Collection<GMLShape>>() {
@Override
public Collection<GMLShape> createValue() {
return new HashSet<GMLShape>();
}
};
boundsKnown = false;
nextID = 0;
}
/**
Create a new GMLNode.
@param x The X coordinate of the node in m.
@param y The Y coordinate of the node in m.
@return A new GMLNode with a unique ID.
*/
public GMLNode createNode(double x, double y) {
GMLNode n = new GMLNode(nextID++, x, y);
addNode(n);
return n;
}
/**
Create a new GMLNode.
@param coords The coordinates of the node.
@return A new GMLNode with a unique ID.
*/
public GMLNode createNode(GMLCoordinates coords) {
GMLNode n = new GMLNode(nextID++, coords);
addNode(n);
return n;
}
/**
Create a set of new GMLNodes.
@param coords The coordinates of the new nodes.
@return A set of new GMLNodes with unique IDs.
*/
public List<GMLNode> createNodes(List<GMLCoordinates> coords) {
List<GMLNode> result = new ArrayList<GMLNode>(coords.size());
for (GMLCoordinates c : coords) {
GMLNode n = new GMLNode(nextID++, c);
addNode(n);
result.add(n);
}
return result;
}
/**
Create a set of new GMLNodes.
@param coords The coordinates of the new nodes.
@return A set of new GMLNodes with unique IDs.
*/
public List<GMLNode> createNodesFromPoints(List<Point2D> coords) {
List<GMLNode> result = new ArrayList<GMLNode>(coords.size());
for (Point2D p : coords) {
GMLNode n = new GMLNode(nextID++, p.getX(), p.getY());
addNode(n);
result.add(n);
}
return result;
}
/**
Create a new GMLEdge between two nodes.
@param first The 'start' node.
@param second The 'end' node.
@return A new GMLEdge with a unique ID.
*/
public GMLEdge createEdge(GMLNode first, GMLNode second) {
GMLEdge e = new GMLEdge(nextID++, first, second, false);
addEdge(e);
return e;
}
/**
Create new GMLBuilding.
@param apexes The apexes of the building.
@return A new GMLBuilding with a unique ID.
*/
public GMLBuilding createBuildingFromNodes(List<GMLNode> apexes) {
return createBuilding(apexesToEdges(apexes));
}
/**
Create new GMLBuilding.
@param bEdges The edges of the building.
@return A new GMLBuilding with a unique ID.
*/
public GMLBuilding createBuilding(List<GMLDirectedEdge> bEdges) {
GMLBuilding b = new GMLBuilding(nextID++, bEdges);
b.setFloors(1);
b.setCode(0);
b.setImportance(1);
b.setCapacity(0);//todo
addBuilding(b);
return b;
}
/**
Create new GMLRoad.
@param apexes The apexes of the road.
@return A new GMLRoad with a unique ID.
*/
public GMLRoad createRoadFromNodes(List<GMLNode> apexes) {
return createRoad(apexesToEdges(apexes));
}
/**
Create new GMLRoad.
@param rEdges The edges of the road.
@return A new GMLRoad with a unique ID.
*/
public GMLRoad createRoad(List<GMLDirectedEdge> rEdges) {
GMLRoad r = new GMLRoad(nextID++, rEdges);
addRoad(r);
return r;
}
/**
Create new GMLSpace.
@param apexes The apexes of the space.
@return A new GMLSpace with a unique ID.
*/
public GMLSpace createSpaceFromNodes(List<GMLNode> apexes) {
return createSpace(apexesToEdges(apexes));
}
/**
Create new GMLSpace.
@param sEdges The edges of the space.
@return A new GMLSpace with a unique ID.
*/
public GMLSpace createSpace(List<GMLDirectedEdge> sEdges) {
GMLSpace s = new GMLSpace(nextID++, sEdges);
addSpace(s);
return s;
}
/**
Add a node.
@param n The node to add.
*/
public void addNode(GMLNode n) {
if (nodes.containsKey(n.getID())) {
return;
}
addObject(n);
nodes.put(n.getID(), n);
boundsKnown = false;
}
/**
Add an edge. The edge's nodes will also be added if required.
@param e The edge to add.
*/
public void addEdge(GMLEdge e) {
if (edges.containsKey(e.getID())) {
return;
}
addObject(e);
edges.put(e.getID(), e);
addNode(e.getStart());
addNode(e.getEnd());
attachedEdges.get(e.getStart()).add(e);
attachedEdges.get(e.getEnd()).add(e);
}
/**
Add a building. The building's edges will be added if required.
@param b The building to add.
*/
public void addBuilding(GMLBuilding b) {
if (buildings.containsKey(b.getID())) {
return;
}
addShape(b);
buildings.put(b.getID(), b);
}
/**
Add a road. The road's edges will be added if required.
@param r The road to add.
*/
public void addRoad(GMLRoad r) {
if (roads.containsKey(r.getID())) {
return;
}
addShape(r);
roads.put(r.getID(), r);
}
/**
Add a space. The space's edges will be added if required.
@param s The space to add.
*/
public void addSpace(GMLSpace s) {
if (spaces.containsKey(s.getID())) {
return;
}
addShape(s);
spaces.put(s.getID(), s);
}
/**
Add an object.
@param object The object to add.
*/
public void add(GMLObject object) {
if (object instanceof GMLNode) {
addNode((GMLNode)object);
}
else if (object instanceof GMLEdge) {
addEdge((GMLEdge)object);
}
else if (object instanceof GMLRoad) {
addRoad((GMLRoad)object);
}
else if (object instanceof GMLBuilding) {
addBuilding((GMLBuilding)object);
}
else if (object instanceof GMLSpace) {
addSpace((GMLSpace)object);
}
else {
throw new IllegalArgumentException("Don't know how to add " + object + " (class: " + object.getClass().getName() + ")");
}
}
/**
Add a set of objects.
@param objects The objects to add.
*/
public void add(Collection<? extends GMLObject> objects) {
for (GMLObject next : objects) {
add(next);
}
}
/**
Add a set of objects.
@param objects The objects to add.
*/
public void add(GMLObject... objects) {
for (GMLObject next : objects) {
add(next);
}
}
/**
Remove a node, any edges attached to the node and any shapes attached to those edges.
@param n The node to remove.
@return All removed objects, not including n.
*/
public Collection<GMLObject> removeNode(GMLNode n) {
Collection<GMLObject> result = new HashSet<GMLObject>();
if (nodes.containsKey(n.getID())) {
removeObject(n);
nodes.remove(n.getID());
Collection<GMLEdge> attached = new HashSet<GMLEdge>(getAttachedEdges(n));
for (GMLEdge next : attached) {
result.add(next);
result.addAll(removeEdge(next));
}
boundsKnown = false;
}
return result;
}
/**
Remove an edge and any attached shapes.
@param e The edge to remove.
@return All removed objects, not including e.
*/
public Collection<GMLObject> removeEdge(GMLEdge e) {
Collection<GMLObject> result = new HashSet<GMLObject>();
if (edges.containsKey(e.getID())) {
removeObject(e);
edges.remove(e.getID());
Collection<GMLShape> attached = new HashSet<GMLShape>(getAttachedShapes(e));
for (GMLShape next : attached) {
result.add(next);
remove(next);
}
attachedEdges.get(e.getStart()).remove(e);
attachedEdges.get(e.getEnd()).remove(e);
}
return result;
}
/**
Remove a building.
@param b The building to remove.
*/
public void removeBuilding(GMLBuilding b) {
if (buildings.containsKey(b.getID())) {
removeShape(b);
buildings.remove(b.getID());
}
}
/**
Remove a road.
@param r The road to remove.
*/
public void removeRoad(GMLRoad r) {
if (roads.containsKey(r.getID())) {
removeShape(r);
roads.remove(r.getID());
}
}
/**
Remove a space.
@param s The space to remove.
*/
public void removeSpace(GMLSpace s) {
if (spaces.containsKey(s.getID())) {
removeShape(s);
spaces.remove(s.getID());
}
}
/**
Remove an object.
@param object The object to remove.
*/
public void remove(GMLObject object) {
if (object instanceof GMLNode) {
removeNode((GMLNode)object);
}
else if (object instanceof GMLEdge) {
removeEdge((GMLEdge)object);
}
else if (object instanceof GMLRoad) {
removeRoad((GMLRoad)object);
}
else if (object instanceof GMLBuilding) {
removeBuilding((GMLBuilding)object);
}
else if (object instanceof GMLSpace) {
removeSpace((GMLSpace)object);
}
else {
throw new IllegalArgumentException("Don't know how to remove " + object + " (class: " + object.getClass().getName() + ")");
}
}
/**
Remove a set of objects.
@param objects The objects to remove.
*/
public void remove(Collection<? extends GMLObject> objects) {
for (GMLObject next : objects) {
remove(next);
}
}
/**
Remove a set of objects.
@param objects The objects to remove.
*/
public void remove(GMLObject... objects) {
for (GMLObject next : objects) {
remove(next);
}
}
/**
Remove all nodes, edges and shapes.
*/
public void removeAllNodes() {
nodes.clear();
edges.clear();
roads.clear();
buildings.clear();
spaces.clear();
allShapes.clear();
allObjects.clear();
attachedEdges.clear();
attachedShapes.clear();
boundsKnown = false;
}
/**
Remove all edges and shapes.
*/
public void removeAllEdges() {
edges.clear();
roads.clear();
buildings.clear();
spaces.clear();
allShapes.clear();
allObjects.retainAll(nodes.values());
attachedEdges.clear();
attachedShapes.clear();
}
/**
Remove all buildings.
*/
public void removeAllBuildings() {
for (Map.Entry<GMLEdge, Collection<GMLShape>> entry : attachedShapes.entrySet()) {
entry.getValue().removeAll(buildings.values());
}
allShapes.removeAll(buildings.values());
allObjects.removeAll(buildings.values());
buildings.clear();
}
/**
Remove all roads.
*/
public void removeAllRoads() {
for (Map.Entry<GMLEdge, Collection<GMLShape>> entry : attachedShapes.entrySet()) {
entry.getValue().removeAll(buildings.values());
}
allShapes.removeAll(roads.values());
allObjects.removeAll(roads.values());
roads.clear();
}
/**
Remove all spaces.
*/
public void removeAllSpaces() {
for (Map.Entry<GMLEdge, Collection<GMLShape>> entry : attachedShapes.entrySet()) {
entry.getValue().removeAll(buildings.values());
}
allShapes.removeAll(spaces.values());
allObjects.removeAll(spaces.values());
spaces.clear();
}
/**
Get a node by ID.
@param id The ID to look up.
@return The node with that ID or null if the ID is not found.
*/
public GMLNode getNode(int id) {
return nodes.get(id);
}
/**
Get an edge by ID.
@param id The ID to look up.
@return The edge with that ID or null if the ID is not found.
*/
public GMLEdge getEdge(int id) {
return edges.get(id);
}
/**
Get a building by ID.
@param id The ID to look up.
@return The building with that ID or null if the ID is not found.
*/
public GMLBuilding getBuilding(int id) {
return buildings.get(id);
}
/**
Get a road by ID.
@param id The ID to look up.
@return The road with that ID or null if the ID is not found.
*/
public GMLRoad getRoad(int id) {
return roads.get(id);
}
/**
Get a space by ID.
@param id The ID to look up.
@return The space with that ID or null if the ID is not found.
*/
public GMLSpace getSpace(int id) {
return spaces.get(id);
}
/**
Get a shape by ID.
@param id The ID to look up.
@return The shape with that ID or null if the ID is not found.
*/
public GMLShape getShape(int id) {
GMLBuilding b = getBuilding(id);
if (b != null) {
return b;
}
GMLRoad r = getRoad(id);
if (r != null) {
return r;
}
GMLSpace s = getSpace(id);
if (s != null) {
return s;
}
return null;
}
/**
Get an object by ID.
@param id The ID to look up.
@return The object with that ID or null if the ID is not found.
*/
public GMLObject getObject(int id) {
GMLNode n = getNode(id);
if (n != null) {
return n;
}
GMLEdge e = getEdge(id);
if (e != null) {
return e;
}
GMLBuilding b = getBuilding(id);
if (b != null) {
return b;
}
GMLRoad r = getRoad(id);
if (r != null) {
return r;
}
GMLSpace s = getSpace(id);
if (s != null) {
return s;
}
return null;
}
/**
Get all nodes in the map.
@return All nodes.
*/
public Set<GMLNode> getNodes() {
return new HashSet<GMLNode>(nodes.values());
}
/**
Get all edges in the map.
@return All edges.
*/
public Set<GMLEdge> getEdges() {
return new HashSet<GMLEdge>(edges.values());
}
/**
Get all buildings in the map.
@return All buildings.
*/
public Set<GMLBuilding> getBuildings() {
return new HashSet<GMLBuilding>(buildings.values());
}
/**
Get all roads in the map.
@return All roads.
*/
public Set<GMLRoad> getRoads() {
return new HashSet<GMLRoad>(roads.values());
}
/**
Get all spaces in the map.
@return All spaces.
*/
public Set<GMLSpace> getSpaces() {
return new HashSet<GMLSpace>(spaces.values());
}
/**
Get all shapes in the map.
@return All shapes.
*/
public Set<GMLShape> getAllShapes() {
return Collections.unmodifiableSet(allShapes);
}
/**
Get all objects in the map.
@return All objects.
*/
public Set<GMLObject> getAllObjects() {
return Collections.unmodifiableSet(allObjects);
}
/**
Get the minimum x coordinate.
@return The minimum x coordinate.
*/
public double getMinX() {
calculateBounds();
return minX;
}
/**
Get the maximum x coordinate.
@return The maximum x coordinate.
*/
public double getMaxX() {
calculateBounds();
return maxX;
}
/**
Get the minimum y coordinate.
@return The minimum y coordinate.
*/
public double getMinY() {
calculateBounds();
return minY;
}
/**
Get the maximum y coordinate.
@return The maximum y coordinate.
*/
public double getMaxY() {
calculateBounds();
return maxY;
}
/**
Find out if this map has a real size or not. Maps with zero or one nodes do not have a real size.
@return True if this map has two or more nodes.
*/
public boolean hasSize() {
return nodes.size() > 1;
}
/**
Rescale the map coordinates.
@param conversion The coordinate conversion to apply.
*/
public void convertCoordinates(CoordinateConversion conversion) {
for (GMLNode next : nodes.values()) {
next.convert(conversion);
}
boundsKnown = false;
}
/**
Create or retrieve an existing edge between two nodes.
@param first The 'start' node.
@param second The 'end' node.
@return A new GMLEdge with a unique ID or an existing edge. The returned edge may be reversed with respect to first and second.
*/
public GMLEdge ensureEdge(GMLNode first, GMLNode second) {
for (GMLEdge next : edges.values()) {
if ((next.getStart().equals(first) && next.getEnd().equals(second))
|| (next.getStart().equals(second) && next.getEnd().equals(first))
) {
return next;
}
}
return createEdge(first, second);
}
/**
Turn a list of apexes into a list of directed edges.
@param apexes The apexes to convert.
@return A list of directed edges.
*/
public List<GMLDirectedEdge> apexesToEdges(GMLNode... apexes) {
return apexesToEdges(Arrays.asList(apexes));
}
/**
Turn a list of apexes into a list of directed edges.
@param apexes The apexes to convert.
@return A list of directed edges.
*/
public List<GMLDirectedEdge> apexesToEdges(List<GMLNode> apexes) {
List<GMLDirectedEdge> edgeList = new ArrayList<GMLDirectedEdge>(apexes.size());
Iterator<GMLNode> it = apexes.iterator();
GMLNode first = it.next();
GMLNode previous = first;
while (it.hasNext()) {
GMLNode next = it.next();
GMLEdge edge = ensureEdge(previous, next);
edgeList.add(new GMLDirectedEdge(edge, previous));
previous = next;
}
GMLEdge edge = ensureEdge(previous, first);
edgeList.add(new GMLDirectedEdge(edge, previous));
return edgeList;
}
/**
Get all GMLNodes inside a region.
@param xMin The lower X bound of the region.
@param yMin The lower Y bound of the region.
@param xMax The upper X bound of the region.
@param yMax The upper Y bound of the region.
@return All GMLNodes inside the region.
*/
public Collection<GMLNode> getNodesInRegion(double xMin, double yMin, double xMax, double yMax) {
Collection<GMLNode> result = new ArrayList<GMLNode>();
for (GMLNode next : nodes.values()) {
double x = next.getX();
double y = next.getY();
if (x >= xMin && x <= xMax && y >= yMin && y <= yMax) {
result.add(next);
}
}
return result;
}
/**
Find the GMLNode nearest a point.
@param x The X coordinate.
@param y The Y coordinate.
@return The nearest GMLNode.
*/
public GMLNode findNearestNode(double x, double y) {
GMLNode best = null;
double bestDistance = Double.NaN;
for (GMLNode next : nodes.values()) {
double dx = x - next.getX();
double dy = y - next.getY();
double d = (dx * dx) + (dy * dy);
if (best == null || d < bestDistance) {
best = next;
bestDistance = d;
}
}
return best;
}
/**
Find the GMLEdge nearest a point.
@param x The X coordinate.
@param y The Y coordinate.
@return The nearest GMLEdge.
*/
public GMLEdge findNearestEdge(double x, double y) {
return findNearestEdge(x, y, edges.values());
}
/**
Find the GMLEdge nearest a point from a set of possible edges.
@param x The X coordinate.
@param y The Y coordinate.
@param possible The set of possible edges.
@return The nearest GMLEdge.
*/
public GMLEdge findNearestEdge(double x, double y, Collection<? extends GMLEdge> possible) {
GMLEdge best = null;
double bestDistance = Double.NaN;
Point2D test = new Point2D(x, y);
for (GMLEdge next : possible) {
Line2D line = GMLTools.toLine(next);
Point2D closest = GeometryTools2D.getClosestPointOnSegment(line, test);
double d = GeometryTools2D.getDistance(test, closest);
if (best == null || d < bestDistance) {
best = next;
bestDistance = d;
}
}
return best;
}
/**
Find the GMLShape under a point.
@param x The X coordinate.
@param y The Y coordinate.
@return The shape under the point or null if no shapes are found.
*/
public GMLShape findShapeUnder(double x, double y) {
for (GMLShape next : allShapes) {
if (GMLTools.coordsToShape(next.getUnderlyingCoordinates()).contains(x, y)) {
return next;
}
}
return null;
}
/**
Get all GMLEdges that are attached to a GMLNode.
@param node The GMLNode to look up.
@return All attached GMLEdges.
*/
public Collection<GMLEdge> getAttachedEdges(GMLNode node) {
return Collections.unmodifiableCollection(attachedEdges.get(node));
}
/**
Get all GMLShapes that are attached to a GMLEdge.
@param edge The GMLEdge to look up.
@return All attached GMLShapes.
*/
public Collection<GMLShape> getAttachedShapes(GMLEdge edge) {
return Collections.unmodifiableCollection(attachedShapes.get(edge));
}
/**
Merge a pair of edges and form a new edge. The node and existing edges are not removed from the map.
@param edge1 The first edge.
@param edge2 The second edge.
@return The new edge.
*/
public GMLEdge mergeEdges(GMLEdge edge1, GMLEdge edge2) {
GMLNode commonNode = edge1.getStart();
if (!commonNode.equals(edge2.getStart()) && !commonNode.equals(edge2.getEnd())) {
commonNode = edge1.getEnd();
}
if (!commonNode.equals(edge2.getStart()) && !commonNode.equals(edge2.getEnd())) {
throw new IllegalArgumentException("Edges " + edge1 + " and " + edge2 + " do not have a common node");
}
GMLNode start = commonNode.equals(edge1.getStart()) ? edge1.getEnd() : edge1.getStart();
GMLNode end = commonNode.equals(edge2.getStart()) ? edge2.getEnd() : edge2.getStart();
return ensureEdge(start, end);
}
/**
Replace all references to a node with another node. This does not delete the old node from the map.
@param oldNode The node to replace.
@param newNode The new node.
*/
public void replaceNode(GMLNode oldNode, GMLNode newNode) {
List<GMLEdge> attached = new ArrayList<GMLEdge>(getAttachedEdges(oldNode));
for (GMLEdge next : attached) {
if (next.getStart().equals(oldNode)) {
next.setStart(newNode);
attachedEdges.get(oldNode).remove(next);
attachedEdges.get(newNode).add(next);
}
if (next.getEnd().equals(oldNode)) {
next.setEnd(newNode);
attachedEdges.get(oldNode).remove(next);
attachedEdges.get(newNode).add(next);
}
}
}
/**
Replace all references to an edge with another edge. This does not delete the old edge from the map. The two edges must have the same pair of start and end nodes but may be in different directions.
@param oldEdge The edge to replace.
@param newEdge The new edge.
*/
public void replaceEdge(GMLEdge oldEdge, GMLEdge newEdge) {
if ((oldEdge.getStart() != newEdge.getStart() && oldEdge.getStart() != newEdge.getEnd())
|| (oldEdge.getEnd() != newEdge.getStart() && oldEdge.getEnd() != newEdge.getEnd())) {
throw new IllegalArgumentException("oldEdge and newEdge do not share start and end nodes");
}
Collection<GMLShape> attached = new HashSet<GMLShape>(getAttachedShapes(oldEdge));
for (GMLShape next : attached) {
for (GMLDirectedEdge dEdge : next.getEdges()) {
if (dEdge.getEdge() == oldEdge) {
boolean forward;
if (oldEdge.getStart() == newEdge.getStart()) {
forward = dEdge.isForward();
}
else {
forward = !dEdge.isForward();
}
GMLDirectedEdge replacement = new GMLDirectedEdge(newEdge, forward);
next.replaceEdge(dEdge, replacement);
attachedShapes.get(oldEdge).remove(next);
attachedShapes.get(newEdge).add(next);
}
}
}
}
/**
Insert a node into an edge. This method updates attached edges but does not delete the old edge.
@param edge The edge to split.
@param node The node to insert.
@return The two new edges.
*/
public Collection<GMLEdge> splitEdge(GMLEdge edge, GMLNode node) {
Collection<GMLEdge> result = new ArrayList<GMLEdge>(2);
GMLEdge first = ensureEdge(edge.getStart(), node);
GMLEdge second = ensureEdge(node, edge.getEnd());
result.add(first);
result.add(second);
// Update any attached edges
Collection<GMLShape> attached = new HashSet<GMLShape>(getAttachedShapes(edge));
for (GMLShape shape : attached) {
for (GMLDirectedEdge dEdge : shape.getEdges()) {
if (dEdge.getEdge() == edge) {
// Create two new directed edges
GMLDirectedEdge d1;
GMLDirectedEdge d2;
if (dEdge.isForward()) {
d1 = new GMLDirectedEdge(first, true);
d2 = new GMLDirectedEdge(second, true);
}
else {
d1 = new GMLDirectedEdge(second, false);
d2 = new GMLDirectedEdge(first, false);
}
shape.replaceEdge(dEdge, d1, d2);
attachedShapes.get(edge).remove(shape);
attachedShapes.get(first).add(shape);
attachedShapes.get(second).add(shape);
}
}
}
return result;
}
private void addShape(GMLShape shape) {
addObject(shape);
allShapes.add(shape);
for (GMLDirectedEdge edge : shape.getEdges()) {
addEdge(edge.getEdge());
attachedShapes.get(edge.getEdge()).add(shape);
}
}
private void addObject(GMLObject object) {
allObjects.add(object);
nextID = Math.max(nextID, object.getID() + 1);
}
private void removeShape(GMLShape shape) {
removeObject(shape);
allShapes.remove(shape);
for (GMLDirectedEdge edge : shape.getEdges()) {
attachedShapes.get(edge.getEdge()).remove(shape);
}
}
private void removeObject(GMLObject object) {
allObjects.remove(object);
}
private void calculateBounds() {
if (boundsKnown) {
return;
}
minX = Double.POSITIVE_INFINITY;
minY = Double.POSITIVE_INFINITY;
maxX = Double.NEGATIVE_INFINITY;
maxY = Double.NEGATIVE_INFINITY;
for (GMLNode n : nodes.values()) {
GMLCoordinates c = n.getCoordinates();
minX = Math.min(minX, c.getX());
maxX = Math.max(maxX, c.getX());
minY = Math.min(minY, c.getY());
maxY = Math.max(maxY, c.getY());
}
boundsKnown = true;
}
}
package maps.gml;
import java.io.File;
import java.io.Reader;
import java.io.FileReader;
import java.io.FileOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.DocumentException;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.dom4j.io.OutputFormat;
import maps.MapFormat;
import maps.MapException;
import maps.Map;
import rescuecore2.log.Logger;
/**
Abstract base class for map formats that use GML.
*/
public abstract class GMLMapFormat implements MapFormat {
@Override
public GMLMap read(File file) throws MapException {
FileReader r;
try {
r = new FileReader(file);
}
catch (FileNotFoundException e) {
throw new MapException(e);
}
try {
return read(r);
}
catch (DocumentException e) {
throw new MapException(e);
}
finally {
try {
r.close();
}
catch (IOException e) {
Logger.warn("IOException while closing file reader", e);
}
}
}
@Override
public void write(Map map, File file) throws MapException {
if (map == null) {
throw new IllegalArgumentException("Map must not be null");
}
if (file == null) {
throw new IllegalArgumentException("File must not be null");
}
if (!(map instanceof GMLMap)) {
throw new IllegalArgumentException("Map is not a GMLMap: " + map.getClass().getName());
}
Document doc = write((GMLMap)map);
try {
if (!file.exists()) {
File parent = file.getParentFile();
if (!parent.exists()) {
if (!file.getParentFile().mkdirs()) {
throw new MapException("Couldn't create file " + file.getPath());
}
}
if (!file.createNewFile()) {
throw new MapException("Couldn't create file " + file.getPath());
}
}
XMLWriter writer = new XMLWriter(new FileOutputStream(file), OutputFormat.createPrettyPrint());
Element root = doc.getRootElement();
for (java.util.Map.Entry<String, String> next : getNamespaces().entrySet()) {
root.addNamespace(next.getKey(), next.getValue());
}
writer.write(doc);
writer.flush();
writer.close();
}
catch (IOException e) {
throw new MapException(e);
}
}
@Override
public boolean canRead(File file) throws MapException {
if (file.isDirectory() || !file.exists()) {
return false;
}
if (!file.getName().endsWith(".gml")) {
return false;
}
// Check that the XML dialect is correct by looking at the root element.
FileReader r;
try {
r = new FileReader(file);
}
catch (FileNotFoundException e) {
throw new MapException(e);
}
try {
XMLStreamReader reader = XMLInputFactory.newInstance().createXMLStreamReader(r);
while (reader.hasNext()) {
if (reader.next() == XMLStreamConstants.START_ELEMENT) {
return isCorrectRootElement(reader.getNamespaceURI(), reader.getLocalName());
}
}
}
catch (XMLStreamException e) {
Logger.debug("Exception while reading XML stream", e);
return false;
}
finally {
try {
r.close();
}
catch (IOException e) {
Logger.warn("IOException while closing file reader", e);
}
}
return false;
}
/**
Read a GMLMap from a Reader.
@param reader The Reader to read.
@return A new GMLMap.
@throws DocumentException If there is a problem parsing the XML.
@throws MapException If there is a problem reading the map.
*/
public GMLMap read(Reader reader) throws DocumentException, MapException {
Logger.debug("Parsing GML");
SAXReader saxReader = new SAXReader();
Document doc = saxReader.read(reader);
Logger.debug("Building map");
return read(doc);
}
/**
Find out if the root element is correct for this format type.
@param uri The URI of the root element.
@param localName The local name of the root element.
@return True if the uri and localName are correct for this format's root element, false otherwise.
*/
protected abstract boolean isCorrectRootElement(String uri, String localName);
/**
Read a Document and return a GMLMap.
@param doc The document to read.
@return A new GMLMap.
@throws MapException If there is a problem reading the map.
*/
protected abstract GMLMap read(Document doc) throws MapException;
/**
Turn a GMLMap into an xml document.
@param map The map to write.
@return A new document.
*/
protected abstract Document write(GMLMap map);
/**
Get the uris and preferred prefixes for all namespaces this format cares about.
@return A map from prefix to uri for all relevant namespaces.
*/
protected abstract java.util.Map<String, String> getNamespaces();
}
package maps.gml;
import maps.CoordinateConversion;
/**
A GML node object.
*/
public class GMLNode extends GMLObject {
private GMLCoordinates coordinates;
/**
Construct a new GML node.
@param id The ID of this node.
@param x The x coordinate of this node.
@param y The y coordinate of this node.
*/
public GMLNode(int id, double x, double y) {
this(id, new GMLCoordinates(x, y));
}
/**
Construct a new GML node.
@param id The ID of this node.
@param coordinates The coordinates of this node.
*/
public GMLNode(int id, GMLCoordinates coordinates) {
super(id);
this.coordinates = coordinates;
}
/**
Get the coordinates of this node.
@return The node coordinates.
*/
public GMLCoordinates getCoordinates() {
return coordinates;
}
/**
Set the coordinates of this node.
@param c The new coordinates.
*/
public void setCoordinates(GMLCoordinates c) {
if (c == null) {
throw new IllegalArgumentException("Coordinates cannot be null");
}
this.coordinates = c;
}
/**
Get the X coordinate.
@return The X coordinate.
*/
public double getX() {
return coordinates.getX();
}
/**
Get the Y coordinate.
@return The Y coordinate.
*/
public double getY() {
return coordinates.getY();
}
/**
Apply a CoordinateConversion to this node.
@param c The conversion to apply.
*/
public void convert(CoordinateConversion c) {
double oldX = coordinates.getX();
double oldY = coordinates.getY();
double newX = c.convertX(oldX);
double newY = c.convertY(oldY);
coordinates = new GMLCoordinates(newX, newY);
}
@Override
public String toString() {
return "GMLNode " + getID() + " at " + coordinates;
}
}
package maps.gml;
/**
A GML map object.
*/
public abstract class GMLObject {
private int id;
/**
Construct a GML object.
@param id The id of the object.
*/
protected GMLObject(int id) {
this.id = id;
}
/**
Get this object's ID.
@return The object ID.
*/
public int getID() {
return id;
}
@Override
public int hashCode() {
return (int)id;
}
@Override
public boolean equals(Object o) {
if (o instanceof GMLObject) {
return this.id == ((GMLObject)o).id;
}
return false;
}
}
package maps.gml;
import java.util.List;
public class GMLRefuge extends GMLBuilding {
private int bedCapacity;
private int refillCapacity;
/**
* Construct a GMLRefuge.
*
* @param id
* The ID of the Refuge.
*/
public GMLRefuge( int id ) {
super( id );
}
/**
* Construct a GMLRefuge.
*
* @param id
* The ID of the Refuge.
* @param edges
* The edges of the Refuge.
*/
public GMLRefuge( int id, List<GMLDirectedEdge> edges ) {
super( id, edges );
}
/**
* Construct a GMLRefuge.
*
* @param id
* The ID of the Refuge.
* @param edges
* The edges of the Refuge.
* @param neighbours
* The neighbours of each edge.
*/
public GMLRefuge( int id, List<GMLDirectedEdge> edges, List<Integer> neighbours ) {
super( id, edges, neighbours );
}
@Override
public String toString() {
return "GMLRefuge " + getID();
}
/**
* Set the bed capacity of this Refuge.
*
* @param newCapacity
* The new bed capacity of the Refuge.
*/
public void setBedCapacity( int newCapacity ) {
bedCapacity = newCapacity;
}
/**
* Get the bed capacity of this Refuge.
*
* @return The the bed capacity of Refuge.
*/
public int getBedCapacity() {
return bedCapacity;
}
/**
* Set the refill capacity of this Refuge.
*
* @param newCapacity
* The new refill capacity of the Refuge.
*/
public void setRefillCapacity( int newCapacity ) {
refillCapacity = newCapacity;
}
/**
* Get the refill capacity of this Refuge.
*
* @return The the refill capacity of Refuge.
*/
public int getRefillCapacity() {
return refillCapacity;
}
}
package maps.gml;
import java.util.List;
/**
A road in GML space.
*/
public class GMLRoad extends GMLShape {
/**
Construct a GMLRoad.
@param id The ID of the road.
*/
public GMLRoad(int id) {
super(id);
}
/**
Construct a GMLRoad.
@param id The ID of the road.
@param edges The edges of the road.
*/
public GMLRoad(int id, List<GMLDirectedEdge> edges) {
super(id, edges);
}
/**
Construct a GMLRoad.
@param id The ID of the road.
@param edges The edges of the road.
@param neighbours The neighbours of each edge.
*/
public GMLRoad(int id, List<GMLDirectedEdge> edges, List<Integer> neighbours) {
super(id, edges, neighbours);
}
@Override
public String toString() {
return "GMLRoad " + getID();
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment