Index: resources/help/en/options
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- resources/help/en/options	(revision 2346)
+++ resources/help/en/options	(revision )
@@ -383,9 +383,6 @@
 	that join together but go nowhere, 2 = individual oneway roads
 	that go nowhere.
 
---report-undefined-nodes
-	Issue a warning when a way references an undefined node.
-
 --road-name-pois[=GarminCode]
 	Generate a POI for each named road. By default, the POIs'
 	Garmin type code is 0x640a. If desired, a different type code
Index: src/uk/me/parabola/mkgmap/reader/osm/bin/OsmBinHandler.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- src/uk/me/parabola/mkgmap/reader/osm/bin/OsmBinHandler.java	(revision 2346)
+++ src/uk/me/parabola/mkgmap/reader/osm/bin/OsmBinHandler.java	(revision )
@@ -16,7 +16,6 @@
 
 import uk.me.parabola.imgfmt.MapFailedException;
 import uk.me.parabola.imgfmt.app.Coord;
-import uk.me.parabola.log.Logger;
 import uk.me.parabola.mkgmap.reader.osm.Element;
 import uk.me.parabola.mkgmap.reader.osm.GeneralRelation;
 import uk.me.parabola.mkgmap.reader.osm.Node;
@@ -34,7 +33,6 @@
  * @author Steve Ratcliffe
  */
 public class OsmBinHandler extends OsmHandler {
-	private static final Logger log = Logger.getLogger(OsmBinHandler.class);
 
 	public OsmBinHandler(EnhancedProperties props) {
 	}
@@ -129,7 +127,7 @@
 
 		protected void parseWays(List<Osmformat.Way> ways) {
 			for (Osmformat.Way binWay : ways) {
-				Way way = new Way(binWay.getId());
+				Way way = startWay(binWay.getId());
 
 				for (int j = 0; j < binWay.getKeysCount(); j++) {
 
@@ -143,21 +141,10 @@
 				long nid = 0;
 				for (long idDelta : binWay.getRefsList()) {
 					nid += idDelta;
-					Coord co = saver.getCoord(nid);
-					if (co != null) {
-						hooks.onCoordAddedToWay(way, nid, co);
-						co = saver.getCoord(nid);
-						way.addPoint(co);
-
-						// nodes (way joins) will have highwayCount > 1
-						co.incHighwayCount();
-					} else {
-						log.info("Way", way.toBrowseURL(), "references undefined node", nid);
+					addCoordToWay(way, nid);
-					}
+				}
-				}
 
-				saver.addWay(way);
-				hooks.onAddWay(way);
+				endWay(way);
 			}
 		}
 
Index: src/uk/me/parabola/mkgmap/reader/osm/OsmHandler.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- src/uk/me/parabola/mkgmap/reader/osm/OsmHandler.java	(revision 2346)
+++ src/uk/me/parabola/mkgmap/reader/osm/OsmHandler.java	(revision )
@@ -17,6 +17,7 @@
 import java.util.Set;
 
 import uk.me.parabola.imgfmt.app.Area;
+import uk.me.parabola.imgfmt.app.Coord;
 
 /**
  * Base class for OSM file handlers.
@@ -32,6 +33,11 @@
 	private Map<String,Set<String>> deletedTags;
 	private Map<String, String> usedTags;
 
+	// Node references within a way
+	protected long firstNodeRef;
+	protected long lastNodeRef;
+	protected boolean missingNodeRef;
+
 	/**
 	 * Set a set of tags with values that are to be deleted on input.
 	 * For each key there is a set of values.  If the value set is empty then
@@ -129,5 +135,54 @@
 
 	public void setHooks(OsmReadingHooks plugin) {
 		this.hooks = plugin;
+	}
+
+	/**
+	 * Common actions to take when creating a new way.
+	 * Reset some state and create the Way object.
+	 * @param id The osm id of the new way.
+	 * @return The new Way itself.
+	 */
+	protected Way startWay(long id) {
+		firstNodeRef = 0;
+		lastNodeRef = 0;
+		missingNodeRef = false;
+		return new Way(id);
+	}
+
+	/**
+	 * Common actions to take when a way has been completely read by the parser.
+	 * It is saved
+	 * @param way The way that was read.
+	 */
+	protected void endWay(Way way) {
+		way.setClosed(firstNodeRef == lastNodeRef);
+		way.setComplete(!missingNodeRef);
+
+		saver.addWay(way);
+		hooks.onAddWay(way);
+	}
+
+	/**
+	 * Add a coordinate point to the way.
+	 * @param way The Way.
+	 * @param id The coordinate id.
+	 */
+	protected void addCoordToWay(Way way, long id) {
+		lastNodeRef = id;
+		if (firstNodeRef == 0) firstNodeRef = id;
+
+		Coord co = saver.getCoord(id);
+
+		if (co != null) {
+			hooks.onCoordAddedToWay(way, id, co);
+			co = saver.getCoord(id);
+			way.addPoint(co);
+
+			// nodes (way joins) will have highwayCount > 1
+			co.incHighwayCount();
+		} else {
+			missingNodeRef = true;
+		}
 	}
 }
