[PATCH v4] sea polygons

With this version of the patch, the intersection of the bounding box and the landmass does not have to be simply connected as in version 3 of the patch. There are still some "flooded island" in ireland, but I'm pretty sure this is a problem in the multipolygon code this patch relies on (it just add one sea polygon and constructs a multipolygon with all land components as inner ways). Best wishes Christian Index: src/uk/me/parabola/mkgmap/reader/osm/MultiPolygonRelation.java =================================================================== --- src/uk/me/parabola/mkgmap/reader/osm/MultiPolygonRelation.java (Revision 1135) +++ src/uk/me/parabola/mkgmap/reader/osm/MultiPolygonRelation.java (Arbeitskopie) @@ -1,12 +1,13 @@ package uk.me.parabola.mkgmap.reader.osm; import java.util.ArrayList; -import java.util.Collection; +import java.util.Iterator; import java.util.List; import java.util.Map; -import uk.me.parabola.imgfmt.Utils; import uk.me.parabola.imgfmt.app.Coord; +import uk.me.parabola.mkgmap.general.MapShape; +import uk.me.parabola.mkgmap.general.MultiShapeMerger; /** * Representation of an OSM Multipolygon Relation. @@ -16,15 +17,19 @@ */ public class MultiPolygonRelation extends Relation { private Way outer; - private final Collection<Way> inners = new ArrayList<Way>(); + private List<Way> outers = new ArrayList<Way>(); + private List<Way> inners = new ArrayList<Way>(); + private Map<Long, Way> myWayMap; /** * Create an instance based on an exsiting relation. We need to do * this because the type of the relation is not known until after all * its tags are read in. * @param other The relation to base this one on. + * @param wayMap Map of all ways. */ - public MultiPolygonRelation(Relation other) { + public MultiPolygonRelation(Relation other, Map<Long, Way> wayMap) { + myWayMap = wayMap; setId(other.getId()); for (Map.Entry<Element, String> pairs: other.getRoles().entrySet()){ addElement(pairs.getValue(), pairs.getKey()); @@ -33,10 +38,12 @@ if (value != null && pairs.getKey() instanceof Way) { Way way = (Way) pairs.getKey(); - if (value.equals("outer")) - outer = way; - else if (value.equals("inner")) + if (value.equals("outer")){ + outers.add(way); + } + else if (value.equals("inner")){ inners.add(way); + } } } @@ -45,18 +52,120 @@ } /** Process the ways in this relation. + * Joins way with the role "outer" * Adds ways with the role "inner" to the way with the role "outer" */ + public void processElementsNew() { + if (outers != null) + { + // copy first outer way + Iterator<Way> it = outers.iterator(); + if (it.hasNext()){ + // duplicate outer way and remove tags for cascaded multipolygons + Way tempWay = it.next(); + outer = new Way(-tempWay.getId()); + outer.copyTags(tempWay); + for(Coord point: tempWay.getPoints()){ + outer.addPoint(point); + } + myWayMap.put(outer.getId(), outer); + if (tempWay.getTags() != null){ + tempWay.getTags().removeAll(); + } + it.remove(); + } + + // if we have more than one outer way, we join them if they are parts of a long way + it = outers.iterator(); + while (it.hasNext()){ + Way tempWay = it.next(); + if (tempWay.getPoints().get(0) == outer.getPoints().get(outer.getPoints().size()-1)){ + for(Coord point: tempWay.getPoints()){ + outer.addPoint(point); + } + if (tempWay.getTags() != null){ + tempWay.getTags().removeAll(); + } + it.remove(); + it = outers.iterator(); + } + } + } + + if (outer != null) + { + MapShape outer_s = new MapShape(); + outer_s.setPoints(outer.getPoints()); + MultiShapeMerger merger = new MultiShapeMerger(outer_s); + + for (Way w: inners) { + if (w != null) { + MapShape inner_s = new MapShape(); + inner_s.setPoints(w.getPoints()); + merger.addInner(inner_s); + } + } + + merger.processElements(); + } + } + + public void processElements() { - if (outer != null) - { + + if (outers != null) + { + // copy first outer way + Iterator<Way> it = outers.iterator(); + if (it.hasNext()){ + // duplicate outer way and remove tags for cascaded multipolygons + Way tempWay = it.next(); + outer = new Way(-tempWay.getId()); + outer.copyTags(tempWay); + for(Coord point: tempWay.getPoints()){ + outer.addPoint(point); + } + myWayMap.put(outer.getId(), outer); + if (tempWay.getTags() != null){ + tempWay.getTags().removeAll(); + } + it.remove(); + } + + // if we have more than one outer way, we join them if they are parts of a long way + it = outers.iterator(); + while (it.hasNext()){ + Way tempWay = it.next(); + if (tempWay.getPoints().get(0) == outer.getPoints().get(outer.getPoints().size()-1)){ + for(Coord point: tempWay.getPoints()){ + outer.addPoint(point); + } + if (tempWay.getTags() != null){ + tempWay.getTags().removeAll(); + } + it.remove(); + it = outers.iterator(); + } + } + for (Way w: inners) { - if (w != null) { - List<Coord> pts = w.getPoints(); - int[] insert = findCpa(outer.getPoints(), pts); - if (insert[0] > 0) - insertPoints(pts, insert[0], insert[1]); - pts.clear(); + if (w != null && outer!= null) { + int[] insert = findCpa(outer.getPoints(), w.getPoints()); + if (insert[0] >= 0) + insertPoints(w, insert[0], insert[1]); + + // remove tags from inner way that are available in the outer way + if (outer.getTags() != null){ + for (Map.Entry<String, String> mapTags: outer.getTags().getKeyValues().entrySet()){ + String key = mapTags.getKey(); + String value = mapTags.getValue(); + if (w.getTag(key) != null){ + if (w.getTag(key).equals(value)){ + w.deleteTag(key); + } + } + } + } } } } @@ -64,22 +173,43 @@ /** * Insert Coordinates into the outer way. - * @param inList List of Coordinates to be inserted + * @param way Way to be inserted * @param out Coordinates will be inserted after this point in the outer way. * @param in Points will be inserted starting at this index, * then from element 0 to (including) this element; */ - private void insertPoints(List<Coord> inList, int out, int in){ + private void insertPoints(Way way, int out, int in){ List<Coord> outList = outer.getPoints(); + List<Coord> inList = way.getPoints(); int index = out+1; - for (int i = in; i < inList.size(); i++) + for (int i = in; i < inList.size(); i++){ outList.add(index++, inList.get(i)); - for (int i = 0; i <= in; i++) + } + for (int i = 0; i < in; i++){ outList.add(index++, inList.get(i)); - - //with this line commented we get triangles, when uncommented some areas disappear - // at least in mapsource, on device itself looks OK. - outList.add(index,outList.get(out)); + } + + if (outer.getPoints().size() < 32){ + outList.add(index++, inList.get(in)); + outList.add(index, outList.get(out)); + } + else{ + // we shift the nodes to avoid duplicate nodes (large areas only) + int oLat = outList.get(out).getLatitude(); + int oLon = outList.get(out).getLongitude(); + int iLat = inList.get(in).getLatitude(); + int iLon = inList.get(in).getLongitude(); + if (Math.abs(oLat - iLat) > Math.abs(oLon - iLon)){ + int delta = (oLon > iLon)? -1 : 1; + outList.add(index++, new Coord(iLat + delta, iLon)); + outList.add(index, new Coord(oLat + delta, oLon)); + } + else{ + int delta = (oLat > iLat)? 1 : -1; + outList.add(index++, new Coord(iLat, iLon + delta)); + outList.add(index, new Coord(oLat, oLon + delta)); + } + } } /** Index: src/uk/me/parabola/mkgmap/reader/osm/Way.java =================================================================== --- src/uk/me/parabola/mkgmap/reader/osm/Way.java (Revision 1135) +++ src/uk/me/parabola/mkgmap/reader/osm/Way.java (Arbeitskopie) @@ -76,6 +76,10 @@ } } + public boolean isClosed() { + return points.size() > 0 && points.get(0).equals(points.get(points.size()-1)); + } + /** * A simple representation of this way. * @return A string with the name and start point Index: src/uk/me/parabola/mkgmap/reader/osm/xml/Osm5XmlHandler.java =================================================================== --- src/uk/me/parabola/mkgmap/reader/osm/xml/Osm5XmlHandler.java (Revision 1135) +++ src/uk/me/parabola/mkgmap/reader/osm/xml/Osm5XmlHandler.java (Arbeitskopie) @@ -19,9 +19,13 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.IdentityHashMap; +import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.NavigableSet; import uk.me.parabola.imgfmt.app.Area; import uk.me.parabola.imgfmt.app.Coord; @@ -65,6 +69,7 @@ private final Map<String, Long> fakeIdMap = new HashMap<String, Long>(); private final List<Node> exits = new ArrayList<Node>(); private final List<Way> motorways = new ArrayList<Way>(); + private final List<Way> shoreline = new ArrayList<Way>(); private static final int MODE_NODE = 1; private static final int MODE_WAY = 2; @@ -92,6 +97,7 @@ private final boolean ignoreTurnRestrictions; private final boolean linkPOIsToWays; private final boolean routing; + private final boolean generateSea; private final Double minimumArcLength; private final String frigRoundabouts; @@ -105,6 +111,7 @@ } linkPOIsToWays = props.getProperty("link-pois-to-ways", false); ignoreBounds = props.getProperty("ignore-osm-bounds", false); + generateSea = props.getProperty("generate-sea", false); routing = props.containsKey("route"); String rsa = props.getProperty("remove-short-arcs", null); if(rsa != null) @@ -370,6 +377,8 @@ if("motorway".equals(highway) || "trunk".equals(highway)) motorways.add(currentWay); + if(generateSea && "coastline".equals(currentWay.getTag("natural"))) + shoreline.add(currentWay); currentWay = null; // ways are processed at the end of the document, // may be changed by a Relation class @@ -399,7 +408,7 @@ String type = currentRelation.getTag("type"); if (type != null) { if ("multipolygon".equals(type)) - currentRelation = new MultiPolygonRelation(currentRelation); + currentRelation = new MultiPolygonRelation(currentRelation, wayMap); else if("restriction".equals(type)) { if(ignoreTurnRestrictions) @@ -446,6 +455,10 @@ } coordMap = null; + + if (generateSea) + generateSeaPolygon(shoreline); + for (Relation r : relationMap.values()) converter.convertRelation(r); @@ -707,7 +720,7 @@ } currentWay.addPoint(co); co.incHighwayCount(); // nodes (way joins) will have highwayCount > 1 - if(minimumArcLength != null) + if (minimumArcLength != null || generateSea) nodeIdMap.put(co, id); } } @@ -746,4 +759,294 @@ return fakeIdVal; } } + + private void generateSeaPolygon(List<Way> shoreline) { + + Area seaBounds; + if (bbox != null) + seaBounds = bbox; + else + seaBounds = mapper.getBounds(); + + log.info("generating sea, seaBounds=", seaBounds); + int minLat = seaBounds.getMinLat(); + int maxLat = seaBounds.getMaxLat(); + int minLong = seaBounds.getMinLong(); + int maxLong = seaBounds.getMaxLong(); + Coord nw = new Coord(minLat, minLong); + Coord ne = new Coord(minLat, maxLong); + Coord sw = new Coord(maxLat, minLong); + Coord se = new Coord(maxLat, maxLong); + + long seaId = (1L << 62) + nextFakeId++; + Way sea = new Way(seaId); + sea.addPoint(nw); + sea.addPoint(sw); + sea.addPoint(se); + sea.addPoint(ne); + sea.addPoint(nw); + sea.addTag("natural", "sea"); + log.info("sea: ", sea); + wayMap.put(seaId, sea); + + long multiId = (1L << 62) + nextFakeId++; + Relation seaRelation = new GeneralRelation(multiId); + seaRelation.addTag("type", "multipolygon"); + seaRelation.addElement("outer", sea); + + List<Way> islands = new ArrayList(); + + // handle islands (closes shoreline components) first (they're easy) + Iterator<Way> it = shoreline.iterator(); + while (it.hasNext()) { + Way w = it.next(); + if (w.isClosed()) { + islands.add(w); + it.remove(); + } + } + concatenateWays(shoreline); + // there may be more islands now + it = shoreline.iterator(); + while (it.hasNext()) { + Way w = it.next(); + if (w.isClosed()) { + log.debug("island after concatenating\n"); + islands.add(w); + it.remove(); + } + } + + // create a "inner" way for each island + for (Way w : islands) { + log.info("adding island " + w); + seaRelation.addElement("inner", w); + } + + // the remaining shoreline segments should intersect the boundary + // find the intersection points and store them in a SortedMap + SortedMap<EdgeHit, Way> hitMap = new TreeMap<EdgeHit, Way>(); + for (Way w : shoreline) { + List<Coord> points = w.getPoints(); + Coord pStart = points.get(0); + Coord pEnd = points.get(points.size()-1); + + EdgeHit hStart = getEdgeHit(seaBounds, pStart); + EdgeHit hEnd = getEdgeHit(seaBounds, pEnd); + if (hStart == null || hEnd == null) { + String msg = String.format("Non-closed coastline segment does not hit bounding box - expect strange results: %d (%s) %d (%s) %s\n", + nodeIdMap.get(pStart), pStart.toDegreeString(), + nodeIdMap.get(pEnd), pEnd.toDegreeString(), + pStart.toOSMURL()); + log.error(msg); + seaRelation.addElement("inner", w); + } + else { + log.debug("hits: ", hStart, hEnd); + hitMap.put(hStart, w); + hitMap.put(hEnd, null); + } + } + + + // now construct inner ways from these segments + NavigableSet<EdgeHit> hits = (NavigableSet<EdgeHit>) hitMap.keySet(); + while (hits.size() > 0) { + long id = (1L << 62) + nextFakeId++; + Way w = new Way(id); + wayMap.put(id, w); + + EdgeHit hit = hits.first(); + EdgeHit hFirst = hit; + EdgeHit hNext; + do { + Way segment = hitMap.get(hit); + log.info("current hit: " + hit); + if (segment != null) { + // add the segment and get the "ending hit" + log.info("adding: ", segment); + w.getPoints().addAll(segment.getPoints()); + hNext = getEdgeHit(seaBounds, segment.getPoints().get(segment.getPoints().size()-1)); + } + else { + w.addPoint(hit.getPoint(seaBounds)); + hNext = hits.higher(hit); + if (hNext == null) + hNext = hFirst; + + Coord p = hit.getPoint(seaBounds); + if (hit.edge < hNext.edge) { + log.info("joining: ", hit, hNext); + for (int i=hit.edge; i<hNext.edge; i++) { + EdgeHit corner = new EdgeHit(i, 1.0); + p = corner.getPoint(seaBounds); + log.debug("way: ", corner, p); + w.addPoint(p); + } + } + else if (hit.edge > hNext.edge) { + log.info("joining: ", hit, hNext); + for (int i=hit.edge; i<4; i++) { + EdgeHit corner = new EdgeHit(i, 1.0); + p = corner.getPoint(seaBounds); + log.debug("way: ", corner, p); + w.addPoint(p); + } + for (int i=0; i<hNext.edge; i++) { + EdgeHit corner = new EdgeHit(i, 1.0); + p = corner.getPoint(seaBounds); + log.debug("way: ", corner, p); + w.addPoint(p); + } + } + w.addPoint(hNext.getPoint(seaBounds)); + } + hits.remove(hit); + hit = hNext; + } while (hits.size() > 0 && !hit.equals(hFirst)); + + if (!w.isClosed()) + w.getPoints().add(w.getPoints().get(0)); + w.addTag("highway", "primary"); + log.info("adding non-island landmass, hits.size()=" + hits.size()); + seaRelation.addElement("inner", w); + } + + seaRelation = new MultiPolygonRelation(seaRelation, wayMap); + relationMap.put(multiId, seaRelation); + seaRelation.processElements(); + } + + /** + * Specifies where an edge of the bounding box is hit. + */ + private static class EdgeHit implements Comparable<EdgeHit> + { + int edge; + double t; + + EdgeHit(int edge, double t) { + this.edge = edge; + this.t = t; + } + + public int compareTo(EdgeHit o) { + if (edge < o.edge) + return -1; + else if (edge > o.edge) + return +1; + else if (t > o.t) + return +1; + else if (t < o.t) + return -1; + else + return 0; + } + + @Override public boolean equals(Object o) { + if (o instanceof EdgeHit) { + EdgeHit h = (EdgeHit) o; + return (h.edge == edge && Double.compare(h.t, t) == 0); + } + else + return false; + } + + Coord getPoint(Area a) { + log.info("getPoint: ", this, a); + switch (edge) { + case 0: + return new Coord(a.getMinLat(), (int) (a.getMinLong() + t * (a.getMaxLong()-a.getMinLong()))); + + case 1: + return new Coord((int)(a.getMinLat() + t * (a.getMaxLat()-a.getMinLat())), a.getMaxLong()); + + case 2: + return new Coord(a.getMaxLat(), (int)(a.getMaxLong() - t * (a.getMaxLong()-a.getMinLong()))); + + case 3: + return new Coord((int)(a.getMaxLat() - t * (a.getMaxLat()-a.getMinLat())), a.getMinLong()); + + default: + throw new RuntimeException("illegal state"); + } + } + + public String toString() { + return "EdgeHit " + edge + "@" + t; + } + } + + private EdgeHit getEdgeHit(Area a, Coord p) + { + return getEdgeHit(a, p, 10); + } + + private EdgeHit getEdgeHit(Area a, Coord p, int tolerance) + { + int lat = p.getLatitude(); + int lon = p.getLongitude(); + int minLat = a.getMinLat(); + int maxLat = a.getMaxLat(); + int minLong = a.getMinLong(); + int maxLong = a.getMaxLong(); + + log.info(String.format("getEdgeHit: (%d %d) (%d %d %d %d)", lat, lon, minLat, minLong, maxLat, maxLong)); + if (lat <= minLat+tolerance) { + return new EdgeHit(0, ((double)(lon - minLong))/(maxLong-minLong)); + } + else if (lon >= maxLong-tolerance) { + return new EdgeHit(1, ((double)(lat - minLat))/(maxLat-minLat)); + } + else if (lat >= maxLat-tolerance) { + return new EdgeHit(2, ((double)(maxLong - lon))/(maxLong-minLong)); + } + else if (lon <= minLong+tolerance) { + return new EdgeHit(3, ((double)(maxLat - lat))/(maxLat-minLat)); + } + else + return null; + } + + private void concatenateWays(List<Way> ways) { + Map<Coord, Way> beginMap = new HashMap(); + + for (Way w : ways) { + if (!w.isClosed()) { + List<Coord> points = w.getPoints(); + beginMap.put(points.get(0), w); + } + } + + int merged = 1; + while (merged > 0) { + merged = 0; + for (Way w1 : ways) { + if (w1.isClosed()) continue; + + List<Coord> points1 = w1.getPoints(); + Way w2 = beginMap.get(points1.get(points1.size()-1)); + if (w2 != null) { + log.info("merging: ", ways.size(), w1.getId(), w2.getId()); + List<Coord> points2 = w2.getPoints(); + Way wm; + if (w1.getId() < (1L << 62)) { + wm = new Way((1L << 62) + nextFakeId++); + ways.remove(w1); + ways.add(wm); + wm.getPoints().addAll(points1); + beginMap.put(points1.get(0), wm); + } + else { + wm = w1; + } + wm.getPoints().addAll(points2); + ways.remove(w2); + beginMap.remove(points2.get(0)); + merged++; + break; + } + } + } + } } Index: src/uk/me/parabola/mkgmap/reader/osm/Element.java =================================================================== --- src/uk/me/parabola/mkgmap/reader/osm/Element.java (Revision 1135) +++ src/uk/me/parabola/mkgmap/reader/osm/Element.java (Arbeitskopie) @@ -86,6 +86,7 @@ * element. */ public void copyTags(Element other) { + if (other.tags != null) tags = other.tags.copy(); } @@ -97,4 +98,8 @@ if (this.name == null) this.name = name; } + + public Tags getTags() { + return tags; + } } Index: src/uk/me/parabola/mkgmap/reader/osm/Tags.java =================================================================== --- src/uk/me/parabola/mkgmap/reader/osm/Tags.java (Revision 1135) +++ src/uk/me/parabola/mkgmap/reader/osm/Tags.java (Arbeitskopie) @@ -16,7 +16,9 @@ */ package uk.me.parabola.mkgmap.reader.osm; +import java.util.HashMap; import java.util.Iterator; +import java.util.Map; /** * Store the tags that belong to an Element. @@ -120,7 +122,7 @@ } return null; } - + /** * Make a deep copy of this object. * @return A copy of this object. @@ -271,4 +273,22 @@ put(e.key, e.value); } } -} + + public void removeAll() { + for (int i = 0; i < capacity; i++){ + keys[i] = null; + values[i] = null; + } + size = 0; + } + + public Map<String, String> getKeyValues() { + Map<String, String> tagMap = new HashMap<String, String>(); + for (int i = 0; i < capacity; i++) + if (keys[i] != null && values[i] != null) + tagMap.put(keys[i], values[i]); + return tagMap; + } + + +} \ No newline at end of file Index: src/uk/me/parabola/mkgmap/filters/PolygonSplitterBase.java =================================================================== --- src/uk/me/parabola/mkgmap/filters/PolygonSplitterBase.java (Revision 1135) +++ src/uk/me/parabola/mkgmap/filters/PolygonSplitterBase.java (Arbeitskopie) @@ -30,14 +30,19 @@ */ public class PolygonSplitterBase extends BaseFilter { protected static final int MAX_SIZE = 0x7fff/2; - + protected boolean dontSplit = false; + /** * Split the given shape and place the resulting shapes in the outputs list. * @param shape The original shape (that is too big). * @param outputs The output list. */ protected void split(MapShape shape, List<MapShape> outputs) { - + if (dontSplit) { + outputs.add(shape); + return; + } + else { // Convert to a awt polygon Polygon polygon = new Polygon(); for (Coord co : shape.getPoints()) { @@ -73,6 +78,7 @@ areaToShapes(shape, a1, outputs); areaToShapes(shape, a2, outputs); + } } /** @@ -93,7 +99,7 @@ while (!pit.isDone()) { int type = pit.currentSegment(res); - //System.out.println("T" + type + " " + res[0] + "," + res[1] + " " + res[2] + "," + res[3] + " " + res[4] + "," + res[5]); + //System.out.println("T" + type + " " + res[0] + "," + res[1] + " " + res[2] + "," + res[3] + " " + res[4] + "," + res[5] + ": " + pit.getWindingRule()); Coord co = new Coord(Math.round(res[1]), Math.round(res[0])); if (type == PathIterator.SEG_MOVETO) { Index: src/uk/me/parabola/mkgmap/general/MapShape.java =================================================================== --- src/uk/me/parabola/mkgmap/general/MapShape.java (Revision 1135) +++ src/uk/me/parabola/mkgmap/general/MapShape.java (Arbeitskopie) @@ -67,6 +67,19 @@ return contains(this.getPoints(), co, true); } + /** + * Checks if a point is contained within this shape. An additional + * option allows to control, whether points on the edge of the shape + * are considered inside. + * + * @param co point to check + * @param onLineIsInside controls, whether points on the edge are considered inside + * @return true if point is in shape, false otherwise + */ + public boolean contains(Coord co, boolean onLineIsInside) { + return contains(this.getPoints(), co, onLineIsInside); + } + /* * Checks if a point is contained within a shape. * Index: resources/styles/default/polygons =================================================================== --- resources/styles/default/polygons (Revision 1135) +++ resources/styles/default/polygons (Arbeitskopie) @@ -54,6 +54,7 @@ natural=mud [0x51 resolution 20] natural=scrub [0x4f resolution 20] natural=water [0x3c resolution 20] +natural=sea [0x32 resolution 10] natural=wood [0x50 resolution 18] place=village [0x03 resolution 18]

