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

Reply via email to