Hi Gerd
Attached patch stops the errors from shapeMergeFilter. The java2D
intersect.() / converter used for clipping sometimes generates
flattened shapes which I now check for and chuck away.
I've also moved the code that shares common coord.
The patch is independent of the pending drawLevel patch
Ticker
On Sat, 2016-11-19 at 10:46 +0000, Ticker Berkin wrote:
> Your right - it is big. My system didn't want to download it without
> installing some add-ons which I don't want to do at the moment.
>
> However, I have reproduced the same diagnostic from one of my maps
> and
> am investigating that. It is more related to --order-by-.. than
> mkgmap:drawLevel and the boundaries of different precisions used by
> mkgmap.
>
> Looking at the map output at a very high resolution in the problem
> area, I can't see any difference between when I get the error and
> when
> I've made it not happen by some trickery.
>
> I should be able to come up with the correct fix in a day or two.
>
> Ticker
>
> _______________________________________________
> mkgmap-dev mailing list
> [email protected]
> http://www.mkgmap.org.uk/mailman/listinfo/mkgmap-dev
Index: src/uk/me/parabola/mkgmap/build/MapArea.java
===================================================================
--- src/uk/me/parabola/mkgmap/build/MapArea.java (revision 3703)
+++ src/uk/me/parabola/mkgmap/build/MapArea.java (working copy)
@@ -20,6 +20,9 @@
import java.util.Arrays;
import java.util.List;
+import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
+
+import uk.me.parabola.imgfmt.Utils;
import uk.me.parabola.util.Java2DConverter;
import uk.me.parabola.imgfmt.app.Area;
import uk.me.parabola.imgfmt.app.Coord;
@@ -90,6 +93,8 @@
/** The resolution that this area is at */
private final int areaResolution;
+ private Long2ObjectOpenHashMap<Coord> areasHashMap;
+
/**
* Create a map area from the given map data source. This map
* area will have the same bounds as the map data source and
@@ -660,10 +665,19 @@
* @param e The map element.
* @param used flag vector to say area has been added to.
*/
- private static void splitIntoAreas(MapArea[] areas, MapShape e, boolean[] used)
+ private void splitIntoAreas(MapArea[] areas, MapShape e, boolean[] used)
{
// quick check if bbox of shape lies fully inside one of the areas
Area shapeBounds = e.getBounds();
+
+ // this is worked out at standard precision, along with Area.contains() and so can get
+ // tricky problems as it might not really be fully within the area.
+ // so: pretend the shape is a touch bigger. Will get the optimisation most of the time
+ // and in the boundary cases will fall into the precise code.
+ shapeBounds = new Area(shapeBounds.getMinLat()-2,
+ shapeBounds.getMinLong()-2,
+ shapeBounds.getMaxLat()+2,
+ shapeBounds.getMaxLong()+2);
for (int areaIndex = 0; areaIndex < areas.length; ++areaIndex) {
if (areas[areaIndex].getBounds().contains(shapeBounds)) {
used[areaIndex] = true;
@@ -672,8 +686,19 @@
}
}
// Shape crosses area(s), we have to split it
+
// Convert to a awt area
- java.awt.geom.Area area = Java2DConverter.createArea(e.getPoints());
+ List<Coord> coords = e.getPoints();
+ java.awt.geom.Area area = Java2DConverter.createArea(coords);
+ // remember actual coord, so can re-use
+ int origSize = coords.size();
+ Long2ObjectOpenHashMap<Coord> shapeHashMap = new Long2ObjectOpenHashMap<>(origSize);
+ for (int i = 0; i < origSize; ++i) {
+ Coord co = coords.get(i);
+ shapeHashMap.put(Utils.coord2Long(co), co);
+ }
+ if (areasHashMap == null)
+ areasHashMap = new Long2ObjectOpenHashMap<>();
for (int areaIndex = 0; areaIndex < areas.length; ++areaIndex) {
java.awt.geom.Area clipper = Java2DConverter.createBoundsArea(areas[areaIndex].getBounds());
@@ -680,10 +705,45 @@
clipper.intersect(area);
List<List<Coord>> subShapePoints = Java2DConverter.areaToShapes(clipper);
for (List<Coord> subShape : subShapePoints) {
- used[areaIndex] = true;
+ // Use original or share newly created coords on clipped edge.
+ // NB: .intersect()/areaToShapes can output flattened shapes,
+ // normally triangles, in any orientation; check we haven't got one by calc area.
+ long signedAreaSize = 0;
+ int subSize = subShape.size();
+ int c1_highPrecLat = 0, c1_highPrecLon = 0;
+ int c2_highPrecLat, c2_highPrecLon;
+ for (int i = 0; i < subSize; ++i) {
+ Coord co = subShape.get(i);
+ c2_highPrecLat = co.getHighPrecLat();
+ c2_highPrecLon = co.getHighPrecLon();
+ if (i > 0)
+ signedAreaSize += (long)(c2_highPrecLon + c1_highPrecLon) *
+ (c1_highPrecLat - c2_highPrecLat);
+ c1_highPrecLat = c2_highPrecLat;
+ c1_highPrecLon = c2_highPrecLon;
+ long hashVal = Utils.coord2Long(co);
+ Coord replCoord = shapeHashMap.get(hashVal);
+ if (replCoord != null)
+ subShape.set(i, replCoord);
+ else { // not an original coord
+ replCoord = areasHashMap.get(hashVal);
+ if (replCoord != null)
+ subShape.set(i, replCoord);
+ else
+ areasHashMap.put(hashVal, co);
+ }
+ }
+ if (signedAreaSize == 0) {
+ log.warn("splitIntoAreas flat shape. id", e.getOsmid(),
+ "type", uk.me.parabola.mkgmap.reader.osm.GType.formatType(e.getType()), subSize,
+ "points, at", subShape.get(0).toOSMURL());
+ continue;
+ }
MapShape s = e.copy();
s.setPoints(subShape);
+ s.setClipped(true);
areas[areaIndex].addShape(s);
+ used[areaIndex] = true;
}
}
}
Index: src/uk/me/parabola/mkgmap/build/MapBuilder.java
===================================================================
--- src/uk/me/parabola/mkgmap/build/MapBuilder.java (revision 3703)
+++ src/uk/me/parabola/mkgmap/build/MapBuilder.java (working copy)
@@ -1122,9 +1122,6 @@
config.setRoutable(doRoads);
if (mergeShapes){
- if (orderByDecreasingArea) // splitIntoAreas destroyed the shared coord, so redo
- prepShapesForMerge(shapes);
-
ShapeMergeFilter shapeMergeFilter = new ShapeMergeFilter(res, orderByDecreasingArea);
List<MapShape> mergedShapes = shapeMergeFilter.merge(shapes);
shapes = mergedShapes;
_______________________________________________
mkgmap-dev mailing list
[email protected]
http://www.mkgmap.org.uk/mailman/listinfo/mkgmap-dev