Hi Gerd I've just rebuilt britain-and-ireland-latest.osm.pbf with the current trunk and find that many tiles on the coast have flooded rectangles. The coastline is all correct and these flooded area are normally well inland and unrelated to where the coastline crosses the tile boundary.
I use option --generate-sea="multipolygon,extend-sea-sectors,close-gaps=750" and this worked fine up to about a year ago - I've only just noticed the problem. Cutting this from a suitable area (Wales, UK, Britain, Europe etc) 74220030: 2408448,-331776 to 2430976,-196608 # : 51.679688,-7.119141 to 52.163086,-4.218750 and building with minimal options plus above and default style should demonstrate the problem. There are also extra error messages starting ... 030.osm.pbf: Multipolygon generated SeaPolygonRelation [nat... in the log file, which I attach I think it is related to the faster-mp changes and the MP structure SeaGenerator created. This has 1 outer (the tile) and all bits of land are inners, including land touching the edge of the tile. I've attached a patch for an enhanced version of SeaGenerator that distinguishes between land/sea areas that touch the edge and true [anti-]islands and only use MP processing for the islands. With this, tiles are correct. It also has other benefits: It is clearer in what is happening, doesn't do a lot of pointless tagging of ways that will joined to become polygons, etc Ticker
FINE: uk.me.parabola.mkgmap.main.Main option: number-of-files 1 FINE: uk.me.parabola.mkgmap.main.Main option: mapname 63240001 FINE: uk.me.parabola.mkgmap.main.Main option: description OSM street map FINE: uk.me.parabola.mkgmap.main.Main option: overview-mapname osmmap FINE: uk.me.parabola.mkgmap.main.Main option: overview-mapnumber 63240000 FINE: uk.me.parabola.mkgmap.main.Main option: poi-address FINE: uk.me.parabola.mkgmap.main.Main option: merge-lines FINE: uk.me.parabola.mkgmap.main.Main option: area-name fldfA FINE: uk.me.parabola.mkgmap.main.Main option: code-page 1252 FINE: uk.me.parabola.mkgmap.main.Main option: drive-on left FINE: uk.me.parabola.mkgmap.main.Main option: family-id 7420 FINE: uk.me.parabola.mkgmap.main.Main option: family-name fldfF FINE: uk.me.parabola.mkgmap.main.Main option: generate-sea multipolygon FINE: uk.me.parabola.mkgmap.main.Main option: mapset-name fldfM FINE: uk.me.parabola.mkgmap.main.Main option: overview-mapnumber 74200000 FINE: uk.me.parabola.mkgmap.main.Main option: preserve-element-order FINE: uk.me.parabola.mkgmap.main.Main option: region-abbr ABBR FINE: uk.me.parabola.mkgmap.main.Main option: region-name fldfR FINE: uk.me.parabola.mkgmap.main.Main option: series-name fldfS FINE: uk.me.parabola.mkgmap.main.Main option: mapname 74200001 FINE: uk.me.parabola.mkgmap.main.Main option: description fldfD FINE: uk.me.parabola.mkgmap.main.Main file ../mapGB/74220030.osm.pbf , extension is pbf INFO: uk.me.parabola.mkgmap.main.Main Submitting job ../mapGB/74220030.osm.pbf FINE: uk.me.parabola.mkgmap.main.Main option: description fldfG OVER: global Mkgmap version none OVER: global Time started: Thu May 12 06:40:41 BST 2022 INFO: uk.me.parabola.mkgmap.main.Main Start tile processors INFO: uk.me.parabola.mkgmap.main.Main Creating thread pool with 1 threads WARN: uk.me.parabola.mkgmap.reader.osm.MultiPolygonRelation ../mapGB/74220030.osm.pbf: Node with unknown role is ignored outer http://www.openstreetmap.org/node/6406767339 in multipolygon http://www.openstreetmap.org/relation/9487722 [area=yes, name=Plas Treforgan Holiday Cottages, phone=+44 1239 615430, tourism=accommodation, type=multipolygon] WARN: uk.me.parabola.mkgmap.reader.osm.MultiPolygonRelation ../mapGB/74220030.osm.pbf: Node with unknown role is ignored outer http://www.openstreetmap.org/node/6406767340 in multipolygon http://www.openstreetmap.org/relation/9487722 [area=yes, name=Plas Treforgan Holiday Cottages, phone=+44 1239 615430, tourism=accommodation, type=multipolygon] WARN: uk.me.parabola.mkgmap.reader.osm.MultiPolygonRelation ../mapGB/74220030.osm.pbf: Multipolygon http://www.openstreetmap.org/relation/10002574 [natural=wood, type=multipolygon] contains errors. WARN: uk.me.parabola.mkgmap.reader.osm.MultiPolygonRelation ../mapGB/74220030.osm.pbf: Polygon 4611686018427510963(17P)(722168720[17P]) carries role inner but lies inside an inner polygon. Potentially its role should be outer. WARN: uk.me.parabola.mkgmap.reader.osm.MultiPolygonRelation ../mapGB/74220030.osm.pbf: Multipolygon http://www.openstreetmap.org/relation/10109409 does not contain any way tagged with role=outer or empty role. WARN: uk.me.parabola.mkgmap.reader.osm.MultiPolygonRelation ../mapGB/74220030.osm.pbf: Multipolygon http://www.openstreetmap.org/relation/11046992 does not contain any way tagged with role=outer or empty role. WARN: uk.me.parabola.mkgmap.reader.osm.MultiPolygonRelation ../mapGB/74220030.osm.pbf: Way role invalid in` http://www.openstreetmap.org/way/825925725 in multipolygon http://www.openstreetmap.org/relation/11299713 [natural=water, type=multipolygon, water=pond] WARN: uk.me.parabola.mkgmap.reader.osm.MultiPolygonRelation ../mapGB/74220030.osm.pbf: Multipolygon http://www.openstreetmap.org/relation/11579011 [leisure=garden, name=Colby Woodland Garden, natural=wood, type=multipolygon] contains errors. WARN: uk.me.parabola.mkgmap.reader.osm.MultiPolygonRelation ../mapGB/74220030.osm.pbf: Polygon 4611686018427526677(6P)(262068191[6P]) carries role inner but lies inside an inner polygon. Potentially its role should be outer. WARN: uk.me.parabola.mkgmap.reader.osm.MultiPolygonRelation ../mapGB/74220030.osm.pbf: Multipolygon http://www.openstreetmap.org/relation/13048971 does not contain any way tagged with role=outer or empty role. WARN: uk.me.parabola.mkgmap.reader.osm.MultiPolygonRelation ../mapGB/74220030.osm.pbf: Multipolygon http://www.openstreetmap.org/relation/13049762 does not contain any way tagged with role=outer or empty role. WARN: uk.me.parabola.mkgmap.reader.osm.MultiPolygonRelation ../mapGB/74220030.osm.pbf: Multipolygon http://www.openstreetmap.org/relation/13061091 does not contain any way tagged with role=outer or empty role. WARN: uk.me.parabola.mkgmap.reader.osm.MultiPolygonRelation ../mapGB/74220030.osm.pbf: Non Way/Node member with role is ignored subarea http://www.openstreetmap.org/relation/62273 in multipolygon http://www.openstreetmap.org/relation/13428950 [boundary=historic, description=Ireland, name=Éire / Ireland, type=boundary] WARN: uk.me.parabola.mkgmap.reader.osm.MultiPolygonRelation ../mapGB/74220030.osm.pbf: Multipolygon generated SeaPolygonRelation [natural=sea, type=mkgmap:seapolygon] contains errors. WARN: uk.me.parabola.mkgmap.reader.osm.MultiPolygonRelation ../mapGB/74220030.osm.pbf: Polygon 4611686018427565187(5P)(4611686018427560562[45194P]) carries role inner but is not inside any other polygon. Potentially it does not belong to this multipolygon. WARN: uk.me.parabola.mkgmap.reader.osm.MultiPolygonRelation ../mapGB/74220030.osm.pbf: Way 4611686018427560562 is composed of other artificial ways. Details: WARN: uk.me.parabola.mkgmap.reader.osm.MultiPolygonRelation ../mapGB/74220030.osm.pbf: Start: http://www.openstreetmap.org/?mlat=51.679688&mlon=-4.924210&zoom=17 WARN: uk.me.parabola.mkgmap.reader.osm.MultiPolygonRelation ../mapGB/74220030.osm.pbf: Mid: http://www.openstreetmap.org/?mlat=51.899056&mlon=-5.296845&zoom=17 WARN: uk.me.parabola.mkgmap.reader.osm.MultiPolygonRelation ../mapGB/74220030.osm.pbf: Multipolygon generated SeaPolygonRelation [natural=sea, type=mkgmap:seapolygon] contains errors. WARN: uk.me.parabola.mkgmap.reader.osm.MultiPolygonRelation ../mapGB/74220030.osm.pbf: Polygon 4611686018427565260(5P)(4611686018427560562[45194P]) carries role inner but is not inside any other polygon. Potentially it does not belong to this multipolygon. WARN: uk.me.parabola.mkgmap.reader.osm.MultiPolygonRelation ../mapGB/74220030.osm.pbf: Way 4611686018427560562 is composed of other artificial ways. Details: WARN: uk.me.parabola.mkgmap.reader.osm.MultiPolygonRelation ../mapGB/74220030.osm.pbf: Start: http://www.openstreetmap.org/?mlat=51.679688&mlon=-4.924210&zoom=17 WARN: uk.me.parabola.mkgmap.reader.osm.MultiPolygonRelation ../mapGB/74220030.osm.pbf: Mid: http://www.openstreetmap.org/?mlat=51.899056&mlon=-5.296845&zoom=17 WARN: uk.me.parabola.mkgmap.reader.osm.MultiPolygonRelation ../mapGB/74220030.osm.pbf: Multipolygon generated SeaPolygonRelation [natural=sea, type=mkgmap:seapolygon] contains errors. WARN: uk.me.parabola.mkgmap.reader.osm.MultiPolygonRelation ../mapGB/74220030.osm.pbf: Polygon 4611686018427565596(5P)(4611686018427560562[45194P]) carries role inner but is not inside any other polygon. Potentially it does not belong to this multipolygon. WARN: uk.me.parabola.mkgmap.reader.osm.MultiPolygonRelation ../mapGB/74220030.osm.pbf: Way 4611686018427560562 is composed of other artificial ways. Details: WARN: uk.me.parabola.mkgmap.reader.osm.MultiPolygonRelation ../mapGB/74220030.osm.pbf: Start: http://www.openstreetmap.org/?mlat=51.679688&mlon=-4.924210&zoom=17 WARN: uk.me.parabola.mkgmap.reader.osm.MultiPolygonRelation ../mapGB/74220030.osm.pbf: Mid: http://www.openstreetmap.org/?mlat=51.899056&mlon=-5.296845&zoom=17 WARN: uk.me.parabola.mkgmap.reader.osm.MultiPolygonRelation ../mapGB/74220030.osm.pbf: Multipolygon generated SeaPolygonRelation [natural=sea, type=mkgmap:seapolygon] contains errors. WARN: uk.me.parabola.mkgmap.reader.osm.MultiPolygonRelation ../mapGB/74220030.osm.pbf: Polygon 4611686018427565688(5P)(4611686018427560562[45194P]) carries role inner but is not inside any other polygon. Potentially it does not belong to this multipolygon. WARN: uk.me.parabola.mkgmap.reader.osm.MultiPolygonRelation ../mapGB/74220030.osm.pbf: Way 4611686018427560562 is composed of other artificial ways. Details: WARN: uk.me.parabola.mkgmap.reader.osm.MultiPolygonRelation ../mapGB/74220030.osm.pbf: Start: http://www.openstreetmap.org/?mlat=51.679688&mlon=-4.924210&zoom=17 WARN: uk.me.parabola.mkgmap.reader.osm.MultiPolygonRelation ../mapGB/74220030.osm.pbf: Mid: http://www.openstreetmap.org/?mlat=51.899056&mlon=-5.296845&zoom=17 WARN: uk.me.parabola.mkgmap.filters.ShapeMergeFilter ../mapGB/74220030.osm.pbf: merging shapes skipped for shapes near http://www.openstreetmap.org/?mlat=51.991960&mlon=-4.980285&zoom=17 (maybe overlapping shapes?) WARN: uk.me.parabola.mkgmap.filters.ShapeMergeFilter ../mapGB/74220030.osm.pbf: merging shapes skipped for shapes near http://www.openstreetmap.org/?mlat=51.991960&mlon=-4.980285&zoom=17 (maybe overlapping shapes?) WARN: uk.me.parabola.mkgmap.filters.ShapeMergeFilter ../mapGB/74220030.osm.pbf: merging shapes skipped for shapes near http://www.openstreetmap.org/?mlat=51.991960&mlon=-4.980285&zoom=17 (maybe overlapping shapes?) WARN: uk.me.parabola.mkgmap.filters.ShapeMergeFilter ../mapGB/74220030.osm.pbf: merging shapes skipped for shapes near http://www.openstreetmap.org/?mlat=51.991960&mlon=-4.980285&zoom=17 (maybe overlapping shapes?) WARN: uk.me.parabola.mkgmap.filters.ShapeMergeFilter ../mapGB/74220030.osm.pbf: merging shapes skipped for shapes near http://www.openstreetmap.org/?mlat=51.991960&mlon=-4.980285&zoom=17 (maybe overlapping shapes?) WARN: uk.me.parabola.mkgmap.filters.ShapeMergeFilter ../mapGB/74220030.osm.pbf: merging shapes skipped for shapes near http://www.openstreetmap.org/?mlat=52.061748&mlon=-4.607507&zoom=17 (maybe overlapping shapes?) WARN: uk.me.parabola.mkgmap.filters.ShapeMergeFilter ../mapGB/74220030.osm.pbf: merging shapes skipped for shapes near http://www.openstreetmap.org/?mlat=52.061748&mlon=-4.607507&zoom=17 (maybe overlapping shapes?) WARN: uk.me.parabola.mkgmap.filters.ShapeMergeFilter ../mapGB/74220030.osm.pbf: ignoring duplicate shape with id 4611686018427515572 at http://www.openstreetmap.org/?mlat=52.036185&mlon=-4.470695&zoom=17 with type 0x13 for resolution 24 WARN: uk.me.parabola.mkgmap.filters.ShapeMergeFilter ../mapGB/74220030.osm.pbf: ignoring duplicate shape with id 0 at http://www.openstreetmap.org/?mlat=52.116036&mlon=-4.505916&zoom=17 with type 0x13 for resolution 24 WARN: uk.me.parabola.mkgmap.filters.ShapeMergeFilter ../mapGB/74220030.osm.pbf: ignoring duplicate shape with id 4611686018427511457 at http://www.openstreetmap.org/?mlat=52.074961&mlon=-4.263041&zoom=17 with type 0x13 for resolution 24 WARN: uk.me.parabola.mkgmap.filters.ShapeMergeFilter ../mapGB/74220030.osm.pbf: merging shapes skipped for shapes near http://www.openstreetmap.org/?mlat=52.074944&mlon=-4.263001&zoom=17 (maybe overlapping shapes?) WARN: uk.me.parabola.mkgmap.filters.ShapeMergeFilter ../mapGB/74220030.osm.pbf: ignoring duplicate shape with id 4611686018427511456 at http://www.openstreetmap.org/?mlat=52.074857&mlon=-4.262968&zoom=17 with type 0x13 for resolution 24 WARN: uk.me.parabola.mkgmap.filters.ShapeMergeFilter ../mapGB/74220030.osm.pbf: merging shapes skipped for shapes near http://www.openstreetmap.org/?mlat=52.074771&mlon=-4.263044&zoom=17 (maybe overlapping shapes?) WARN: uk.me.parabola.mkgmap.filters.ShapeMergeFilter ../mapGB/74220030.osm.pbf: merging shapes skipped for shapes near http://www.openstreetmap.org/?mlat=52.074771&mlon=-4.263044&zoom=17 (maybe overlapping shapes?) FINE: uk.me.parabola.mkgmap.main.Main ../mapGB/74220030.osm.pbf: adding output name /norbert/tstMisc/./74200001.img OVER: global Number of MapFailedExceptions: 0 INFO: uk.me.parabola.mkgmap.main.Main Combining maps INFO: uk.me.parabola.mkgmap.main.Main /norbert/tstMisc/./ovm_74200001.img INFO: uk.me.parabola.mkgmap.main.Main ../mapGB/74220030.osm.pbf -> /norbert/tstMisc/./74200001.img OVER: global Number of ExitExceptions: 0 OVER: global Time finished: Thu May 12 06:41:24 BST 2022 OVER: global Total time taken: 43 seconds
Index: src/uk/me/parabola/mkgmap/reader/osm/SeaGenerator.java =================================================================== --- src/uk/me/parabola/mkgmap/reader/osm/SeaGenerator.java (revision 4900) +++ src/uk/me/parabola/mkgmap/reader/osm/SeaGenerator.java (working copy) @@ -113,6 +113,7 @@ private static final Pattern KEY_SPLITTER = Pattern.compile(Pattern.quote("_")); private static final Pattern SEMICOLON_SPLITTER = Pattern.compile(Pattern.quote(";")); + private static final short NATURAL_TAG_KEY = TagDict.getInstance().xlate("natural"); /** * When order-by-decreasing-area we need all bit of sea to be output consistently. @@ -498,48 +499,22 @@ */ @Override public void onAddWay(Way way) { - String natural = way.getTag("natural"); + String natural = way.getTag(NATURAL_TAG_KEY); if (natural == null) return; - - // cope with compound tag value - StringBuilder others = null; - boolean foundCoastline = false; - for (String n : SEMICOLON_SPLITTER.split(natural)) { - if ("coastline".equals(n.trim())) - foundCoastline = true; - else if (others == null) - others = new StringBuilder(n); - else - others.append(';').append(n); - } - if (!foundCoastline) + int posn = natural.indexOf("coastline"); + if (posn < 0) return; + if (posn > 0 && natural.charAt(posn-1) != ';') + return; + if (natural.length() > posn+9 && natural.charAt(posn+9) != ';') + return; if (precompSea != null) splitCoastLineToLineAndShape(way, natural); else if (coastlineFilenames == null) { - /* RWB ??? - * - * I'd have thought it better to leave the original way, which has been saved, - * untouched. The copy doesn't need any tags at this point. Later it might - * be made into a polygon and tagged as land or sea. - * - * Could do a couple of quick check here to save effort later: - * 1/ if no part in tile then stop, don't change anything or save. - * 2/ if closed(), add to island list instead of shoreline. Any single closed - * way will be a small island, not a sea! Later, after shoreline - * has been merged/clipped etc, check these again for clipping and add clippings - * to shoreline and unclipped back into islands - */ - // create copy of way that has only the natural=coastline tag + // create copy of way that will become (part of) land/sea polygon Way shore = new Way(way.getOriginalId(), way.getPoints()); shore.markAsGeneratedFrom(way); - shore.addTag("natural", "coastline"); - saver.addWay(shore); - - way.deleteTag("natural"); - if (others != null) - way.addTag("natural", others.toString()); shoreline.add(way); } } @@ -553,8 +528,6 @@ * @param naturalVal the tag value */ private void splitCoastLineToLineAndShape(Way way, String naturalVal){ - if (precompSea == null) - return; if (way.hasIdenticalEndPoints()){ // add a copy of this way to be able to draw it as a shape Way shapeWay = new Way(way.getOriginalId(), way.getPoints()); @@ -561,7 +534,7 @@ shapeWay.markAsGeneratedFrom(way); shapeWay.copyTags(way); // change the tag so that only special rules looking for it are firing - shapeWay.deleteTag("natural"); + shapeWay.deleteTag(NATURAL_TAG_KEY); shapeWay.addTag("mkgmap:removed_natural",naturalVal); // tag that this way so that it is used as shape only shapeWay.addTag(MultiPolygonRelation.STYLE_FILTER_TAG, MultiPolygonRelation.STYLE_FILTER_POLYGON); @@ -695,7 +668,7 @@ boolean changeLandTag = landTag != null && ("natural".equals(landTag[0]) && !"land".equals(landTag[1])); for (Way w : landWays) { if (changeLandTag) { - w.deleteTag("natural"); + w.deleteTag(NATURAL_TAG_KEY); w.addTag(landTag[0], landTag[1]); } saver.addWay(w); @@ -842,7 +815,7 @@ if (entry != null) { is = pd.zipFile.getInputStream(entry); } else { - log.error("Preompiled sea tile " + tileName + " not found."); + log.error("Precompiled sea tile " + tileName + " not found."); } } else { File precompTile = new File(pd.dirFile, tileName); @@ -873,7 +846,7 @@ // interfere with the ids of this run w.markAsGeneratedFrom(w); - if ("land".equals(w.getTag("natural"))) { + if ("land".equals(w.getTag(NATURAL_TAG_KEY))) { landWays.add(w); } else { seaWays.add(w); @@ -937,7 +910,7 @@ List<List<Coord>> shapes = Java2DConverter.areaToShapes(area, commonCoordMap); for (List<Coord> points : shapes) { Way w = new Way(FakeIdGenerator.makeFakeId(), points); - w.addTag("natural", type); + w.addTag(NATURAL_TAG_KEY, type); ways.add(w); } } @@ -1045,10 +1018,13 @@ return; } - Relation seaRelation = null; - // handle islands (closed shoreline components) first (they're easy) handleIslands(); + if (maxCoastlineGap > 0) { + if (closeGaps()) { // there may be more islands now + handleIslands(); + } + } if (islands.isEmpty()) { // the tile doesn't contain any islands so we can assume @@ -1060,41 +1036,32 @@ // the remaining shoreline segments should intersect the boundary // find the intersection points and store them in a SortedMap - NavigableMap<Double, Way> hitMap = findIntesectionPoints(); + NavigableMap<Double, Way> hitMap = findIntersectionPoints(); verifyHits(hitMap); + NavigableMap<Double, Way> copyHitMap = new TreeMap<>(hitMap); + // generate background polygons (sea & land) that cover complete area and touch the edge + List<Way> seaAreas = createSeaPolygons(hitMap); + List<Way> landAreas = createLandPolygons(copyHitMap); - if (generateSeaBackground) { - // the background is sea so all anti-islands should be - // contained by land otherwise they won't be visible - if (generateSeaUsingMP) { - long multiId = FakeIdGenerator.makeFakeId(); - log.debug("Generate seabounds relation", multiId); - seaRelation = new GeneralRelation(multiId); - seaRelation.addTag("type", "multipolygon"); - seaRelation.addTag("natural", "sea"); - } + Relation seaRelation = null; + if (generateSeaUsingMP && generateSeaBackground) { // use multipolygon to cut out islands from sea + // generateSeaBackground is now a bit of a mismomer and really means "there are islands" + long multiId = FakeIdGenerator.makeFakeId(); + log.debug("Generate seabounds relation", multiId); + seaRelation = new GeneralRelation(multiId); + seaRelation.addTag("type", "multipolygon"); + seaRelation.addTag(NATURAL_TAG_KEY, "sea"); + } - createLandPolygons(hitMap); - processIslands(seaRelation); - checkIslands(true); + processSeaAreas(seaAreas, seaRelation); // seaAreas meet the edge of the tile + processLandAreas(landAreas, null); // landAreas meet the edge if the tile + processSeaAreas(antiIslands, seaRelation); // antiIslands are seas that don't touch the edge + processLandAreas(islands, seaRelation); // islands don't touch the edge - Way sea = createSeaWay(true); - - log.info("sea:", sea); - saver.addWay(sea); - if(seaRelation != null) - seaRelation.addElement("outer", sea); - } else { - // background is land - createSeaPolygons(hitMap); - checkIslands(false); - - // generate a land polygon so that the tile's - // background colour will match the land colour on the - // tiles that do contain some sea - Way land = createLandWay(); - saver.addWay(land); - log.info("land:", land); + if (checkCoastline) { + islands.addAll(landAreas); + antiIslands.addAll(seaAreas); + checkIslands(generateSeaBackground); } if (seaRelation != null) { @@ -1110,7 +1077,7 @@ } saver.addRelation(coastRel); } - + shoreline = null; islands = null; antiIslands = null; @@ -1118,25 +1085,44 @@ /** * These are bit of land that have been generated as polygons + * @param areas list of land polygons * @param seaRelation if set, add as inner */ - private void processIslands(Relation seaRelation) { - for (Way w : islands) { + private void processLandAreas(List<Way> areas, Relation seaRelation) { + for (Way w : areas) { + w.addTag(landTag[0], landTag[1]); + //log.info("adding land", land); if (seaRelation != null) { // create a "inner" way for each island seaRelation.addElement("inner", w); } + saver.addWay(w); } } /** + * These are bits of sea have been generated as polygons. + * @param areas list of sea polygons + * @param seaRelation if set, add as inner + */ + private void processSeaAreas(List<Way> areas, Relation seaRelation) { + for (Way w : areas) { + //log.info("adding sea", w); + w.setFullArea(SEA_SIZE); + if (seaRelation != null) { + seaRelation.addElement("outer", w); + } else { + w.addTag(NATURAL_TAG_KEY, "sea"); + saver.addWay(w); + } + } + } + + /** * Check whether land is enclosed in land or sea within sea. * @param seaBased true if the tile is also sea with land [multi-]polygons */ private void checkIslands(boolean seaBased) { - if (!checkCoastline) - return; - for (Way ai : antiIslands) { Way containingLand = null; Way containingSea = null; @@ -1216,7 +1202,7 @@ } Way sea = new Way(seaId, bbox.toCoords()); sea.reverse(); // make clockwise for consistency - sea.addTag("natural", "sea"); + sea.addTag(NATURAL_TAG_KEY, "sea"); sea.setFullArea(SEA_SIZE); return sea; } @@ -1255,31 +1241,19 @@ while (it.hasNext()) { Way w = it.next(); if (w.hasIdenticalEndPoints()) { - addClosedShore(w); + if (Way.clockwise(w.getPoints())) + antiIslands.add(w); + else + islands.add(w); it.remove(); } } - - closeGaps(); - // there may be more islands now - it = shoreline.iterator(); - while (it.hasNext()) { - Way w = it.next(); - if (w.hasIdenticalEndPoints()) { - log.debug("closed after concatenating", w); - addClosedShore(w); - it.remove(); - } - } } - private void closeGaps() { - if (maxCoastlineGap <= 0) - return; - + private boolean closeGaps() { // join up coastline segments whose end points are less than // maxCoastlineGap metres apart - boolean changed; + boolean someClosed = false, changed; do { changed = false; Iterator<Way> iter = shoreline.iterator(); @@ -1293,10 +1267,12 @@ if (closed != null) { saver.addWay(closed); changed = true; + someClosed = true; } } } } while (changed); + return someClosed; } private Way tryCloseGap(Way w1) { @@ -1334,7 +1310,7 @@ shoreline.remove(nearest); // make a line that shows the filled gap Way w = new Way(FakeIdGenerator.makeFakeId()); - w.addTag("natural", "mkgmap:coastline-gap"); + w.addTag(NATURAL_TAG_KEY, "mkgmap:coastline-gap"); w.addPoint(w1e); w.addPoint(w2s); return w; @@ -1342,35 +1318,14 @@ return null; } - private void addClosedShore(Way w) { - if (Way.clockwise(w.getPoints())) - addAsSea(w); - else - addAsLand(w); - } - - private void addAsSea(Way w) { - w.addTag("natural", "sea"); - log.info("adding anti-island", w); - antiIslands.add(w); - w.setFullArea(SEA_SIZE); - saver.addWay(w); - } - - private void addAsLand(Way w) { - w.addTag(landTag[0], landTag[1]); - log.info("adding island", w); - islands.add(w); - saver.addWay(w); - } - /** * Add lines to ways that touch or cross the sea bounds so that the way is closed along the edges of the bounds. * Adds complete edges or parts of them. This is done counter-clockwise. * @param hitMap A map of the 'hits' where the shore line intersects the boundary. */ - private void createLandPolygons(NavigableMap<Double, Way> hitMap) { + private List<Way> createLandPolygons(NavigableMap<Double, Way> hitMap) { NavigableSet<Double> hits = hitMap.navigableKeySet(); + List<Way> areas = new ArrayList<>(); while (!hits.isEmpty()) { Double hFirst = hits.first(); Double hStart = hFirst, hEnd; @@ -1398,8 +1353,9 @@ } while (!finished); w.addPoint(w.getFirstPoint()); // close shape log.info("adding landPoly, hits.size()", hits.size()); - addAsLand(w); + areas.add(w); } + return areas; } /** @@ -1408,8 +1364,9 @@ * This is much the same as createLandPolygons, but in reverse. * @param hitMap A map of the 'hits' where the shore line intersects the boundary. */ - private void createSeaPolygons(NavigableMap<Double, Way> hitMap) { + private List<Way> createSeaPolygons(NavigableMap<Double, Way> hitMap) { NavigableSet<Double> hits = hitMap.navigableKeySet(); + List<Way> areas = new ArrayList<>(); while (!hits.isEmpty()) { Double hFirst = hits.last(); Double hStart = hFirst, hEnd; @@ -1437,8 +1394,9 @@ } while (!finished); w.addPoint(w.getFirstPoint()); // close shape log.info("adding seaPoly, hits.size()", hits.size()); - addAsSea(w); + areas.add(w); } + return areas; } /** @@ -1473,7 +1431,7 @@ * map boundary. * @return A map of the 'hits' where the shore line intersects the boundary. */ - private NavigableMap<Double, Way> findIntesectionPoints() { + private NavigableMap<Double, Way> findIntersectionPoints() { NavigableMap<Double, Way> hitMap = new TreeMap<>(); for (Way w : shoreline) { Coord pStart = w.getFirstPoint(); @@ -1504,11 +1462,9 @@ * 4. Extend the ends of the shoreline to the nearest edge of the tile with ...,extend-sea-sectors * This, in conjunction with close-gaps, normally works well but it isn't foolproof. */ - List<Coord> points = w.getPoints(); if (allowSeaSectors) { - Way seaOrLand = new Way(w.getOriginalId()); + Way seaOrLand = new Way(w.getOriginalId(), w.getPoints()); seaOrLand.markAsGeneratedFrom(w); - seaOrLand.getPoints().addAll(points); int startLat = pStart.getHighPrecLat(); int startLon = pStart.getHighPrecLon(); int endLat = pEnd.getHighPrecLat(); @@ -1515,11 +1471,11 @@ int endLon = pEnd.getHighPrecLon(); boolean startLatIsCorner = (startLat > endLat) == (startLon > endLon); int cornerLat, cornerLon; - if (generateSeaBackground) { // the tile is sea, with islands + if (generateSeaBackground) { // already have islands, so do this likewise startLatIsCorner = !startLatIsCorner; - addAsLand(seaOrLand); - } else { // the tile is land, maybe with sea polygons on edge - addAsSea(seaOrLand); + islands.add(seaOrLand); + } else { // no islands, so more chance of sea being seen + antiIslands.add(seaOrLand); } if (startLatIsCorner) { cornerLat = startLat; @@ -1530,7 +1486,7 @@ } seaOrLand.addPoint(Coord.makeHighPrecCoord(cornerLat, cornerLon)); seaOrLand.addPoint(pStart); - log.info("seaSector:", generateSeaBackground, startLatIsCorner, Way.clockwise(seaOrLand.getPoints()), seaOrLand.getBasicLogInformation()); + log.info("seaSector:", islands.size(), antiIslands.size(), startLatIsCorner, Way.clockwise(seaOrLand.getPoints()), seaOrLand.getBasicLogInformation()); } else if (extendSeaSectors) { // join to nearest tile border if (null == hStart) { @@ -1545,11 +1501,9 @@ hitMap.put(hStart, w); hitMap.put(hEnd, null); // put this for verifyHits which then deletes it } else { - // show the coastline even though we can't produce - // a polygon for the land - w.addTag("natural", "coastline"); - log.error("adding sea shape that is not really closed"); - saver.addWay(w); + // Can't produce a polygon for the land/sea. + // The original coastline might show, depending on the style + log.error("Unresolved section of coastline", w.getBasicLogInformation()); } } } @@ -1563,7 +1517,7 @@ * After checking, the end hit is removed */ private void verifyHits(NavigableMap<Double, Way> hitMap) { - log.debug("Islands", islands.size(), "Seas", antiIslands.size(), "hits", hitMap.size()); + log.debug("Shorelines", shoreline.size(), "Islands", islands.size(), "Seas", antiIslands.size(), "hits", hitMap.size()); NavigableSet<Double> hits = hitMap.navigableKeySet(); Iterator<Double> iter = hits.iterator(); int lastStatus = 0, thisStatus; @@ -1624,8 +1578,10 @@ /** * Calculate a Double that represents the position where the given point touches * the boundary. - * - * @param bounds the boundary + * Assumes that, if the way crosses the boundary, it has been cut so the end point + * is exactly on the boundary. + * + * @param a the boundary * @param p the point * @return null if the point is not touching the boundary, else a value * between 0.0 (inclusive) and 4.0 (exclusive), where 0 means the lower @@ -1632,15 +1588,8 @@ * left corner, 0.5 means the middle of the bottom edge, 1.5 the * middle of the right edge, 4 would be the lower left corner again */ - private static Double getEdgeHit(Area bounds, Coord p) { - Double hit = getEdgeHit(bounds, p, 10); // 10 points in garmin units - if (hit != null && hit >= 4) - hit = 0.0; - return hit; - } - - private static Double getEdgeHit(Area a, Coord p, int tolerance24) { - final int toleranceHp = tolerance24 << Coord.DELTA_SHIFT; + private static Double getEdgeHit(Area a, Coord p) { + final int toleranceHp = 0; // tolerance24 << Coord.DELTA_SHIFT; final int latHp = p.getHighPrecLat(); final int lonHp = p.getHighPrecLon(); final int minLatHp = a.getMinLat() << Coord.DELTA_SHIFT; @@ -1657,6 +1606,7 @@ return 2 + ((double) (maxLongHp - lonHp) / (maxLongHp - minLongHp)); } else if (lonHp <= minLongHp + toleranceHp) { return 3 + ((double) (maxLatHp - latHp) / (maxLatHp - minLatHp)); + // if exactly on bottom LHS corner (4), will have been caught by first case (0) } return null; } @@ -1665,12 +1615,12 @@ * Find the nearest edge for supplied Coord p. */ private static Double getNextEdgeHit(Area a, Coord p) { - int latHp = p.getHighPrecLat(); - int lonHp = p.getHighPrecLon(); - int minLatHp = a.getMinLat() << Coord.DELTA_SHIFT; - int maxLatHp = a.getMaxLat() << Coord.DELTA_SHIFT; - int minLongHp = a.getMinLong() << Coord.DELTA_SHIFT; - int maxLongHp = a.getMaxLong() << Coord.DELTA_SHIFT; + final int latHp = p.getHighPrecLat(); + final int lonHp = p.getHighPrecLon(); + final int minLatHp = a.getMinLat() << Coord.DELTA_SHIFT; + final int maxLatHp = a.getMaxLat() << Coord.DELTA_SHIFT; + final int minLongHp = a.getMinLong() << Coord.DELTA_SHIFT; + final int maxLongHp = a.getMaxLong() << Coord.DELTA_SHIFT; log.info(String.format("getNextEdgeHit: (%d %d) (%d %d %d %d)", latHp, lonHp, minLatHp, minLongHp, maxLatHp, maxLongHp)); // shortest distance to border (init with distance to southern border)
_______________________________________________ mkgmap-dev mailing list mkgmap-dev@lists.mkgmap.org.uk https://www.mkgmap.org.uk/mailman/listinfo/mkgmap-dev