Index: doc/options.txt
===================================================================
--- doc/options.txt	(revision 4590)
+++ doc/options.txt	(working copy)
@@ -771,6 +771,9 @@
 :: switches on the debugging of the flood blocker
 generates GPX files for each polygon checked by
 the flood blocker
+:;fix-land-sea
+:: swap between land and sea where sea is within sea or
+land within land
 
 ;--make-poi-index
 :	Generate a POI index in each map tile. Probably not used by modern devices,
Index: src/uk/me/parabola/mkgmap/reader/osm/SeaGenerator.java
===================================================================
--- src/uk/me/parabola/mkgmap/reader/osm/SeaGenerator.java	(revision 4590)
+++ src/uk/me/parabola/mkgmap/reader/osm/SeaGenerator.java	(working copy)
@@ -63,13 +63,14 @@
 	private boolean generateSeaUsingMP = true;
 	private int maxCoastlineGap;
 	private boolean allowSeaSectors = true;
-	private boolean extendSeaSectors;
+	private boolean extendSeaSectors = false;
 	private String[] landTag = { "natural", "land" };
-	private boolean floodblocker;
+	private boolean floodblocker = false;
 	private int fbGap = 40;
 	private double fbRatio = 0.5d;
 	private int fbThreshold = 20;
-	private boolean fbDebug;
+	private boolean fbDebug = false;
+	private boolean fixLandSea = false;
 
 	private ElementSaver saver;
 
@@ -187,6 +188,8 @@
 					fbThreshold = (int) Double.parseDouble(option.substring("fbthres=".length()));
 				} else if ("fbdebug".equals(option)) {
 					fbDebug = true;
+				} else if ("fix-land-sea".equals(option)) {
+					fixLandSea = true;
 				} else {
 					printOptionHelpMsg(forPrecompSea, option);
 				}
@@ -286,6 +289,7 @@
 		System.err.println("  land-tag=TAG=VAL    tag to use for land polygons (default natural=land)");
 		if (forPrecompSea)
 			return;
+
 		System.err.println("  no-sea-sectors      disable use of \"sea sectors\"");
 		System.err.println("  extend-sea-sectors  extend coastline to reach border");
 		System.err.println("  close-gaps=NUM      close gaps in coastline that are less than this distance (metres)");
@@ -293,6 +297,7 @@
 		System.err.println("  fbgap=NUM           points closer to the coastline are ignored for flood blocking (default 40)");
 		System.err.println("  fbthres=NUM         min points contained in a polygon to be flood blocked (default 20)");
 		System.err.println("  fbratio=NUM         min ratio (points/area size) for flood blocking (default 0.5)");
