package maps.validate; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Queue; import java.util.Set; import maps.gml.GMLBuilding; import maps.gml.GMLDirectedEdge; import maps.gml.GMLMap; import maps.gml.GMLRoad; import maps.gml.GMLShape; /** * Checks if a GML map is fully connected and if all individual shaped are * correctly connected to each other (i.e. there are no dangling or no one-way * connections). */ public class GMLConnectivityValidator implements MapValidator<GMLMap> { private GMLMap map = null; @Override public Collection<ValidationError> validate(GMLMap mmap) { this.map = mmap; List<ValidationError> errors = new LinkedList<ValidationError>(); // Check if all shapes are connected correctly (no dangling connections, // etc...) Set<GMLShape> toBeChecked = new HashSet<GMLShape>(); for (GMLShape shape : map.getAllShapes()) { errors.addAll(validateShape(shape)); if (shape instanceof GMLBuilding || shape instanceof GMLRoad) { toBeChecked.add(shape); } } Queue<GMLShape> open = new LinkedList<GMLShape>(); GMLShape first = toBeChecked.iterator().next(); open.add(first); // check for connectivity (only simple connectivity needs to be checked, // as // we made sure that there are no one-way connections while (!open.isEmpty()) { GMLShape next = open.remove(); toBeChecked.remove(next); for (GMLShape n : getNeigbours(next)) { if (toBeChecked.contains(n)) { open.add(n); } if (!(n instanceof GMLBuilding || n instanceof GMLRoad)) { String message = "Can reach non-building, non-road shape " + n.getID(); errors.add(new ValidationError(next.getID(), message)); } } } if (!toBeChecked.isEmpty()) { for (GMLShape unreachable : toBeChecked) { String message = "The map is not fully connected. Shape cannot be reached from " + first.getID(); errors.add(new ValidationError(unreachable.getID(), message)); } } return errors; } /** * Check if all connections to neighbours are reflexive. * * @param shape * @return */ private Collection<ValidationError> validateShape(GMLShape shape) { List<ValidationError> errors = new LinkedList<ValidationError>(); for (GMLDirectedEdge e : shape.getEdges()) { if (shape.hasNeighbour(e)) { int nId = shape.getNeighbour(e); GMLShape neighbour = map.getShape(nId); if (neighbour == null) { String message = "Connection to nonexisting id " + nId + " via Edge " + e.getEdge().getID(); errors.add(new ValidationError(shape.getID(), message)); } else if (neighbour == shape) { String message = "Shape is connected to itself via Edge" + e.getEdge().getID(); errors.add(new ValidationError(shape.getID(), message)); } else { GMLShape backRef = null; try { if (neighbour.hasNeighbour(e.getEdge())) { backRef = map.getShape(neighbour.getNeighbour(e .getEdge())); } if (backRef != shape) { String message = "Connection to " + neighbour.getID() + " via Edge " + e.getEdge().getID() + " is not reflexive."; errors.add(new ValidationError(shape.getID(), message)); } } catch (IllegalArgumentException ex) { String message = "Neigbour " + neighbour.getID() + " does not share Edge " + e.getEdge().getID(); errors.add(new ValidationError(shape.getID(), message)); } } } } return errors; } /** * Get all shapes that a shape is connected to. * @param shape * @return */ private Collection<GMLShape> getNeigbours(GMLShape shape) { Collection<GMLShape> result = new ArrayList<GMLShape>(); for (GMLDirectedEdge edge : shape.getEdges()) { if (shape.hasNeighbour(edge)) { GMLShape n = map.getShape(shape.getNeighbour(edge)); if (n != null) { result.add(n); } } } return result; } }