logo separator

[mkgmap-dev] mkgmap-dev Digest, Vol 153, Issue 40 Resolution 23 raster problems

From Andrzej Popowski popej at poczta.onet.pl on Tue Apr 27 16:16:31 BST 2021

Hi,

some more experiments, see attached patch. I have tried to optimize 
rounding of coordinates for lowest distance to line. This is not good 
for polygons, because can creates gaps between adjacent polygons.

--
Best regards,
Andrzej
-------------- next part --------------
Index: src/uk/me/parabola/mkgmap/build/MapBuilder.java
===================================================================
--- src/uk/me/parabola/mkgmap/build/MapBuilder.java	(revision 4685)
+++ src/uk/me/parabola/mkgmap/build/MapBuilder.java	(working copy)
@@ -87,6 +87,7 @@
 import uk.me.parabola.mkgmap.filters.RemoveEmpty;
 import uk.me.parabola.mkgmap.filters.RemoveObsoletePointsFilter;
 import uk.me.parabola.mkgmap.filters.RoundCoordsFilter;
+import uk.me.parabola.mkgmap.filters.RoundCoordsFilter2;
 import uk.me.parabola.mkgmap.filters.ShapeMergeFilter;
 import uk.me.parabola.mkgmap.filters.SizeFilter;
 import uk.me.parabola.mkgmap.general.CityInfo;
@@ -1188,10 +1189,10 @@
 		
 		LayerFilterChain filters = new LayerFilterChain(config);
 		if (enableLineCleanFilters && (res < 24)) {
-			filters.addFilter(new RoundCoordsFilter());
 			filters.addFilter(new SizeFilter(MIN_SIZE_LINE));
 			if(reducePointError > 0)
 				filters.addFilter(new DouglasPeuckerFilter(reducePointError));
+			filters.addFilter(new RoundCoordsFilter2());
 		}
 		filters.addFilter(new LineSplitterFilter());
 		filters.addFilter(new RemoveEmpty());
@@ -1243,7 +1244,6 @@
 		LayerFilterChain filters = new LayerFilterChain(config);
 		filters.addFilter(new PolygonSplitterFilter());
 		if (enableLineCleanFilters && (res < 24)) {
-			filters.addFilter(new RoundCoordsFilter());
 			int sizefilterVal =  getMinSizePolygonForResolution(res);
 			if (sizefilterVal > 0)
 				filters.addFilter(new SizeFilter(sizefilterVal));
@@ -1251,6 +1251,7 @@
 			//Is there an similar algorithm for polygons?
 			if(reducePointErrorPolygon > 0)
 				filters.addFilter(new DouglasPeuckerFilter(reducePointErrorPolygon));
+			filters.addFilter(new RoundCoordsFilter());
 		}
 		filters.addFilter(new RemoveObsoletePointsFilter());
 		filters.addFilter(new RemoveEmpty());