+		System.err.println("  fix-land-sea        swap between land and sea where sea is within sea or land within land");
 	}
 	
     /**
@@ -602,9 +607,9 @@
 		}
 		
 		// check if the land tags need to be changed
-		boolean changeLadTag = landTag != null && ("natural".equals(landTag[0]) && !"land".equals(landTag[1]));
+		boolean changeLandTag = landTag != null && ("natural".equals(landTag[0]) && !"land".equals(landTag[1]));
 		for (Way w : landWays) {
-			if (changeLadTag) {
+			if (changeLandTag) {
 				w.deleteTag("natural");
 				w.addTag(landTag[0], landTag[1]);
 			}
@@ -785,7 +790,7 @@
 
 	// merge the ways and maintain maps and list
 	private static void merge(Map<Coord, Way> beginMap, List<Way> joined, Way w1, Way w2) {
-		log.info("merging: ", beginMap.size(), w1.getId(), w2.getId());
+		log.info("merging:", beginMap.size(), w1, "with", w2);
 		Way wm;
 		if (FakeIdGenerator.isFakeId(w1.getId())) {
 			wm = w1;
@@ -831,7 +836,7 @@
 		}
 		
 		// clip all shoreline segments
-		clipShorlineSegments();
+		clipShorelineSegments();
 
 		if(shoreline.isEmpty()) {
 			// No sea required
@@ -862,7 +867,7 @@
 		// the remaining shoreline segments should intersect the boundary
 		// find the intersection points and store them in a SortedMap
 		NavigableMap<Double, Way> hitMap = findIntesectionPoints();
-		verifyHits(hitMap);
+		verifyHits(hitMap, fixLandSea, true);
 
 		if (generateSeaBackground) {
 			// the background is sea so all anti-islands should be
@@ -877,11 +882,11 @@
 
 			createLandPolygons(hitMap);
 			processIslands(seaRelation);
-			processAntiIslands(true);
+			checkIslands(true);
 
 			Way sea = createSeaWay(true);
 
-			log.info("sea: ", sea);
+			log.info("sea:", sea);
 			saver.addWay(sea);
 			if(seaRelation != null)
 				seaRelation.addElement("outer", sea);
@@ -888,7 +893,7 @@
 		} else {
 			// background is land
 			createSeaPolygons(hitMap);
-			processAntiIslands(false);
+			checkIslands(false);
 			
 			// generate a land polygon so that the tile's
 			// background colour will match the land colour on the
@@ -931,27 +936,63 @@
 	}
 
 	/**
-	 * These are bits of sea have been generated as polygons.
-	 * if the tile is also sea based, then check that surrounded by an island
-	 * @param seaRelation if set, add as inner
+	 * Check whether land is enclosed in land or sea within sea.
+	 * Sea polygons are anti-clockwise, land clockwise.
+	 * If --fix-land-sea is specified, checkIslands will attempt to correct errors
+	 * in the data by reversing the direction of rotation.
 	 * @param seaBased true if the tile is also sea with land [multi-]polygons
 	 */
