package uk.me.parabola.mkgmap.filters; import java.util.ArrayList; import java.util.List; import uk.me.parabola.imgfmt.app.Coord; import uk.me.parabola.log.Logger; import uk.me.parabola.mkgmap.general.MapLine; import uk.me.parabola.util.MultiHashMap; public class LineMergeFilter{ private static final Logger log = Logger.getLogger(LineMergeFilter.class); private List<MapLine> linesMerged; private final MultiHashMap<Coord, MapLine> startPoints = new MultiHashMap<Coord, MapLine>(); private final MultiHashMap<Coord, MapLine> endPoints = new MultiHashMap<Coord, MapLine>(); private void addLine(MapLine line) { linesMerged.add(line); List<Coord> points = line.getPoints(); startPoints.add(points.get(0), line); endPoints.add(points.get(points.size()-1), line); } private void mergeLines(MapLine line1, MapLine line2) { // Removes the first line, // Merges the points in the second one List<Coord> points1 = line1.getPoints(); List<Coord> points2 = line2.getPoints(); startPoints.removeMapping(points1.get(0), line1); endPoints.removeMapping(points1.get(points1.size() - 1), line1); startPoints.removeMapping(points2.get(0), line2); startPoints.add(points1.get(0), line2); line2.insertPointsAtStart(points1); linesMerged.remove(line1); } private void addPointsAtStart(MapLine line, List<Coord> additionalPoints) { log.info("merged lines before " + line.getName()); List<Coord> points = line.getPoints(); startPoints.removeMapping(points.get(0), line); line.insertPointsAtStart(additionalPoints); startPoints.add(points.get(0), line); } private void addPointsAtEnd(MapLine line, List<Coord> additionalPoints) { log.info("merged lines after " + line.getName()); List<Coord> points = line.getPoints(); endPoints.removeMapping(points.get(points.size() - 1), line); line.insertPointsAtEnd(additionalPoints); endPoints.add(points.get(points.size()-1), line); } //TODO: This routine has a side effect: it modifies some of the MapLine instances // instead of creating copies. It seems that this has no bad effect, but it is not clean public List<MapLine> merge(List<MapLine> lines, int res) { linesMerged = new ArrayList<MapLine>(lines.size()); //better use LinkedList?? for (MapLine line : lines) { if (line.getMinResolution() > res || line.getMaxResolution() < res) continue; if (line.isRoad()){ linesMerged.add(line); continue; } boolean isMerged = false; List<Coord> points = line.getPoints(); Coord start = points.get(0); Coord end = points.get(points.size()-1); // Search for start point in hashlist // (can the end of current line connected to an existing line?) for (MapLine line2 : startPoints.get(end)) { if (line.isSimilar(line2)) { addPointsAtStart(line2, points); // Search for endpoint in hashlist // (if the other end (=start of line =start of line2) could be connected to an existing line, // both lines has to be merged and one of them dropped) for (MapLine line1 : endPoints.get(start)) { if (line2.isSimilar(line1) && !line2.equals(line1)) // don't make a closed loop a double loop { mergeLines(line1, line2); break; } } isMerged = true; break; } } if (isMerged) continue; // Search for endpoint in hashlist // (can the start of current line connected to an existing line?) for (MapLine line2 : endPoints.get(start)) { if (line.isSimilar(line2)) { addPointsAtEnd(line2, points); isMerged = true; break; } } if (isMerged) continue; // No matching, create a copy of line MapLine l = line.copy(); List<Coord> p = new ArrayList<Coord>(line.getPoints()); //use better LinkedList for performance? l.setPoints(p); addLine(l); } return linesMerged; } }