Index: src/uk/me/parabola/mkgmap/reader/osm/Way.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- src/uk/me/parabola/mkgmap/reader/osm/Way.java	(revision 2346)
+++ src/uk/me/parabola/mkgmap/reader/osm/Way.java	(revision )
@@ -34,6 +34,16 @@
 
 	private final List<Coord> points;
 
+	// This will be set if a way is read from an OSM file and the first node is the same node as the last
+	// one in the way. This can be set to true even if there are missing nodes and so the nodes that we
+	// have do not form a closed loop.
+	// Note: this is not always set, you must use isClosed()
+	private boolean closed;
+
+	// This is set to false if, we know that there are nodes missing from this way.
+	// If you set this to false, then you *must* also set closed to the correct value.
+	private boolean complete  = true;
+
 	public Way(long id) {
 		points = new ArrayList<Coord>(5);
 		setId(id);
@@ -48,6 +58,8 @@
 		Way dup = new Way(getId(), new ArrayList<Coord>(points));
 		dup.setName(getName());
 		dup.copyTags(this);
+		dup.closed = this.closed;
+		dup.complete = this.complete;
 		return dup;
 	}
 
@@ -96,8 +108,37 @@
 		Collections.reverse(points);
 	}
 
+	/**
+	 * Returns true if the way is really closed in OSM.
+	 *
+	 * Will return true even if the way is incomplete in the tile that we are reading, but the way is
+	 * really closed in OSM.
+	 *
+	 * @return True if the way is really closed.
+	 */
 	public boolean isClosed() {
+		if (!isComplete())
+			return closed;
+
 		return !points.isEmpty() && points.get(0).equals(points.get(points.size()-1));
+	}
+
+	public void setClosed(boolean closed) {
+		this.closed = closed;
+	}
+
+	public boolean isComplete() {
+		return complete;
+	}
+
+	/**
+	 * Set this to false if you know that the way does not have its complete set of nodes.
+	 *
+	 * If you do set this to false, then you must also call {@link #setClosed} to indicate if the way
+	 * is really closed or not.
+	 */
+	public void setComplete(boolean complete) {
+		this.complete = complete;
 	}
 
 	/**
Index: src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java	(revision 2346)
+++ src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java	(revision )
@@ -528,24 +528,15 @@
 	}
 
 	private void addShape(Way way, GType gt) {
-		if (way.isClosed()==false) {
-			// check if start or end point lie within the bbox
-			if (bbox.insideBoundary(way.getPoints().get(0)) || 
-				bbox.insideBoundary(way.getPoints().get(way.getPoints().size()-1))) {
-				log.warn("Unclosed way",way.toBrowseURL(),way.toTagString(),
-						"should be converted as shape but the start or end point lies inside the bbox. Skip it.");
+		// This is deceptively simple. At the time of writing, splitter only retains points that are within
+		// the tile and some distance around it.  Therefore a way that is closed in reality may not be closed
+		// as we see it in its incomplete state.
+		//
+		// Here isClosed means that it is really closed in OSM, and therefore it is safe to clip the line
+		// segment to the tile boundaries.
+		if (!way.isClosed())
-				return;
+			return;
-			}
-
-			log.info("Add unclosed way as shape. Close it automatically.",way.toBrowseURL());
-			// it is handled as shape so the way should be closed
-			// reasons for this are:
-			// * OSM data failures
-			// * the way cuts the tile bounds and the splitter did not close it
-			// * the way should be a line but the style has a polygon rule only
-			way.addPoint(way.getPoints().get(0));
-		}
-		
+
 		final MapShape shape = new MapShape();
 		elementSetup(shape, gt, way);
 		shape.setPoints(way.getPoints());
Index: src/uk/me/parabola/mkgmap/reader/osm/xml/Osm5XmlHandler.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- src/uk/me/parabola/mkgmap/reader/osm/xml/Osm5XmlHandler.java	(revision 2346)
+++ src/uk/me/parabola/mkgmap/reader/osm/xml/Osm5XmlHandler.java	(revision )
@@ -55,19 +55,16 @@
 	private static final int MODE_RELATION = 4;
 	private static final int MODE_BOUNDS = 5;
 
-	// Current state.
-	private Node currentNode;
-	private Way currentWay;
-	private Relation currentRelation;
-	private long currentElementId;
-
 	// Options
-	private final boolean reportUndefinedNodes;
 	private final boolean ignoreBounds;
+	// Current state.
+	protected Node currentNode;
+	protected Way currentWay;
+	protected Relation currentRelation;
+	protected long currentElementId;
 
 	public Osm5XmlHandler(EnhancedProperties props) {
 		ignoreBounds = props.getProperty("ignore-osm-bounds", false);
-		reportUndefinedNodes = props.getProperty("report-undefined-nodes", false);
 	}
 
 	/**
@@ -163,8 +160,8 @@
 			} else if (mode == MODE_WAY) {
 				if (qName.equals("way")) {
 					mode = 0;
-					saver.addWay(currentWay);
-					hooks.onAddWay(currentWay);
+
+					endWay(currentWay);
 					currentWay = null;
 				}
 
@@ -238,7 +235,7 @@
 	private void startInWay(String qName, Attributes attributes) {
 		if (qName.equals("nd")) {
 			long id = idVal(attributes.getValue("ref"));
-			addCoordToWay(id);
+			addCoordToWay(currentWay, id);
 		} else if (qName.equals("tag")) {
 			String key = attributes.getValue("k");
 			String val = attributes.getValue("v");
@@ -353,28 +350,9 @@
 	private void startWay(String sid) {
 		try {
 			long id = idVal(sid);
-			currentWay = new Way(id);
+			currentWay = startWay(id);
 		} catch (NumberFormatException e) {
 			// ignore bad numeric data. The way will be discarded
-		}
-	}
-
-	/**
-	 * Add a coordinate point to the way.
-	 * @param id The coordinate id.
-	 */
-	private void addCoordToWay(long id) {
-		Coord co = saver.getCoord(id);
-
-		if (co != null) {
-			hooks.onCoordAddedToWay(currentWay, id, co);
-			co = saver.getCoord(id);
-			currentWay.addPoint(co);
-
-			// nodes (way joins) will have highwayCount > 1
-			co.incHighwayCount();
-		} else if(reportUndefinedNodes && currentWay != null) {
-			log.warn("Way", currentWay.toBrowseURL(), "references undefined node", id);
 		}
 	}
 }