-	private void processAntiIslands(boolean seaBased) {
+	private void checkIslands(boolean seaBased) {
 		for (Way ai : antiIslands) {
-			if (seaBased) {
-				boolean containedByLand = false;
-				for (Way i : islands) {
-					if (i.containsPointsOf(ai)) {
-						containedByLand = true;
-						break;
-					}
+			boolean containedByLand = false;
+			boolean containedBySea= false;
+			for (Way i : islands) {
+				if (i.containsPointsOf(ai)) {
+					containedByLand = true;
+					break;
 				}
-				if (!containedByLand) {
-					// found an anti-island that is not contained by land
-					log.warn("inner sea", ai , "is surrounded by water");
+			}
+			for (Way ai2 : antiIslands) {
+				if (ai2.containsPointsOf(ai)) {
+					containedBySea = true;
+					break;
 				}
 			}
+			if (!containedByLand & (seaBased | containedBySea)) {
+				if (fixLandSea) {
+					log.warn("inner sea", ai , "is surrounded by water - converted to land");
+					ai.deleteTag("natural");
+					ai.addTag(landTag[0], landTag[1]);
+				}
+				else
+					log.error("inner sea", ai , "is surrounded by water");
+			}
 		}
+		for (Way i : islands) {
+			boolean containedByLand = false;
+			boolean containedBySea= false;
+			for (Way ai : antiIslands) {
+				if (ai.containsPointsOf(i)) {
+					containedBySea = true;
+					break;
+				}
+			}
+			for (Way i2 : islands) {
+				if (i2 != i && i2.containsPointsOf(i)) {
+					containedByLand = true;
+					break;
+				}
+			}
+			if (containedByLand && !containedBySea) {
+				if (fixLandSea) {
+					log.warn("island", i , "is surrounded by land - converted to sea");
+					i.deleteTag(landTag[0]);
+					i.addTag("natural", "sea");
+				}
+				else
+					log.error("island", i , "is surrounded by land");
+			}
+		}
 	}
 
 	private Way createLandWay() {
@@ -990,7 +1031,7 @@
 	 * @param shoreline All the the ways making up the coast.
 	 * @param bounds The map bounds.
 	 */
-	private void clipShorlineSegments() {
+	private void clipShorelineSegments() {
 		List<Way> toBeRemoved = new ArrayList<>();
 		List<Way> toBeAdded = new ArrayList<>();
 		for (Way segment : shoreline) {
@@ -1112,7 +1153,7 @@
 		if (Way.clockwise(w.getPoints()))
 			addAsSea(w);
 		else
-		    	addAsLand(w);
+			addAsLand(w);
 	}
 
 	private void addAsSea(Way w) {
@@ -1141,6 +1182,7 @@
 			Way w = new Way(FakeIdGenerator.makeFakeId());
 			Double hFirst = hits.first();
 			Double hStart = hFirst, hEnd;
+			w.markAsGeneratedFrom(hitMap.get(hFirst));
 			boolean finished = false;
 			do {
 				Way segment = hitMap.get(hStart);
@@ -1179,6 +1221,7 @@
 			Way w = new Way(FakeIdGenerator.makeFakeId());
 			Double hFirst = hits.last();
 			Double hStart = hFirst, hEnd;
+			w.markAsGeneratedFrom(hitMap.get(hFirst));
 			boolean finished = false;
 			do {
 				Way segment = hitMap.get(hStart);
@@ -1247,7 +1290,7 @@
 			Double hEnd = getEdgeHit(tileBounds, pEnd);
 			if (hStart != null && hEnd != null) {
 				// nice case: both ends touch the boundary 
-				log.debug("hits: ", hStart, hEnd);
+				log.debug("hits:", hStart, hEnd);
 				hitMap.put(hStart, w);
 				hitMap.put(hEnd, null); // put this for verifyHits which then deletes it
 			} else {
@@ -1270,7 +1313,8 @@
 				 */
 				List<Coord> points = w.getPoints();
 				if (allowSeaSectors) {
-					Way seaOrLand = new Way(FakeIdGenerator.makeFakeId());
+ 					Way seaOrLand = new Way(FakeIdGenerator.makeFakeId());
+					seaOrLand.markAsGeneratedFrom(w);
 					seaOrLand.getPoints().addAll(points);
 					int startLat = pStart.getHighPrecLat();
 					int startLon = pStart.getHighPrecLon();
@@ -1293,7 +1337,7 @@
 					}
 					seaOrLand.addPoint(Coord.makeHighPrecCoord(cornerLat, cornerLon));
 					seaOrLand.addPoint(pStart);
-					log.info("seaSector: ", generateSeaBackground, startLatIsCorner, Way.clockwise(seaOrLand.getPoints()), seaOrLand);
+					log.info("seaSector:", generateSeaBackground, startLatIsCorner, Way.clockwise(seaOrLand.getPoints()), seaOrLand);
 				} else if (extendSeaSectors) {
 					// join to nearest tile border
 					if (null == hStart) {
@@ -1304,7 +1348,7 @@
 						hEnd = getNextEdgeHit(tileBounds, pEnd);
 						w.getPoints().add(getPoint(tileBounds, hEnd));
 					}
-					log.debug("hits (second try): ", hStart, hEnd);
+					log.debug("hits (second try):", hStart, hEnd);
 					hitMap.put(hStart, w);
 					hitMap.put(hEnd, null); // put this for verifyHits which then deletes it
 				} else {
@@ -1325,11 +1369,16 @@
 	 * due to extendSeaSectors when there is another bit of coastline in the gap, that this doesn't detect.
 	 * After checking, the end hit is removed
 	 */
-	private void verifyHits(NavigableMap<Double, Way> hitMap) {
+	private int verifyHits(NavigableMap<Double, Way> hitMap, boolean fix, boolean report) {
 		log.debug("Islands", islands.size(), "Seas", antiIslands.size(), "hits", hitMap.size());
 		NavigableSet<Double> hits = hitMap.navigableKeySet();
 		Iterator<Double> iter = hits.iterator();
+		List<Way> reversedWays = new ArrayList<>();
+		List<Way> adjacentWays = new ArrayList<>();
 		int lastStatus = 0, thisStatus;
+		Double lastHit = 0.0;
+		int errorCount = 0;
+		Way lastSegment = null;
 		while (iter.hasNext()) {
 			Double aHit = iter.next();
 			Way segment = hitMap.get(aHit);
@@ -1340,15 +1389,57 @@
 			} else {
 				thisStatus = +1;
 			}
-			if (thisStatus == lastStatus)
-				log.error("Adjacent coastlines hit tile edge in same direction", aHit, segment);
+			if (thisStatus == lastStatus) {
+				errorCount++;
+				if (report)
+					log.error("Adjacent coastlines hit tile edge in same direction at", getPoint(tileBounds, lastHit).toDegreeString(), "and", getPoint(tileBounds, aHit).toDegreeString(), segment);
+				if (segment != null && fix) {
+					log.info("attempting to fix coastline by reversing", segment);
+					segment.reverse();
+					reversedWays.add(segment);
+					if (lastSegment != null)
+						adjacentWays.add(lastSegment);
+				}
+			}
+			if (segment != null)
+				lastSegment = segment;
 			lastStatus = thisStatus;
+			lastHit = aHit;
 		}
+		if (errorCount > 0 && fix) {
+			NavigableMap<Double, Way> hitMap2 = findIntesectionPoints();
+			int errorCountAfterFix = verifyHits(hitMap2, false, false);
+			if (errorCountAfterFix > 0) {
+				log.info("fix didn't work");
+				for (Way reversedWay : reversedWays) {
+					log.info("trying reversing", reversedWay);
+					reversedWay.reverse();
+				}
+				if (adjacentWays.size() > 0) {
+					for (Way adjacentWay : adjacentWays)
+						adjacentWay.reverse();
+					hitMap2 = findIntesectionPoints();
+					errorCountAfterFix = verifyHits(hitMap2, false, false);
+					if (errorCountAfterFix > 0) {
+						for (Way adjacentWay : adjacentWays)
+							adjacentWay.reverse();
+						log.warn("coastline not fixed");
+					}
+					else
+						errorCount = 0;
+				}
+			}
+			else
+				errorCount = 0;
+			if (errorCount == 0)
+				log.info("coastline fixed");
+		}
+		return errorCount;
 	}
 
 	// create the point where the shoreline hits the sea bounds
 	private static Coord getPoint(Area a, double edgePos) {
-		log.info("getPoint: ", a, edgePos);
+		log.info("getPoint:", a, edgePos);
 		int aMinLongHP = a.getMinLong() << Coord.DELTA_SHIFT;
 		int aMaxLongHP = a.getMaxLong() << Coord.DELTA_SHIFT;
 		int aMinLatHP = a.getMinLat() << Coord.DELTA_SHIFT;
Index: src/uk/me/parabola/mkgmap/reader/osm/Way.java
===================================================================
--- src/uk/me/parabola/mkgmap/reader/osm/Way.java	(revision 4590)
+++ src/uk/me/parabola/mkgmap/reader/osm/Way.java	(working copy)
@@ -174,17 +174,30 @@
 	 */
 	public String toString() {
 		if (points.isEmpty())
-			return "Way: empty";
+			return "empty way";
 
-		Coord coord = points.get(0);
-		StringBuilder sb = new StringBuilder();
-		sb.append("WAY: ").append(getId()).append(" ");
-		sb.append(getName());
-		sb.append('(');
-		sb.append(Utils.toDegrees(coord.getLatitude()));
-		sb.append('/');
-		sb.append(Utils.toDegrees(coord.getLongitude()));
-		sb.append(')');
+		StringBuilder sb = new StringBuilder("way ");
+		if (FakeIdGenerator.isFakeId(getId()) && !FakeIdGenerator.isFakeId(getOriginalId())) {
+			sb.append("generated from ");
+			sb.append(getOriginalId());
+		} else {
+			sb.append(getId());
+		}
+		if (getName() != null) {
+			sb.append(' ');
+			sb.append(getName());
+		}
+		Coord coord = getFirstPoint();
+		if (hasEqualEndPoints()) {
+			sb.append(" starting and ending at ");
+			sb.append(coord.toDegreeString());
+		}
+		else {
+			sb.append(" starting at ");
+			sb.append(coord.toDegreeString());
+			sb.append(" and ending at ");
+			sb.append(getLastPoint().toDegreeString());
+		}
 		sb.append(' ');
 		sb.append(toTagString());
 		return sb.toString();
