Hi Felix,

are you a night worker or do you live in another timezone? :-)


I wanted to apply it but I get "The chunk size did not match the number of added/removed lines!"

Is there possibly something wrong with Patch V8?


Yes, I have seen some minor changes in the patch, which should not be included, so I modified it by hand. Seems to be no good idea. Attached the corrected patch.

Please note: The patch should work as far it is implemented now. But I have some more ideas for the dp filter, which would need some rework of the base classes, for which I have not the time at the moment. It should be clearly an improvement, but shows not its full potential at the moment.


Index: src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java
===================================================================
--- src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java	(Revision 1404)
+++ src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java	(Arbeitskopie)
@@ -55,6 +55,7 @@
 import uk.me.parabola.mkgmap.reader.osm.Rule;
 import uk.me.parabola.mkgmap.reader.osm.Style;
 import uk.me.parabola.mkgmap.reader.osm.Way;
+import uk.me.parabola.util.EnhancedProperties;
 import uk.me.parabola.mkgmap.reader.polish.PolishMapDataSource;
 
 /**
@@ -143,7 +144,7 @@
 		}
 	};
 
-	public StyledConverter(Style style, MapCollector collector, Properties props) {
+	public StyledConverter(Style style, MapCollector collector, EnhancedProperties props) {
 		this.collector = collector;
 
 		nameTagList = style.getNameTagList();
Index: src/uk/me/parabola/mkgmap/build/MapBuilder.java
===================================================================
--- src/uk/me/parabola/mkgmap/build/MapBuilder.java	(Revision 1404)
+++ src/uk/me/parabola/mkgmap/build/MapBuilder.java	(Arbeitskopie)
@@ -57,6 +57,7 @@
 import uk.me.parabola.mkgmap.filters.DouglasPeuckerFilter;
 import uk.me.parabola.mkgmap.filters.FilterConfig;
 import uk.me.parabola.mkgmap.filters.LineSplitterFilter;
+import uk.me.parabola.mkgmap.filters.LineMergeFilter;
 import uk.me.parabola.mkgmap.filters.MapFilter;
 import uk.me.parabola.mkgmap.filters.MapFilterChain;
 import uk.me.parabola.mkgmap.filters.PolygonSplitterFilter;
@@ -107,6 +108,10 @@
 	private String countryAbbr = "ABC";
 	private String regionName;
 	private String regionAbbr;
+
+	private double reducePointError;
+	private boolean mergeLines;
+
 	private int		locationAutofillLevel;
 	private boolean	poiAddresses = true;
 	private int		poiDisplayFlags;
@@ -126,6 +131,8 @@
 		countryAbbr = props.getProperty("country-abbr", countryAbbr);
 		regionName = props.getProperty("region-name", null);
 		regionAbbr = props.getProperty("region-abbr", null);
+		reducePointError = props.getProperty("reduce-point-density", 2.6);
+		mergeLines = props.containsKey("merge-lines");
 
 		makePOIIndex = props.getProperty("make-poi-index", false);
 
@@ -866,11 +873,19 @@
 		FilterConfig config = new FilterConfig();
 		config.setResolution(res);
 
+
+		//TODO: Maybe this is the wrong place to do merging.
+		// Maybe more efficient if merging before creating subdivisions.
+		if (mergeLines && res < 24) {
+			LineMergeFilter merger = new LineMergeFilter();
+			lines = merger.merge(lines);
+		}
+
 		LayerFilterChain filters = new LayerFilterChain(config);
 		if (enableLineCleanFilters && (res < 24)) {
 			filters.addFilter(new RoundCoordsFilter());
 			filters.addFilter(new SizeFilter());
-			filters.addFilter(new DouglasPeuckerFilter(FILTER_DISTANCE));
+			filters.addFilter(new DouglasPeuckerFilter(reducePointError));
 		}
 		filters.addFilter(new LineSplitterFilter());
 		filters.addFilter(new RemoveEmpty());
@@ -909,7 +924,7 @@
 			filters.addFilter(new SizeFilter());
 			//DouglasPeucker behaves at the moment not really optimal at low zooms, but acceptable.
 			//Is there an similar algorithm for polygons?
-			filters.addFilter(new DouglasPeuckerFilter(FILTER_DISTANCE));
+			filters.addFilter(new DouglasPeuckerFilter(reducePointError));
 		}
 		filters.addFilter(new PolygonSplitterFilter());
 		filters.addFilter(new RemoveEmpty());
Index: src/uk/me/parabola/mkgmap/reader/osm/xml/Osm5MapDataSource.java
===================================================================
--- src/uk/me/parabola/mkgmap/reader/osm/xml/Osm5MapDataSource.java	(Revision 1404)
+++ src/uk/me/parabola/mkgmap/reader/osm/xml/Osm5MapDataSource.java	(Arbeitskopie)
@@ -32,6 +32,7 @@
 import uk.me.parabola.mkgmap.osmstyle.eval.SyntaxException;
 import uk.me.parabola.mkgmap.reader.osm.OsmConverter;
 import uk.me.parabola.mkgmap.reader.osm.Style;
+import uk.me.parabola.util.EnhancedProperties;
 
 import org.xml.sax.SAXException;
 
@@ -104,7 +105,7 @@
 	 */
 	private OsmConverter createStyler() {
 
-		Properties props = getConfig();
+		EnhancedProperties props = getConfig();
 		String loc = props.getProperty("style-file");
 		if (loc == null)
 			loc = props.getProperty("map-features");
Index: src/uk/me/parabola/mkgmap/filters/LineMergeFilter.java
===================================================================
--- src/uk/me/parabola/mkgmap/filters/LineMergeFilter.java	(Revision 0)
+++ src/uk/me/parabola/mkgmap/filters/LineMergeFilter.java	(Revision 0)
@@ -0,0 +1,110 @@
+package uk.me.parabola.mkgmap.filters;
+
+import java.util.List;
+import java.util.ArrayList;
+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);
+
+	List<MapLine> linesMerged;
+	MultiHashMap<Coord, MapLine> startPoints = new MultiHashMap<Coord, MapLine>();
+	MultiHashMap<Coord, MapLine> endPoints = new MultiHashMap<Coord, MapLine>();
+
+	public LineMergeFilter() {}
+
+	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.remove(points1.get(0), line1);
+		endPoints.remove(points1.get(points1.size()-1), line1);
+		startPoints.remove(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.remove(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.remove(points.get(points.size()-1), line);
+		line.insertPointsAtEnd(additionalPoints);
+		endPoints.add(points.get(points.size()-1), line);
+	}
+
+	public List<MapLine> merge(List<MapLine> lines) {
+		linesMerged = new ArrayList<MapLine>(lines.size());	//better use LinkedList??
+		for (MapLine line : lines) {
+			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;
+	}
+
+}
+

Index: src/uk/me/parabola/mkgmap/general/MapLine.java
===================================================================
--- src/uk/me/parabola/mkgmap/general/MapLine.java	(Revision 1404)
+++ src/uk/me/parabola/mkgmap/general/MapLine.java	(Arbeitskopie)
@@ -59,8 +59,13 @@
 		if (this.points != null)
 			log.warn("overwriting points");
 		assert points != null : "trying to set null points";
+		assert points.size() != 0 : "trying to set points with zero length";
 
 		this.points = points;
+		testForConsecutivePoints(points);
+	}
+	
+	public void testForConsecutivePoints(List<Coord> points) {
 		Coord last = null;
 		for (Coord co : points) {
 			if (last != null && last.equals(co))
@@ -72,6 +77,18 @@
 		}
 	}
 
