/* * Copyright (C) 2012. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 or * version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. */ package uk.me.parabola.mkgmap.filters; import java.util.ArrayList; import java.util.List; import uk.me.parabola.imgfmt.Utils; import uk.me.parabola.imgfmt.app.Coord; import uk.me.parabola.log.Logger; import uk.me.parabola.mkgmap.general.MapElement; import uk.me.parabola.mkgmap.general.MapLine; import uk.me.parabola.mkgmap.general.MapShape; /** * Filter for lines and shapes. Remove obsolete points on straight lines and spikes. * @author GerdP * */ public class RemoveObsoletePointsFilter implements MapFilter { private static final Logger log = Logger.getLogger(RemoveObsoletePointsFilter.class); final Coord[] areaTest = new Coord[3]; private boolean checkPreserved; public void init(FilterConfig config) { checkPreserved = config.getLevel() == 0 && config.isRoutable(); } /** * @param element A map element that will be a line or a polygon. * @param next This is used to pass the possibly transformed element onward. */ public void doFilter(MapElement element, MapFilterChain next) { MapLine line = (MapLine) element; List<Coord> points = line.getPoints(); int numPoints = points.size(); if (numPoints <= 1){ return; } int requiredPoints = (line instanceof MapShape ) ? 4:2; List<Coord> newPoints = new ArrayList<Coord>(numPoints); while (true){ boolean removedSpike = false; numPoints = points.size(); Coord lastP = points.get(0); newPoints.add(lastP); for(int i = 1; i < numPoints; i++) { Coord newP = points.get(i); int last = newPoints.size()-1; lastP = newPoints.get(last); if (lastP.equals(newP)){ // only add the new point if it has different // coordinates to the last point or is preserved if (checkPreserved && line.isRoad()){ if (newP.preserved() == false) continue; else if (lastP.preserved() == false){ newPoints.set(last, newP); // replace last } } else continue; } if (newPoints.size() > 1) { switch (Utils.isStraight(newPoints.get(last-1), lastP, newP)){ case Utils.STRICTLY_STRAIGHT: if (checkPreserved && lastP.preserved() && line.isRoad()){ // keep it } else { log.debug("found three consecutive points on strictly straight line"); newPoints.set(last, newP); continue; } break; case Utils.STRAIGHT_SPIKE: if (line instanceof MapShape){ log.debug("removing spike"); newPoints.remove(last); removedSpike = true; if (newPoints.get(last-1).equals(newP)) continue; } break; default: break; } } newPoints.add(newP); } if (!removedSpike || newPoints.size() < requiredPoints) break; points = newPoints; newPoints = new ArrayList<Coord>(points.size()); } if (line instanceof MapShape){ // Check special cases caused by the fact that the first and last point // in a shape are identical. while (newPoints.size() > 3){ int nPoints = newPoints.size(); switch(Utils.isStraight(newPoints.get(newPoints.size()-2), newPoints.get(0), newPoints.get(1))){ case Utils.STRAIGHT_SPIKE: newPoints.remove(0); newPoints.set(newPoints.size()-1, newPoints.get(0)); if (newPoints.get(newPoints.size()-2).equals(newPoints.get(newPoints.size()-1))) newPoints.remove(newPoints.size()-1); break; case Utils.STRICTLY_STRAIGHT: newPoints.remove(newPoints.size()-1); newPoints.set(0, newPoints.get(newPoints.size()-1)); break; } if (nPoints == newPoints.size()) break; } } if (newPoints.size() != line.getPoints().size()){ if (line instanceof MapShape && newPoints.size() <= 3 || newPoints.size() <= 1) return; MapLine newLine = line.copy(); newLine.setPoints(newPoints); next.doFilter(newLine); } else { // no need to create new object next.doFilter(line); } } }