Cannot build this patch using latest trunk (1135): maning@cumingi:~/osm/routable_garmin/mkgmap/trunk$ ant distBuildfile: build.xml prepare: compile: [javac] Compiling 7 source files to /home/maning/osm/routable_garmin/mkgmap/trunk/build/classes [javac] /home/maning/osm/routable_garmin/mkgmap/trunk/src/uk/me/parabola/mkgmap/reader/osm/MultiPolygonRelation.java:10: cannot find symbol [javac] symbol : class MultiShapeMerger [javac] location: package uk.me.parabola.mkgmap.general [javac] import uk.me.parabola.mkgmap.general.MultiShapeMerger; [javac] ^ [javac] /home/maning/osm/routable_garmin/mkgmap/trunk/src/uk/me/parabola/mkgmap/reader/osm/MultiPolygonRelation.java:99: cannot find symbol [javac] symbol : class MultiShapeMerger [javac] location: class uk.me.parabola.mkgmap.reader.osm.MultiPolygonRelation [javac] MultiShapeMerger merger = new MultiShapeMerger(outer_s); [javac] ^ [javac] /home/maning/osm/routable_garmin/mkgmap/trunk/src/uk/me/parabola/mkgmap/reader/osm/MultiPolygonRelation.java:99: cannot find symbol [javac] symbol : class MultiShapeMerger [javac] location: class uk.me.parabola.mkgmap.reader.osm.MultiPolygonRelation [javac] MultiShapeMerger merger = new MultiShapeMerger(outer_s); [javac] ^ [javac] Note: /home/maning/osm/routable_garmin/mkgmap/trunk/src/uk/me/parabola/mkgmap/reader/osm/xml/Osm5XmlHandler.java uses unchecked or unsafe operations. [javac] Note: Recompile with -Xlint:unchecked for details. [javac] 3 errors BUILD FAILED /home/maning/osm/routable_garmin/mkgmap/trunk/build.xml:71: Compile failed; see the compiler error output for details. Total time: 2 seconds On Sun, Aug 16, 2009 at 6:49 AM, Christian Gawron<christian.gawron@gmx.de> wrote:
With this version of the patch, the intersection of the bounding box and the landmass does not have to be simply connected as in version 3 of the patch.
There are still some "flooded island" in ireland, but I'm pretty sure this is a problem in the multipolygon code this patch relies on (it just add one sea polygon and constructs a multipolygon with all land components as inner ways).
Best wishes Christian
Index: src/uk/me/parabola/mkgmap/reader/osm/MultiPolygonRelation.java =================================================================== --- src/uk/me/parabola/mkgmap/reader/osm/MultiPolygonRelation.java (Revision 1135) +++ src/uk/me/parabola/mkgmap/reader/osm/MultiPolygonRelation.java (Arbeitskopie) @@ -1,12 +1,13 @@ package uk.me.parabola.mkgmap.reader.osm;
import java.util.ArrayList; -import java.util.Collection; +import java.util.Iterator; import java.util.List; import java.util.Map;
-import uk.me.parabola.imgfmt.Utils; import uk.me.parabola.imgfmt.app.Coord; +import uk.me.parabola.mkgmap.general.MapShape; +import uk.me.parabola.mkgmap.general.MultiShapeMerger;
/** * Representation of an OSM Multipolygon Relation. @@ -16,15 +17,19 @@ */ public class MultiPolygonRelation extends Relation { private Way outer; - private final Collection<Way> inners = new ArrayList<Way>(); + private List<Way> outers = new ArrayList<Way>(); + private List<Way> inners = new ArrayList<Way>(); + private Map<Long, Way> myWayMap;
/** * Create an instance based on an exsiting relation. We need to do * this because the type of the relation is not known until after all * its tags are read in. * @param other The relation to base this one on. + * @param wayMap Map of all ways. */ - public MultiPolygonRelation(Relation other) { + public MultiPolygonRelation(Relation other, Map<Long, Way> wayMap) { + myWayMap = wayMap; setId(other.getId()); for (Map.Entry<Element, String> pairs: other.getRoles().entrySet()){ addElement(pairs.getValue(), pairs.getKey()); @@ -33,10 +38,12 @@
if (value != null && pairs.getKey() instanceof Way) { Way way = (Way) pairs.getKey(); - if (value.equals("outer")) - outer = way; - else if (value.equals("inner")) + if (value.equals("outer")){ + outers.add(way); + } + else if (value.equals("inner")){ inners.add(way); + } } }
@@ -45,18 +52,120 @@ }
/** Process the ways in this relation. + * Joins way with the role "outer" * Adds ways with the role "inner" to the way with the role "outer" */ + public void processElementsNew() { + if (outers != null) + { + // copy first outer way + Iterator<Way> it = outers.iterator(); + if (it.hasNext()){ + // duplicate outer way and remove tags for cascaded multipolygons + Way tempWay = it.next(); + outer = new Way(-tempWay.getId()); + outer.copyTags(tempWay); + for(Coord point: tempWay.getPoints()){ + outer.addPoint(point); + } + myWayMap.put(outer.getId(), outer); + if (tempWay.getTags() != null){ + tempWay.getTags().removeAll(); + } + it.remove(); + } + + // if we have more than one outer way, we join them if they are parts of a long way + it = outers.iterator(); + while (it.hasNext()){ + Way tempWay = it.next(); + if (tempWay.getPoints().get(0) == outer.getPoints().get(outer.getPoints().size()-1)){ + for(Coord point: tempWay.getPoints()){ + outer.addPoint(point); + } + if (tempWay.getTags() != null){ + tempWay.getTags().removeAll(); + } + it.remove(); + it = outers.iterator(); + } + } + } + + if (outer != null) + { + MapShape outer_s = new MapShape(); + outer_s.setPoints(outer.getPoints()); + MultiShapeMerger merger = new MultiShapeMerger(outer_s); + + for (Way w: inners) { + if (w != null) { + MapShape inner_s = new MapShape(); + inner_s.setPoints(w.getPoints()); + merger.addInner(inner_s); + } + } + + merger.processElements(); + } + } + + public void processElements() { - if (outer != null) - { + + if (outers != null) + { + // copy first outer way + Iterator<Way> it = outers.iterator(); + if (it.hasNext()){ + // duplicate outer way and remove tags for cascaded multipolygons + Way tempWay = it.next(); + outer = new Way(-tempWay.getId()); + outer.copyTags(tempWay); + for(Coord point: tempWay.getPoints()){ + outer.addPoint(point); + } + myWayMap.put(outer.getId(), outer); + if (tempWay.getTags() != null){ + tempWay.getTags().removeAll(); + } + it.remove(); + } + + // if we have more than one outer way, we join them if they are parts of a long way + it = outers.iterator(); + while (it.hasNext()){ + Way tempWay = it.next(); + if (tempWay.getPoints().get(0) == outer.getPoints().get(outer.getPoints().size()-1)){ + for(Coord point: tempWay.getPoints()){ + outer.addPoint(point); + } + if (tempWay.getTags() != null){ + tempWay.getTags().removeAll(); + } + it.remove(); + it = outers.iterator(); + } + } + for (Way w: inners) { - if (w != null) { - List<Coord> pts = w.getPoints(); - int[] insert = findCpa(outer.getPoints(), pts); - if (insert[0] > 0) - insertPoints(pts, insert[0], insert[1]); - pts.clear(); + if (w != null && outer!= null) { + int[] insert = findCpa(outer.getPoints(), w.getPoints()); + if (insert[0] >= 0) + insertPoints(w, insert[0], insert[1]); + + // remove tags from inner way that are available in the outer way + if (outer.getTags() != null){ + for (Map.Entry<String, String> mapTags: outer.getTags().getKeyValues().entrySet()){ + String key = mapTags.getKey(); + String value = mapTags.getValue(); + if (w.getTag(key) != null){ + if (w.getTag(key).equals(value)){ + w.deleteTag(key); + } + } + } + } } } } @@ -64,22 +173,43 @@
/** * Insert Coordinates into the outer way. - * @param inList List of Coordinates to be inserted + * @param way Way to be inserted * @param out Coordinates will be inserted after this point in the outer way. * @param in Points will be inserted starting at this index, * then from element 0 to (including) this element; */ - private void insertPoints(List<Coord> inList, int out, int in){ + private void insertPoints(Way way, int out, int in){ List<Coord> outList = outer.getPoints(); + List<Coord> inList = way.getPoints(); int index = out+1; - for (int i = in; i < inList.size(); i++) + for (int i = in; i < inList.size(); i++){ outList.add(index++, inList.get(i)); - for (int i = 0; i <= in; i++) + } + for (int i = 0; i < in; i++){ outList.add(index++, inList.get(i)); - - //with this line commented we get triangles, when uncommented some areas disappear - // at least in mapsource, on device itself looks OK. - outList.add(index,outList.get(out)); + } + + if (outer.getPoints().size() < 32){ + outList.add(index++, inList.get(in)); + outList.add(index, outList.get(out)); + } + else{ + // we shift the nodes to avoid duplicate nodes (large areas only) + int oLat = outList.get(out).getLatitude(); + int oLon = outList.get(out).getLongitude(); + int iLat = inList.get(in).getLatitude(); + int iLon = inList.get(in).getLongitude(); + if (Math.abs(oLat - iLat) > Math.abs(oLon - iLon)){ + int delta = (oLon > iLon)? -1 : 1; + outList.add(index++, new Coord(iLat + delta, iLon)); + outList.add(index, new Coord(oLat + delta, oLon)); + } + else{ + int delta = (oLat > iLat)? 1 : -1; + outList.add(index++, new Coord(iLat, iLon + delta)); + outList.add(index, new Coord(oLat, oLon + delta)); + } + } }
/** Index: src/uk/me/parabola/mkgmap/reader/osm/Way.java =================================================================== --- src/uk/me/parabola/mkgmap/reader/osm/Way.java (Revision 1135) +++ src/uk/me/parabola/mkgmap/reader/osm/Way.java (Arbeitskopie) @@ -76,6 +76,10 @@ } }
+ public boolean isClosed() { + return points.size() > 0 && points.get(0).equals(points.get(points.size()-1)); + } + /** * A simple representation of this way. * @return A string with the name and start point Index: src/uk/me/parabola/mkgmap/reader/osm/xml/Osm5XmlHandler.java =================================================================== --- src/uk/me/parabola/mkgmap/reader/osm/xml/Osm5XmlHandler.java (Revision 1135) +++ src/uk/me/parabola/mkgmap/reader/osm/xml/Osm5XmlHandler.java (Arbeitskopie) @@ -19,9 +19,13 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.IdentityHashMap; +import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.NavigableSet;
import uk.me.parabola.imgfmt.app.Area; import uk.me.parabola.imgfmt.app.Coord; @@ -65,6 +69,7 @@ private final Map<String, Long> fakeIdMap = new HashMap<String, Long>(); private final List<Node> exits = new ArrayList<Node>(); private final List<Way> motorways = new ArrayList<Way>(); + private final List<Way> shoreline = new ArrayList<Way>();
private static final int MODE_NODE = 1; private static final int MODE_WAY = 2; @@ -92,6 +97,7 @@ private final boolean ignoreTurnRestrictions; private final boolean linkPOIsToWays; private final boolean routing; + private final boolean generateSea; private final Double minimumArcLength; private final String frigRoundabouts;
@@ -105,6 +111,7 @@ } linkPOIsToWays = props.getProperty("link-pois-to-ways", false); ignoreBounds = props.getProperty("ignore-osm-bounds", false); + generateSea = props.getProperty("generate-sea", false); routing = props.containsKey("route"); String rsa = props.getProperty("remove-short-arcs", null); if(rsa != null) @@ -370,6 +377,8 @@ if("motorway".equals(highway) || "trunk".equals(highway)) motorways.add(currentWay); + if(generateSea && "coastline".equals(currentWay.getTag("natural"))) + shoreline.add(currentWay); currentWay = null; // ways are processed at the end of the document, // may be changed by a Relation class @@ -399,7 +408,7 @@ String type = currentRelation.getTag("type"); if (type != null) { if ("multipolygon".equals(type)) - currentRelation = new MultiPolygonRelation(currentRelation); + currentRelation = new MultiPolygonRelation(currentRelation, wayMap); else if("restriction".equals(type)) {
if(ignoreTurnRestrictions) @@ -446,6 +455,10 @@ }
coordMap = null; + + if (generateSea) + generateSeaPolygon(shoreline); + for (Relation r : relationMap.values()) converter.convertRelation(r);
@@ -707,7 +720,7 @@ } currentWay.addPoint(co); co.incHighwayCount(); // nodes (way joins) will have highwayCount > 1 - if(minimumArcLength != null) + if (minimumArcLength != null || generateSea) nodeIdMap.put(co, id); } } @@ -746,4 +759,294 @@ return fakeIdVal; } } + + private void generateSeaPolygon(List<Way> shoreline) { + + Area seaBounds; + if (bbox != null) + seaBounds = bbox; + else + seaBounds = mapper.getBounds(); + + log.info("generating sea, seaBounds=", seaBounds); + int minLat = seaBounds.getMinLat(); + int maxLat = seaBounds.getMaxLat(); + int minLong = seaBounds.getMinLong(); + int maxLong = seaBounds.getMaxLong(); + Coord nw = new Coord(minLat, minLong); + Coord ne = new Coord(minLat, maxLong); + Coord sw = new Coord(maxLat, minLong); + Coord se = new Coord(maxLat, maxLong); + + long seaId = (1L << 62) + nextFakeId++; + Way sea = new Way(seaId); + sea.addPoint(nw); + sea.addPoint(sw); + sea.addPoint(se); + sea.addPoint(ne); + sea.addPoint(nw); + sea.addTag("natural", "sea"); + log.info("sea: ", sea); + wayMap.put(seaId, sea); + + long multiId = (1L << 62) + nextFakeId++; + Relation seaRelation = new GeneralRelation(multiId); + seaRelation.addTag("type", "multipolygon"); + seaRelation.addElement("outer", sea); + + List<Way> islands = new ArrayList(); + + // handle islands (closes shoreline components) first (they're easy) + Iterator<Way> it = shoreline.iterator(); + while (it.hasNext()) { + Way w = it.next(); + if (w.isClosed()) { + islands.add(w); + it.remove(); + } + } + concatenateWays(shoreline); + // there may be more islands now + it = shoreline.iterator(); + while (it.hasNext()) { + Way w = it.next(); + if (w.isClosed()) { + log.debug("island after concatenating\n"); + islands.add(w); + it.remove(); + } + } + + // create a "inner" way for each island + for (Way w : islands) { + log.info("adding island " + w); + seaRelation.addElement("inner", w); + } + + // the remaining shoreline segments should intersect the boundary + // find the intersection points and store them in a SortedMap + SortedMap<EdgeHit, Way> hitMap = new TreeMap<EdgeHit, Way>(); + for (Way w : shoreline) { + List<Coord> points = w.getPoints(); + Coord pStart = points.get(0); + Coord pEnd = points.get(points.size()-1); + + EdgeHit hStart = getEdgeHit(seaBounds, pStart); + EdgeHit hEnd = getEdgeHit(seaBounds, pEnd); + if (hStart == null || hEnd == null) { + String msg = String.format("Non-closed coastline segment does not hit bounding box - expect strange results: %d (%s) %d (%s) %s\n", + nodeIdMap.get(pStart), pStart.toDegreeString(), + nodeIdMap.get(pEnd), pEnd.toDegreeString(), + pStart.toOSMURL()); + log.error(msg); + seaRelation.addElement("inner", w); + } + else { + log.debug("hits: ", hStart, hEnd); + hitMap.put(hStart, w); + hitMap.put(hEnd, null); + } + } + + + // now construct inner ways from these segments + NavigableSet<EdgeHit> hits = (NavigableSet<EdgeHit>) hitMap.keySet(); + while (hits.size() > 0) { + long id = (1L << 62) + nextFakeId++; + Way w = new Way(id); + wayMap.put(id, w); + + EdgeHit hit = hits.first(); + EdgeHit hFirst = hit; + EdgeHit hNext; + do { + Way segment = hitMap.get(hit); + log.info("current hit: " + hit); + if (segment != null) { + // add the segment and get the "ending hit" + log.info("adding: ", segment); + w.getPoints().addAll(segment.getPoints()); + hNext = getEdgeHit(seaBounds, segment.getPoints().get(segment.getPoints().size()-1)); + } + else { + w.addPoint(hit.getPoint(seaBounds)); + hNext = hits.higher(hit); + if (hNext == null) + hNext = hFirst; + + Coord p = hit.getPoint(seaBounds); + if (hit.edge < hNext.edge) { + log.info("joining: ", hit, hNext); + for (int i=hit.edge; i<hNext.edge; i++) { + EdgeHit corner = new EdgeHit(i, 1.0); + p = corner.getPoint(seaBounds); + log.debug("way: ", corner, p); + w.addPoint(p); + } + } + else if (hit.edge > hNext.edge) { + log.info("joining: ", hit, hNext); + for (int i=hit.edge; i<4; i++) { + EdgeHit corner = new EdgeHit(i, 1.0); + p = corner.getPoint(seaBounds); + log.debug("way: ", corner, p); + w.addPoint(p); + } + for (int i=0; i<hNext.edge; i++) { + EdgeHit corner = new EdgeHit(i, 1.0); + p = corner.getPoint(seaBounds); + log.debug("way: ", corner, p); + w.addPoint(p); + } + } + w.addPoint(hNext.getPoint(seaBounds)); + } + hits.remove(hit); + hit = hNext; + } while (hits.size() > 0 && !hit.equals(hFirst)); + + if (!w.isClosed()) + w.getPoints().add(w.getPoints().get(0)); + w.addTag("highway", "primary"); + log.info("adding non-island landmass, hits.size()=" + hits.size()); + seaRelation.addElement("inner", w); + } + + seaRelation = new MultiPolygonRelation(seaRelation, wayMap); + relationMap.put(multiId, seaRelation); + seaRelation.processElements(); + } + + /** + * Specifies where an edge of the bounding box is hit. + */ + private static class EdgeHit implements Comparable<EdgeHit> + { + int edge; + double t; + + EdgeHit(int edge, double t) { + this.edge = edge; + this.t = t; + } + + public int compareTo(EdgeHit o) { + if (edge < o.edge) + return -1; + else if (edge > o.edge) + return +1; + else if (t > o.t) + return +1; + else if (t < o.t) + return -1; + else + return 0; + } + + @Override public boolean equals(Object o) { + if (o instanceof EdgeHit) { + EdgeHit h = (EdgeHit) o; + return (h.edge == edge && Double.compare(h.t, t) == 0); + } + else + return false; + } + + Coord getPoint(Area a) { + log.info("getPoint: ", this, a); + switch (edge) { + case 0: + return new Coord(a.getMinLat(), (int) (a.getMinLong() + t * (a.getMaxLong()-a.getMinLong()))); + + case 1: + return new Coord((int)(a.getMinLat() + t * (a.getMaxLat()-a.getMinLat())), a.getMaxLong()); + + case 2: + return new Coord(a.getMaxLat(), (int)(a.getMaxLong() - t * (a.getMaxLong()-a.getMinLong()))); + + case 3: + return new Coord((int)(a.getMaxLat() - t * (a.getMaxLat()-a.getMinLat())), a.getMinLong()); + + default: + throw new RuntimeException("illegal state"); + } + } + + public String toString() { + return "EdgeHit " + edge + "@" + t; + } + } + + private EdgeHit getEdgeHit(Area a, Coord p) + { + return getEdgeHit(a, p, 10); + } + + private EdgeHit getEdgeHit(Area a, Coord p, int tolerance) + { + int lat = p.getLatitude(); + int lon = p.getLongitude(); + int minLat = a.getMinLat(); + int maxLat = a.getMaxLat(); + int minLong = a.getMinLong(); + int maxLong = a.getMaxLong(); + + log.info(String.format("getEdgeHit: (%d %d) (%d %d %d %d)", lat, lon, minLat, minLong, maxLat, maxLong)); + if (lat <= minLat+tolerance) { + return new EdgeHit(0, ((double)(lon - minLong))/(maxLong-minLong)); + } + else if (lon >= maxLong-tolerance) { + return new EdgeHit(1, ((double)(lat - minLat))/(maxLat-minLat)); + } + else if (lat >= maxLat-tolerance) { + return new EdgeHit(2, ((double)(maxLong - lon))/(maxLong-minLong)); + } + else if (lon <= minLong+tolerance) { + return new EdgeHit(3, ((double)(maxLat - lat))/(maxLat-minLat)); + } + else + return null; + } + + private void concatenateWays(List<Way> ways) { + Map<Coord, Way> beginMap = new HashMap(); + + for (Way w : ways) { + if (!w.isClosed()) { + List<Coord> points = w.getPoints(); + beginMap.put(points.get(0), w); + } + } + + int merged = 1; + while (merged > 0) { + merged = 0; + for (Way w1 : ways) { + if (w1.isClosed()) continue; + + List<Coord> points1 = w1.getPoints(); + Way w2 = beginMap.get(points1.get(points1.size()-1)); + if (w2 != null) { + log.info("merging: ", ways.size(), w1.getId(), w2.getId()); + List<Coord> points2 = w2.getPoints(); + Way wm; + if (w1.getId() < (1L << 62)) { + wm = new Way((1L << 62) + nextFakeId++); + ways.remove(w1); + ways.add(wm); + wm.getPoints().addAll(points1); + beginMap.put(points1.get(0), wm); + } + else { + wm = w1; + } + wm.getPoints().addAll(points2); + ways.remove(w2); + beginMap.remove(points2.get(0)); + merged++; + break; + } + } + } + } } Index: src/uk/me/parabola/mkgmap/reader/osm/Element.java =================================================================== --- src/uk/me/parabola/mkgmap/reader/osm/Element.java (Revision 1135) +++ src/uk/me/parabola/mkgmap/reader/osm/Element.java (Arbeitskopie) @@ -86,6 +86,7 @@ * element. */ public void copyTags(Element other) { + if (other.tags != null) tags = other.tags.copy(); }
@@ -97,4 +98,8 @@ if (this.name == null) this.name = name; } + + public Tags getTags() { + return tags; + } } Index: src/uk/me/parabola/mkgmap/reader/osm/Tags.java =================================================================== --- src/uk/me/parabola/mkgmap/reader/osm/Tags.java (Revision 1135) +++ src/uk/me/parabola/mkgmap/reader/osm/Tags.java (Arbeitskopie) @@ -16,7 +16,9 @@ */ package uk.me.parabola.mkgmap.reader.osm;
+import java.util.HashMap; import java.util.Iterator; +import java.util.Map;
/** * Store the tags that belong to an Element. @@ -120,7 +122,7 @@ } return null; } - + /** * Make a deep copy of this object. * @return A copy of this object. @@ -271,4 +273,22 @@ put(e.key, e.value); } } -} + + public void removeAll() { + for (int i = 0; i < capacity; i++){ + keys[i] = null; + values[i] = null; + } + size = 0; + } + + public Map<String, String> getKeyValues() { + Map<String, String> tagMap = new HashMap<String, String>(); + for (int i = 0; i < capacity; i++) + if (keys[i] != null && values[i] != null) + tagMap.put(keys[i], values[i]); + return tagMap; + } + + +} \ No newline at end of file Index: src/uk/me/parabola/mkgmap/filters/PolygonSplitterBase.java =================================================================== --- src/uk/me/parabola/mkgmap/filters/PolygonSplitterBase.java (Revision 1135) +++ src/uk/me/parabola/mkgmap/filters/PolygonSplitterBase.java (Arbeitskopie) @@ -30,14 +30,19 @@ */ public class PolygonSplitterBase extends BaseFilter { protected static final int MAX_SIZE = 0x7fff/2; - + protected boolean dontSplit = false; + /** * Split the given shape and place the resulting shapes in the outputs list. * @param shape The original shape (that is too big). * @param outputs The output list. */ protected void split(MapShape shape, List<MapShape> outputs) { - + if (dontSplit) { + outputs.add(shape); + return; + } + else { // Convert to a awt polygon Polygon polygon = new Polygon(); for (Coord co : shape.getPoints()) { @@ -73,6 +78,7 @@
areaToShapes(shape, a1, outputs); areaToShapes(shape, a2, outputs); + } }
/** @@ -93,7 +99,7 @@ while (!pit.isDone()) { int type = pit.currentSegment(res);
- //System.out.println("T" + type + " " + res[0] + "," + res[1] + " " + res[2] + "," + res[3] + " " + res[4] + "," + res[5]); + //System.out.println("T" + type + " " + res[0] + "," + res[1] + " " + res[2] + "," + res[3] + " " + res[4] + "," + res[5] + ": " + pit.getWindingRule()); Coord co = new Coord(Math.round(res[1]), Math.round(res[0]));
if (type == PathIterator.SEG_MOVETO) { Index: src/uk/me/parabola/mkgmap/general/MapShape.java =================================================================== --- src/uk/me/parabola/mkgmap/general/MapShape.java (Revision 1135) +++ src/uk/me/parabola/mkgmap/general/MapShape.java (Arbeitskopie) @@ -67,6 +67,19 @@ return contains(this.getPoints(), co, true); }
+ /** + * Checks if a point is contained within this shape. An additional + * option allows to control, whether points on the edge of the shape + * are considered inside. + * + * @param co point to check + * @param onLineIsInside controls, whether points on the edge are considered inside + * @return true if point is in shape, false otherwise + */ + public boolean contains(Coord co, boolean onLineIsInside) { + return contains(this.getPoints(), co, onLineIsInside); + } + /* * Checks if a point is contained within a shape. * Index: resources/styles/default/polygons =================================================================== --- resources/styles/default/polygons (Revision 1135) +++ resources/styles/default/polygons (Arbeitskopie) @@ -54,6 +54,7 @@ natural=mud [0x51 resolution 20] natural=scrub [0x4f resolution 20] natural=water [0x3c resolution 20] +natural=sea [0x32 resolution 10] natural=wood [0x50 resolution 18]
place=village [0x03 resolution 18]
_______________________________________________ mkgmap-dev mailing list mkgmap-dev@lists.mkgmap.org.uk http://www.mkgmap.org.uk/mailman/listinfo/mkgmap-dev
-- cheers, maning ------------------------------------------------------ "Freedom is still the most radical idea of all" -N.Branden wiki: http://esambale.wikispaces.com/ blog: http://epsg4253.wordpress.com/ ------------------------------------------------------

On Sun, Aug 16, 2009 at 12:49 AM, Christian Gawron<christian.gawron@gmx.de> wrote:
With this version of the patch, the intersection of the bounding box and the landmass does not have to be simply connected as in version 3 of the patch.
Hm... I tried out this patch with a tiled map of Germany. The results were kind of amusing: pretty much all of Germany was flooded, with the exception of some areas around Lübeck. I suppose this is because I am using an extract (from Geofabrik) which cuts off coastline at the country border. Creating automatic sea polygons which will work in practice appears to be a rather non-trivial problem. Cheers and thanks for your work on this.

On 09-08-17 17:38:46 CEST, Clinton Gladstone wrote:
On Sun, Aug 16, 2009 at 12:49 AM, Christian Gawron<christian.gawron@gmx.de> wrote:
With this version of the patch, the intersection of the bounding box and the landmass does not have to be simply connected as in version 3 of the patch.
Hm... I tried out this patch with a tiled map of Germany. The results were kind of amusing: pretty much all of Germany was flooded, with the exception of some areas around Lübeck. I suppose this is because I am using an extract (from Geofabrik) which cuts off coastline at the country border.
same here: different input, similar result. using osmosis, i've cut a bbox from planet.osm, from northern germany to croatia. some observations: - one danish island at the northern end of the bounding box has the bbox edge cutting through it. but what is displayed of the island does not reach to the top of the bbox, instead there's a strip of sea, some 600 m wide (which is fake, in reality there's (is)land cover). - its sister island, equally cut through by the bbox edge, is flooded. - main-land reaching into the bbox from the left exhibits the same as the first island, i.e. has a fake strip of sea, some 900 m wide. - a number of islands seem to come out perfectly, land surrounded by sea. - even with most of germany flooded, there's a strip at the coast which is dry--interestingly the dividing line through the land is not exactly east-west but goes from about N54.03 E11.6 to N54.16 E13.3. following this line further to the west, land and sea are sometimes inverse, i.e. the sea is shown as land, the land as sea. it toggles where this line and the coastline cross. - one tile (northern germany/poland at the coast) is fine, but the tiles further inland are flooded... there were nine warnings of the form SEVERE (Osm5XmlHandler): Non-closed coastline segment does not hit bounding box - expect strange results: 25010841 (54.17039/13.35783) 21906107 (53.99328/11.00206) http://www.openstreetmap.org/?lat=54.17039&lon=13.35783&zoom=17 i'm wondering how this can be fixed, by - different use of osmosis (unlikely as some warnings come from inside the bbox?) - changing the splitter (some problems seem to be tile related?) - changing the sea patch - fixing osm data--some problems seem to be at neither bbox nor tile edge, for example lastovo and its sister islands are flooded http://www.openstreetmap.org/?lat=42.751&lon=16.86&zoom=11&layers=B000FTF thank you, rj

Thanks for the feedback! One trivial bug in the patch was that a tile with no shoreline at all got a sea polygon as "background". This can be fixed by adding // don't do anything if there is no shoreline if (shoreline.size() == 0) return; at the begin of generateSeaPolygon(). Am just testing with germany to see wether there are other problems with cut-off shorelines and will post an improved patch this weekend. Best wishes Christian Clinton Gladstone schrieb:
On Sun, Aug 16, 2009 at 12:49 AM, Christian Gawron<christian.gawron@gmx.de> wrote:
With this version of the patch, the intersection of the bounding box and the landmass does not have to be simply connected as in version 3 of the patch.
Hm... I tried out this patch with a tiled map of Germany. The results were kind of amusing: pretty much all of Germany was flooded, with the exception of some areas around Lübeck. I suppose this is because I am using an extract (from Geofabrik) which cuts off coastline at the country border.
Creating automatic sea polygons which will work in practice appears to be a rather non-trivial problem.
Cheers and thanks for your work on this. _______________________________________________ mkgmap-dev mailing list mkgmap-dev@lists.mkgmap.org.uk http://www.mkgmap.org.uk/mailman/listinfo/mkgmap-dev

On 09-08-20 21:42:23 CEST, Christian Gawron wrote:
Thanks for the feedback!
One trivial bug in the patch was that a tile with no shoreline at all got a sea polygon as "background". This can be fixed by adding // don't do anything if there is no shoreline if (shoreline.size() == 0) return; at the begin of generateSeaPolygon().
great! is was about to report this. to facilitate testing, i've created smaller areas to investigate the problems (now recreated with the above patch): - brandenburg in germany: fully flooded before, now fine with above fix. - (same with prague) - a bbox part of the adriatic sea, the kvarner gulf: - there is an invisible diagonal line across the whole tile. - northeast of it land and water are reversed - southwest of it many islands are fine - some islands in the otherwise good half vanish in the sea, e.g. lastovo at about 42.75/16.85 (and its sister islands). i wonder why, as lastovo for example seems to have a closed natural=coastline made up of three ways joined at three nodes: way_10435346 node_89314868 way_10435380 node_89319038 way_10435451 node_89314756 (way_10435346) - the tile has a triangular area of italy in the SW corner, some 20 km of coast line: - it is mostly flooded, with the exception of what seems to be a fake strip of beach, some 1 km wide, in front of the coast. the bbox i out for the last part: left=14 bottom=42.5 right=18.5 top=45.5 the splitter produced one tile only. the diagnostics from mkgmap (the three lines repeat three times): SEVERE (Osm5XmlHandler): Non-closed coastline segment does not hit bounding box - expect strange results: 65856087 (44.80250/14.00158) 65860906 (44.88756/14.00263) http://www.openstreetmap.org/?lat=44.80250&lon=14.00158&zoom=17 SEVERE (Osm5XmlHandler): Non-closed coastline segment does not hit bounding box - expect strange results: 65865448 (44.89250/14.00023) 51492065 (42.50074/18.34468) http://www.openstreetmap.org/?lat=44.89250&lon=14.00023&zoom=17 SEVERE (Osm5XmlHandler): Non-closed coastline segment does not hit bounding box - expect strange results: 33051020 (42.50323/14.17635) 33051034 (42.70034/14.00160) http://www.openstreetmap.org/?lat=42.50323&lon=14.17635&zoom=17 44.89250/14.00023 and 42.50074/18.34468 seem to be the ending points of the above mentioned diagonal line. 42.50323/14.17635 and 42.70034/14.00160 seem to be the ending points of the virtual line through the triangle of italy mentioned above. please tell me if i can help you with any further research. thank you, rj
participants (4)
-
Christian Gawron
-
Clinton Gladstone
-
maning sambale
-
Robert Joop