+	public void insertPointsAtStart(List<Coord> additionalPoints) {
+		testForConsecutivePoints(additionalPoints);
+		points.addAll(0, additionalPoints);
+		points.remove(additionalPoints.size()-1);	//End node exists now twice
+	}
+
+	public void insertPointsAtEnd(List<Coord> additionalPoints) {
+		testForConsecutivePoints(additionalPoints);
+		points.remove(points.size()-1); 
+		points.addAll(additionalPoints);
+	}
+
 	public boolean isDirection() {
 		return direction;
 	}
Index: src/uk/me/parabola/mkgmap/general/MapElement.java
===================================================================
--- src/uk/me/parabola/mkgmap/general/MapElement.java	(Revision 1404)
+++ src/uk/me/parabola/mkgmap/general/MapElement.java	(Arbeitskopie)
@@ -167,6 +167,24 @@
 		this.type = type;
 	}
 
+	public boolean isSimilar(MapElement other) {
+		if (this.minResolution != other.minResolution)
+			return false;
+		if (this.maxResolution != other.maxResolution)
+			return false;
+		if (this.type != other.type)
+			return false;
+
+		String thisName = getName();
+		String otherName = other.getName();
+
+		if (thisName == null && otherName == null)	
+			return true;
+		if (thisName!=null && otherName!=null && thisName.equals(otherName))
+			return true;
+		return false;
+	}
+
 	public boolean hasExtendedType() {
 		return hasExtendedType(type);
 	}
