package maps.convert.osm2gml; import java.util.Map; import java.util.List; import java.util.HashMap; import java.util.ArrayList; import java.util.Set; import java.util.HashSet; import java.util.Collection; import java.util.Collections; import java.awt.Color; import maps.convert.ConvertStep; /** This class merges adjacent shapes of the same type. */ public class MergeShapesStep extends ConvertStep { private TemporaryMap map; /** Construct a MergeShapesStep. @param map The TemporaryMap to use. */ public MergeShapesStep(TemporaryMap map) { this.map = map; } @Override public String getDescription() { return "Merging adjacent shapes"; } @Override protected void step() { debug.setBackground(ConvertTools.getAllDebugShapes(map)); Collection<TemporaryBuilding> buildings = map.getBuildings(); Collection<TemporaryRoad> roads = map.getRoads(); setProgressLimit(buildings.size() + roads.size()); // Merge any buildings with the same ID that got split earlier. int buildingCount = 0; int roadCount = 0; for (TemporaryBuilding next : buildings) { if (tryToMerge(next)) { ++buildingCount; } bumpProgress(); } // Try merging adjacent roads for (TemporaryRoad next : roads) { if (tryToMerge(next)) { ++roadCount; } bumpProgress(); } setStatus("Merged " + buildingCount + " building shapes and " + roadCount + " road shapes"); } private boolean tryToMerge(TemporaryBuilding b) { if (!map.getBuildings().contains(b)) { return false; } Collection<TemporaryBuilding> others = map.getBuildings(); for (TemporaryBuilding other : others) { if (b == other) { continue; } if (other.getBuildingID() == b.getBuildingID()) { List<DirectedEdge> boundary = mergeShapes(b, other); if (boundary == null) { continue; } TemporaryBuilding newBuilding = new TemporaryBuilding(boundary, b.getBuildingID()); map.addBuilding(newBuilding); map.removeBuilding(b); map.removeBuilding(other); debug.show("Merged buildings", new TemporaryObjectInfo(b, "First", Color.BLACK, Color.GREEN), new TemporaryObjectInfo(other, "Second", Color.BLACK, Color.WHITE), new TemporaryObjectInfo(newBuilding, "New building", Color.BLUE, null)); return true; } } return false; } private boolean tryToMerge(TemporaryRoad r) { if (!map.getRoads().contains(r)) { return false; } Collection<TemporaryRoad> others = map.getRoads(); for (TemporaryRoad other : others) { if (r == other) { continue; } List<DirectedEdge> boundary = mergeShapes(r, other); if (boundary == null) { continue; } // Check for convexity if (ConvertTools.isConvex(boundary)) { TemporaryRoad newRoad = new TemporaryRoad(boundary); map.addRoad(newRoad); map.removeRoad(r); map.removeRoad(other); debug.show("Merged roads", new TemporaryObjectInfo(r, "First", Color.BLACK, Color.GREEN), new TemporaryObjectInfo(other, "Second", Color.BLACK, Color.WHITE), new TemporaryObjectInfo(newRoad, "New road", Color.BLUE, null)); return true; } } return false; } private List<DirectedEdge> mergeShapes(TemporaryObject first, TemporaryObject second) { Map<Edge, DirectedEdge> edges1 = new HashMap<Edge, DirectedEdge>(); Map<Edge, DirectedEdge> edges2 = new HashMap<Edge, DirectedEdge>(); for (DirectedEdge e : first.getEdges()) { edges1.put(e.getEdge(), e); } for (DirectedEdge e : second.getEdges()) { edges2.put(e.getEdge(), e); } if (Collections.disjoint(edges1.keySet(), edges2.keySet())) { return null; } Set<DirectedEdge> boundary = new HashSet<DirectedEdge>(); for (Map.Entry<Edge, DirectedEdge> next : edges1.entrySet()) { if (!edges2.containsKey(next.getKey())) { boundary.add(next.getValue()); } } for (Map.Entry<Edge, DirectedEdge> next : edges2.entrySet()) { if (!edges1.containsKey(next.getKey())) { boundary.add(next.getValue()); } } // Walk the boundary DirectedEdge start = boundary.iterator().next(); List<DirectedEdge> result = new ArrayList<DirectedEdge>(); result.add(start); while (!boundary.isEmpty()) { start = findNextEdge(start, boundary); boundary.remove(start); result.add(start); } return result; } private DirectedEdge findNextEdge(DirectedEdge from, Set<DirectedEdge> candidates) { Node n = from.getEndNode(); for (DirectedEdge next : candidates) { if (next.getStartNode().equals(n)) { return next; } } throw new IllegalArgumentException("No candidate edge starting from " + n); } }