Index: src/uk/me/parabola/mkgmap/filters/RoundCoordsFilter2.java
===================================================================
--- src/uk/me/parabola/mkgmap/filters/RoundCoordsFilter2.java	(nonexistent)
+++ src/uk/me/parabola/mkgmap/filters/RoundCoordsFilter2.java	(working copy)
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2007 Steve Ratcliffe
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License 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.app.Coord;
+import uk.me.parabola.imgfmt.app.CoordNode;
+import uk.me.parabola.mkgmap.general.MapElement;
+import uk.me.parabola.mkgmap.general.MapLine;
+import uk.me.parabola.mkgmap.general.MapRoad;
+
+public class RoundCoordsFilter2 implements MapFilter {
+
+	private int shift;
+	private boolean keepNodes;
+	private int level;
+
+	@Override
+	public void init(FilterConfig config) {
+		shift = config.getShift();
+		level = config.getLevel();
+		keepNodes = level == 0 && config.hasNet();
+	}
+
+	/**
+	 * @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.
+	 */
+	@Override
+	public void doFilter(MapElement element, MapFilterChain next) {
+		if (shift == 0) {
+			// do nothing
+			next.doFilter(element);
+		} else {
+			MapLine line = (MapLine) element;
+			int full = 1 << shift;
+			int half = 1 << (shift - 1);	// 0.5 shifted
+			int mask = ~((1 << shift) - 1); // to remove fraction bits
+			
+			// round lat/lon values to nearest for shift
+			List<Coord> newPoints = new ArrayList<>(line.getPoints().size());
+
+			List<Coord> coords = line.getPoints();
+			int endIndex = coords.size() -1;
+
+			Coord lastP = null;
+			boolean hasNumbers = level == 0 && line.isRoad() && ((MapRoad) line).getRoadDef().hasHouseNumbers();
+			for(int i = 0; i <= endIndex; i++) {
+				Coord p = coords.get(i);
+				if (level > 0 && p.isAddedNumberNode()) {
+					// ignore nodes added by housenumber processing for levels > 0   
+					continue;
+				}
+
+				Coord newP;
+				if (p instanceof CoordNode && keepNodes) {
+					int lat = (p.getLatitude() + half) & mask;
+					int lon = (p.getLongitude() + half) & mask;
+					newP = new CoordNode(lat, lon, p.getId(), p.getOnBoundary(), p.getOnCountryBorder());
+				} else if (i == 0 || i == endIndex) {
+					int lat = (p.getLatitude() + half) & mask;
+					int lon = (p.getLongitude() + half) & mask;
+					newP = new Coord(lat, lon);
+					newP.preserved(p.preserved());
+					newP.setNumberNode(hasNumbers && p.isNumberNode());
+				} else { // find best match
+					Coord a = coords.get(i -1);
+					Coord b = coords.get(i +1);
+
+					// point 0,0
+					int lat = p.getLatitude() & mask;
+					int lon = p.getLongitude() & mask;
+					newP = new Coord(lat, lon);
+					double distance = newP.shortestDistToLineSegment(a, p) + newP.shortestDistToLineSegment(p, b);
+
+					Coord testP;
+					double testDistance;
+
+					// point 0,1
+					lon = (p.getLongitude() + full) & mask;
+					testP = new Coord(lat, lon);
+					testDistance = testP.shortestDistToLineSegment(a, p) + testP.shortestDistToLineSegment(p, b);
+					if (testDistance < distance) {
+						distance = testDistance;
+						newP = testP;
+					}
+
+					// point 1,1
+					lat = (p.getLatitude() + full) & mask;
+					testP = new Coord(lat, lon);
+					testDistance = testP.shortestDistToLineSegment(a, p) + testP.shortestDistToLineSegment(p, b);
+					if (testDistance < distance) {
+						distance = testDistance;
+						newP = testP;
+					}
+
+					// point 1,0
+					lon = p.getLongitude() & mask;
+					testP = new Coord(lat, lon);
+					testDistance = testP.shortestDistToLineSegment(a, p) + testP.shortestDistToLineSegment(p, b);
+					if (testDistance < distance) {
+						distance = testDistance;
+						newP = testP;
+					}
+
+					newP.preserved(p.preserved());
+					newP.setNumberNode(hasNumbers && p.isNumberNode());
+				}
+				
+				// only add the new point if it has different
+				// coordinates to the last point or if it's a
+				// special node
+				if (lastP == null || !lastP.equals(newP) || newP.getId() > 0 || (hasNumbers && newP.isNumberNode())) {
+					newPoints.add(newP);
+					lastP = newP;
+				} else if (newP.preserved()) {
+					// this point is not going to be used because it
+					// has the same (rounded) coordinates as the last
+					// node but it has been marked as being "preserved" -
+					// transfer that property to the previous point so
+					// that it's not lost in further filters
+					lastP.preserved(true);
+				}
+			}
+			if (newPoints.size() > 1) {
+				MapLine newLine = line.copy();
+				newLine.setPoints(newPoints);
+				next.doFilter(newLine);
+			}
+		}
+	}
+}


More information about the mkgmap-dev mailing list