Index: src/uk/me/parabola/util/MultiHashMap.java
===================================================================
--- src/uk/me/parabola/util/MultiHashMap.java	(Revision 0)
+++ src/uk/me/parabola/util/MultiHashMap.java	(Revision 0)
@@ -0,0 +1,59 @@
+package uk.me.parabola.util;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+
+
+public class MultiHashMap<K,V> extends HashMap<K,List<V>> {
+
+	/**
+	* the empty list to be returned when there is key without values.
+	*/
+	private List<V> emptyList = Collections.unmodifiableList(new ArrayList<V>(0));
+
+	/**
+	* Returns the list of values associated with the given key.
+	*
+	* @param o the key to get the values for.
+	* @return a list of values for the given keys or the empty list of no such
+	*         value exist.
+	*/
+	public List<V> get(K key) {
+		List<V> result = super.get(key);
+		return result == null ? emptyList : result;
+	}
+
+
+	public V add(K key, V value )
+	{
+	    
+	    List<V> values = super.get(key);
+	    if (values == null ) {
+	        values = new LinkedList<V>();
+	        super.put( key, values );
+	    }
+	    
+	    boolean results = values.add(value);
+	    
+	    return ( results ? value : null );
+	}
+
+	public V remove(K key, V value )
+	{
+	    
+	    List<V> values = super.get(key);
+	    if (values == null )
+			return null;
+	
+	    values.remove(value);
+		
+		if (values.size() == 0)
+			super.remove(values);
+
+		return value;
+	}
+}
+

Index: resources/help/en/options
===================================================================
--- resources/help/en/options	(Revision 1404)
+++ resources/help/en/options	(Arbeitskopie)
@@ -156,6 +156,21 @@
 	number is 63240000.
 	
 
+Optimization options:
+
+--reduce-point-density=num
+	Simplifies the ways with the douglas peucker algorithm.
+	num is the maximal allowed error distance, by which the resulting
+	way may differ from the original one.
+	This distance gets shifted with lower zoom levels. 
+	Recommended setting is 10, this should lead to only small differences
+	(Default is 2.6, which should lead to invisible changes) 
+
+--merge-lines
+	Try to merge lines. This helps the simplify filter to straighten out
+    longer chunks at lower zoom levels. Decreases file size more.
+	Increases paint speed at low zoom levels
+
 Miscellaneous options:
 
 --max-jobs[=number]
_______________________________________________
mkgmap-dev mailing list
[email protected]
http://www.mkgmap.org.uk/mailman/listinfo/mkgmap-dev

Reply via email to