Index: resources/help/en/options
===================================================================
--- resources/help/en/options	(revision 1001)
+++ resources/help/en/options	(working copy)
@@ -78,6 +78,14 @@
 	is the equivalent of: "0=24, 1=22, 2=21, 3=19, 4=18, 5=16"
 	although this may change.
 
+--reduce-point-density=num
+	Simplifies the ways with the douglas peucker algorithm.
+	num is the maximal allowed error distance, by which the resulting
+	way may differ from the original one.
+	This distance gets shifted with lower zoom levels. 
+	Recomended setting is 10, this should lead to only small differences
+	(Default is 2.6, which should lead to invisible changes) 
+
 --name-tag-list
 	Get the tag that will be used to supply the name.  Useful for
 	language variations.  You can supply a list and the first one
@@ -100,6 +108,27 @@
 --area-name
 --overview-mapname
 
+Optimization options:
+
+--reduce-point-density=num
+	Simplifies the ways with the douglas peucker algorithm.
+	num is the maximal allowed error distance, by which the resulting
+	way may differ from the original one.
+	This distance gets shifted with lower zoom levels. 
+	Recommended setting is 10, this should lead to only small differences
+	(Default is 2.6, which should lead to invisible changes) 
+
+-- suppress-dead-end-nodes
+	Do not generate nodes at the end of an dead end way. 
+	Decrease file size and should speed up routing slightly.
+	(Cause errors with the drawing of the pink line at the moment, 
+	don't use it!)
+
+--merge-lines
+	Try to merge lines. This helps the simplify filter to straighten out
+    longer chunks at lower zoom levels. Decreases file size more.
+	Increases paint speed at low zoom levels
+
 Misc options:
 --block-size=''number''
 	Changes the block size that is used in the generated map.
Index: resources/styles/default/lines
===================================================================
--- resources/styles/default/lines	(revision 1001)
+++ resources/styles/default/lines	(working copy)
@@ -26,7 +26,7 @@
 highway=motorway_link {add oneway = yes; add bicycle = no; add foot = no } [0x09 road_class=4 road_speed=3 resolution 16]
 highway=pedestrian & area!=yes {add access = no; add foot = yes} [0x06 road_class=0 road_speed=0 resolution 22]
 highway=primary [0x02 road_class=3 road_speed=4 resolution 19]
-highway=primary_link [0x08 road_class=3 road_speed=3 resolution 19]
+highway=primary_link [0x03 road_class=3 road_speed=3 resolution 20]
 highway=residential | highway=living_street [0x06 road_class=0 road_speed=2 resolution 21]
 highway=secondary [0x04 road_class=2 road_speed=3 resolution 20]
 highway=path {add access = no; add bicycle = yes; add foot = yes} [0x16 road_class=0 road_speed=0 resolution 20]
@@ -35,7 +35,7 @@
 highway=tertiary [0x05 road_class=1 road_speed=3 resolution 20]
 highway=track [0x0a road_class=0 road_speed=1 resolution 21]
 highway=trunk  [0x02 road_class=3 road_speed=5 resolution 16]
-highway=trunk_link [0x08 road_class=3 road_speed=3 resolution 16]
+highway=trunk_link [0x08 road_class=3 road_speed=3 resolution 17]
 highway=unclassified [0x06 road_class=1 road_speed=2 resolution 21]
 highway=unsurfaced [0x0a road_class=0 road_speed=1 resolution 21]
 
Index: src/uk/me/parabola/imgfmt/app/net/RoadDef.java
===================================================================
--- src/uk/me/parabola/imgfmt/app/net/RoadDef.java	(revision 1001)
+++ src/uk/me/parabola/imgfmt/app/net/RoadDef.java	(working copy)
@@ -344,6 +344,7 @@
 
 	// the first point in the road is a node (the above routing node)
 	private boolean startsWithNode = true;
+	private boolean endsWithNode = true;
 	// number of nodes in the road
 	private int nnodes;
 
@@ -373,6 +374,10 @@
 		startsWithNode = s;
 	}
 
+	public void setEndsWithNode(boolean s) {
+		endsWithNode = s;
+	}
+
 	public void setNumNodes(int n) {
 		nnodes = n;
 	}
@@ -406,12 +411,16 @@
 		int nbits = nnodes;
 		if (!startsWithNode)
 			nbits++;
+		if (!endsWithNode)
+			nbits++;
 		writer.putChar((char) nbits);
 		boolean[] bits = new boolean[nbits];
 		for (int i = 0; i < bits.length; i++)
 			bits[i] = true;
 		if (!startsWithNode)
 			bits[0] = false;
+		if (!endsWithNode)
+			bits[bits.length-1] = false;
 		for (int i = 0; i < bits.length; i += 8) {
 			int b = 0;
             for (int j = 0; j < 8 && j < bits.length - i; j++)
Index: src/uk/me/parabola/mkgmap/build/MapBuilder.java
===================================================================
--- src/uk/me/parabola/mkgmap/build/MapBuilder.java	(revision 1001)
+++ src/uk/me/parabola/mkgmap/build/MapBuilder.java	(working copy)
@@ -20,6 +20,7 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.LinkedList;
 import java.util.List;
 
 import uk.me.parabola.imgfmt.app.Coord;
@@ -51,12 +52,14 @@
 import uk.me.parabola.log.Logger;
 import uk.me.parabola.mkgmap.Version;
 import uk.me.parabola.mkgmap.filters.BaseFilter;
+import uk.me.parabola.mkgmap.filters.DouglasPeuckerFilter;
 import uk.me.parabola.mkgmap.filters.FilterConfig;
 import uk.me.parabola.mkgmap.filters.LineSplitterFilter;
 import uk.me.parabola.mkgmap.filters.MapFilter;
 import uk.me.parabola.mkgmap.filters.MapFilterChain;
 import uk.me.parabola.mkgmap.filters.PolygonSplitterFilter;
 import uk.me.parabola.mkgmap.filters.RemoveEmpty;
+import uk.me.parabola.mkgmap.filters.SizeFilter;
 import uk.me.parabola.mkgmap.filters.SmoothingFilter;
 import uk.me.parabola.mkgmap.general.Exit;
 import uk.me.parabola.mkgmap.general.LevelInfo;
@@ -102,6 +105,10 @@
 	private String countryAbbr = "ABC";
 	private String regionName;
 	private String regionAbbr;
+
+	private double reducePointError;
+	private boolean mergeLines;
+
 	private int		locationAutofillLevel;
 	private boolean	poiAddresses = true;
 	private int		poiDisplayFlags;
@@ -119,6 +126,8 @@
 		countryAbbr = props.getProperty("country-abbr", countryAbbr);
 		regionName = props.getProperty("region-name", null);
 		regionAbbr = props.getProperty("region-abbr", null);
+		reducePointError = props.getProperty("reduce-point-density", 2.6);
+		mergeLines = props.containsKey("merge-lines");
 		
 		if(props.getProperty("no-poi-address", null) != null)
 			poiAddresses = false;
@@ -800,6 +809,126 @@
 		}
 	}
 
+	class MultiHashMap<K,V> extends HashMap<K,List<V>> {
+	
+		/**
+		* the empty list to be returned when there is key without values.
+		*/
+		private List<V> emptyList = Collections.unmodifiableList(new ArrayList<V>(0));
+
+		/**
+		* Returns the list of values associated with the given key.
+		*
+		* @param o the key to get the values for.
+		* @return a list of values for the given keys or the empty list of no such
+		*         value exist.
+		*/
+		public List<V> get(K key) {
+			List<V> result = super.get(key);
+			return result == null ? emptyList : result;
+		}
+
+
+		public V add(K key, V value )
+		{
+		    
+		    List<V> values = super.get(key);
+		    if (values == null ) {
+		        values = new LinkedList<V>();
+		        super.put( key, values );
+		    }
+		    
+		    boolean results = values.add(value);
+		    
+		    return ( results ? value : null );
+		}
+
+		public V remove(K key, V value )
+		{
+		    
+		    List<V> values = super.get(key);
+		    if (values == null )
+				return null;
+		
+		    values.remove(value);
+			
+			if (values.size() == 0)
+				super.remove(values);
+
+			return value;
+		}
+	}
+
+
+	class LineMergeFilter{
+		List<MapLine> linesMerged;
+		MultiHashMap<Coord, MapLine> startPoints = new MultiHashMap<Coord, MapLine>();
+		MultiHashMap<Coord, MapLine> endPoints = new MultiHashMap<Coord, MapLine>();
+	
+		public LineMergeFilter() {}
+
+		private void addLine(MapLine line) {
+			linesMerged.add(line);
+			List<Coord> points = line.getPoints();
+			startPoints.add(points.get(0), line);
+			endPoints.add(points.get(points.size()-1), line);
+		}
+		
+		private void addPointsAtStart(MapLine line, List<Coord> additionalPoints) {
+			log.info("merged lines before " + line.getName());
+			List<Coord> points = line.getPoints();
+			startPoints.remove(points.get(0), line);
+			line.insertPointsAtStart(additionalPoints);
+			startPoints.add(points.get(0), line);
+		}
+		
+		private void addPointsAtEnd(MapLine line, List<Coord> additionalPoints) {
+			log.info("merged lines after " + line.getName());
+			List<Coord> points = line.getPoints();
+			endPoints.remove(points.get(points.size()-1), line);
+			line.insertPointsAtEnd(additionalPoints);
+			endPoints.add(points.get(points.size()-1), line);
+		}
+
+		public List<MapLine> merge(List<MapLine> lines) {
+			linesMerged = new ArrayList<MapLine>(lines.size());	//better use LinkedList??
+			for (MapLine line1 : lines) {
+				boolean isMerged = false;
+				List<Coord> points1 = line1.getPoints();
+				Coord start = points1.get(0); 
+				Coord end = points1.get(points1.size()-1); 
+
+				// Search for start point in hashlist
+				for (MapLine line2 : startPoints.get(end)) {
+					if (line1.isSimilar(line2)) {
+						addPointsAtStart(line2, points1);
+						isMerged = true;
+						break;
+					}
+				}
+
+				// Search for endpoint in hashlist
+				for (MapLine line2 : endPoints.get(start)) {
+					if (line1.isSimilar(line2) /*&& 
+						!line2.getPoints().get(0).equals(start)*/) {	// don't mak a loop a double loop
+						addPointsAtEnd(line2, points1);
+						isMerged = true;
+						break;
+					}
+				}
+				if (isMerged)
+					continue;
+
+				// No matching, create a new line
+				MapLine l = line1.copy();
+				List<Coord> p = new ArrayList<Coord>(line1.getPoints());	//use better LinkedList for performance?
+				l.setPoints(p);				
+				addLine(l);
+			}
+			return linesMerged;
+		}
+	
+	}
 	/**
 	 * Step through the lines, filter, simplify if necessary, and create a map
 	 * line which is then added to the map.
@@ -820,8 +949,16 @@
 		FilterConfig config = new FilterConfig();
 		config.setResolution(res);
 
+
+		if (mergeLines) {
+			LineMergeFilter merger = new LineMergeFilter();
+			lines = merger.merge(lines);
+		}
+
 		LayerFilterChain filters = new LayerFilterChain(config);
-		filters.addFilter(new SmoothingFilter());
+		filters.addFilter(new SizeFilter());
+//		filters.addFilter(new SmoothingFilter());
+		filters.addFilter(new DouglasPeuckerFilter(reducePointError));
 		filters.addFilter(new LineSplitterFilter());
 		filters.addFilter(new RemoveEmpty());
 		filters.addFilter(new LineAddFilter(div, map, doRoads));
@@ -854,7 +991,11 @@
 		FilterConfig config = new FilterConfig();
 		config.setResolution(res);
 		LayerFilterChain filters = new LayerFilterChain(config);
-		filters.addFilter(new SmoothingFilter());
+		filters.addFilter(new SizeFilter());
+//		filters.addFilter(new SmoothingFilter());
+//DouglasPeucker behaves at the moment not really optimal at low zooms, but acceptable.
+//Is there an similar algorithm for polygons?
+		filters.addFilter(new DouglasPeuckerFilter(reducePointError));
 		filters.addFilter(new PolygonSplitterFilter());
 		filters.addFilter(new RemoveEmpty());
 		filters.addFilter(new ShapeAddFilter(div, map));
Index: src/uk/me/parabola/mkgmap/filters/DouglasPeuckerFilter.java
===================================================================
--- src/uk/me/parabola/mkgmap/filters/DouglasPeuckerFilter.java	(revision 0)
+++ src/uk/me/parabola/mkgmap/filters/DouglasPeuckerFilter.java	(revision 0)
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2007 Steve Ratcliffe
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ * 
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ * 
+ */
+package uk.me.parabola.mkgmap.filters;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import uk.me.parabola.imgfmt.app.Coord;
+import uk.me.parabola.imgfmt.app.CoordNode;
+import uk.me.parabola.mkgmap.general.MapElement;
+import uk.me.parabola.mkgmap.general.MapLine;
+
+/**
+ * This is a filter that smooths out lines at low resolutions. If the element
+ * has no size at all at the given resolution, then it is not passed on down
+ * the chain at all is excluded from the map at that resolution.
+ */
+public class DouglasPeuckerFilter implements MapFilter {
+
+	private static final double ERROR_DISTANCE = 5.4 / 2;	//One unit is 5.4 m, so error dist is 2.6m
+															//Can be increased more, but may lead to artifacts on T-crossings
+
+	private double filterDistance;
+	private double maxErrorDistance;
+
+	public DouglasPeuckerFilter(double filterDistance) {
+		this.filterDistance = filterDistance;
+	}
+		
+
+	public void init(FilterConfig config) {
+		int shift = config.getShift();
+		this.maxErrorDistance = filterDistance * (1<<shift);
+	}
+
+	/**
+	 * This applies to both lines and polygons.  We are going to smooth out
+	 * the points in the line so that you do not get jaggies. 
+	 *
+	 * @param element A map element that will be a line or a polygon.
+	 * @param next This is used to pass the possibly transformed element onward.
+	 */
+	public void doFilter(MapElement element, MapFilterChain next) {
+		MapLine line = (MapLine) element;
+
+		List<Coord> points = line.getPoints();
+		int n = points.size()-1;
+
+		// Create a new list to rewrite the points into. Don't alter the original one
+		List<Coord> coords = new ArrayList<Coord>(n);
+		coords.addAll(points);
+
+		// If the first point is identic with the last one (a polygon), drop it
+		// Otherwise douglasPeucker will not work!
+		while ((n > 0) && coords.get(0).equals(coords.get(n))) {
+			coords.remove(n);
+			n--;
+		}
+
+//#if (Node version)
+//Dont touch Coords, which are nodes. 
+//So points at croosings will not be moved
+		// For now simplify all points, which are not nodes
+		// and no start and no end point
+		// Loop runs downwards, as the list length gets modified while running
+		int endIndex = coords.size()-1;
+		for(int i = endIndex-1; i > 0; i--) {
+			Coord p = coords.get(i);
+			//int highwayCount = p.getHighwayCount();
+			
+			// If a node in the line use the douglas peucker algorithm for upper segment
+			// TODO: Should consider only nodes connected to roads visible at current resolution.
+			if (p instanceof CoordNode) {
+				douglasPeucker(coords, i, endIndex, maxErrorDistance);
+				endIndex = i;
+				i--;
+			}
+		}
+		// Simplify the rest
+		douglasPeucker(coords, 0, endIndex, maxErrorDistance);
+
+//#else Straight version
+//Do the douglasPeucker on the whole line. 
+//Deletes more points, but may lead to incorrect diplay of crossings at given high error distances 
+/*		
+		douglasPeucker(coords, 0, n, maxErrorDistance);
+	*/	
+//#endif
+		MapLine newline = line.copy();
+
+		newline.setPoints(coords);
+		next.doFilter(newline);
+	}
+
+	/**
+	 * Reduces point density by Douglas-Peucker algorithm
+	 *
+	 * @param points The list of points to simplify.
+	 * @param startIndex First index of segment. The point with this index will not be changed
+	 * @param endIndex Last index of segment. The point with this index will not be changed
+	 * @param allowedError Maximal allowed error to be introduced by simplification. 
+	 * returns number of removed points.
+	 */
+	public void douglasPeucker(List<Coord> points, int startIndex, int endIndex, double allowedError) 
+	{
+		if (startIndex >= endIndex)
+			return;
+
+		double maxDistance = 0;		//Highest distance	
+		int maxIndex = endIndex;	//Index of highest distance
+
+		Coord a = points.get(startIndex);
+		Coord b = points.get(endIndex);
+		double ab = a.distance(b);
+
+		// Find point with highest distance.
+		// Loop runs downwards, as the list length gets modified while running
+		for(int i = endIndex-1; i > startIndex; i--) {
+			Coord p = points.get(i);
+			double ap = p.distance(a);
+			double bp = p.distance(b);
+			double abpa = (ab+ap+bp)/2;
+			double distance = 2 * Math.sqrt(abpa * (abpa-ab) * (abpa-ap) * (abpa-bp)) / ab;
+			if (distance > maxDistance) {
+				maxDistance = distance;
+				maxIndex = i;
+			}
+		}
+		if (maxDistance > allowedError) {
+			// Call recursive for both parts
+			douglasPeucker(points, maxIndex, endIndex, allowedError);		
+			douglasPeucker(points, startIndex, maxIndex, allowedError);		
+		}
+		else {
+			// All points in tolerance, delete all of them.
+			for(int i = endIndex-1; i > startIndex; i--) {
+				points.remove(i);
+			}
+		}
+	}
+
+}
Index: src/uk/me/parabola/mkgmap/filters/RemoveEmpty.java
===================================================================
--- src/uk/me/parabola/mkgmap/filters/RemoveEmpty.java	(revision 1001)
+++ src/uk/me/parabola/mkgmap/filters/RemoveEmpty.java	(working copy)
@@ -19,6 +19,7 @@
 import uk.me.parabola.log.Logger;
 import uk.me.parabola.mkgmap.general.MapElement;
 import uk.me.parabola.mkgmap.general.MapLine;
+import uk.me.parabola.mkgmap.general.MapShape;
 
 /**
  * Filter for removing empty elements and degenerate elements, for example
@@ -49,6 +50,14 @@
 				return;
 			}
 		}
+		if (element instanceof MapShape) {
+			MapShape mapLine = (MapShape) element;
+			if (mapLine.getPoints().size() <= 2) {
+				if (log.isDebugEnabled())
+					log.debug("dropping degenerate element");
+				return;
+			}
+		}
 		next.doFilter(element);
 	}
 }
Index: src/uk/me/parabola/mkgmap/filters/SizeFilter.java
===================================================================
--- src/uk/me/parabola/mkgmap/filters/SizeFilter.java	(revision 0)
+++ src/uk/me/parabola/mkgmap/filters/SizeFilter.java	(revision 0)
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2007 Steve Ratcliffe
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ * 
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ * 
+ * 
+ * Author: Steve Ratcliffe
+ * Create date: Dec 1, 2007
+ */
+package uk.me.parabola.mkgmap.filters;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import uk.me.parabola.imgfmt.app.Coord;
+import uk.me.parabola.mkgmap.general.MapElement;
+import uk.me.parabola.mkgmap.general.MapLine;
+
+/**
+ * This is a filter that dismisses elements too small for the current resolution
+ */
+public class SizeFilter implements MapFilter {
+
+	// Minsize==1 may cause small holes in QLandkarte, but does not at etrex!
+	private static final int MIN_SIZE = 0;	// Drop elements smaller than one unit
+
+	private int minSize;
+
+	public void init(FilterConfig config) {
+		minSize = MIN_SIZE * (1<<config.getShift());
+	}
+
+	/**
+	 * This applies to both lines and polygons. 
+	 * Elements too small for current resolution will be dropped.
+	 *
+	 * @param element A map element that will be a line or a polygon.
+	 * @param next This is used to pass the possibly transformed element onward.
+	 */
+	public void doFilter(MapElement element, MapFilterChain next) {
+		MapLine line = (MapLine) element;
+
+		// Drop things that are too small to get displayed
+		if (line.getBounds().getMaxDimention() < minSize)
+			return;
+
+		next.doFilter(line);
+	}
+}
Index: src/uk/me/parabola/mkgmap/filters/SmoothingFilter.java
===================================================================
--- src/uk/me/parabola/mkgmap/filters/SmoothingFilter.java	(revision 1001)
+++ src/uk/me/parabola/mkgmap/filters/SmoothingFilter.java	(working copy)
@@ -68,10 +68,6 @@
 			return;
 		}
 
-		// Drop things that are too small.
-		if (line.getBounds().getMaxDimention() < MIN_SIZE)
-			return;
-
 		// If the line is not very long then just let it through.  This is done
 		// mainly for the background polygons.
 		List<Coord> points = line.getPoints();
Index: src/uk/me/parabola/mkgmap/general/LineClipper.java
===================================================================
--- src/uk/me/parabola/mkgmap/general/LineClipper.java	(revision 1001)
+++ src/uk/me/parabola/mkgmap/general/LineClipper.java	(working copy)
@@ -163,4 +163,4 @@
 		return false;
 	}
 }
-	
\ No newline at end of file
+	
Index: src/uk/me/parabola/mkgmap/general/MapElement.java
===================================================================
--- src/uk/me/parabola/mkgmap/general/MapElement.java	(revision 1001)
+++ src/uk/me/parabola/mkgmap/general/MapElement.java	(working copy)
@@ -166,6 +166,24 @@
 		this.type = type;
 	}
 
+	public boolean isSimilar(MapElement other) {
+		if (this.minResolution != other.minResolution)
+			return false;
+		if (this.maxResolution != other.maxResolution)
+			return false;
+		if (this.type != other.type)
+			return false;
+
+		String thisName = getName();
+		String otherName = other.getName();
+
+		if (thisName == null && otherName == null)	
+			return true;
+		if (thisName!=null && otherName!=null && thisName.equals(otherName))
+			return true;
+		return false;
+	}
+
 	/**
 	 * Get the 'location' of the element.  This is the mid point of the bounding
 	 * box for the element.  For a point, this will be the coordinates of the
Index: src/uk/me/parabola/mkgmap/general/MapLine.java
===================================================================
--- src/uk/me/parabola/mkgmap/general/MapLine.java	(revision 1001)
+++ src/uk/me/parabola/mkgmap/general/MapLine.java	(working copy)
@@ -60,8 +60,13 @@
 		if (this.points != null)
 			log.warn("overwriting points");
 		assert points != null : "trying to set null points";
+		assert points.size() != 0 : "trying to set points with zero length";
 
 		this.points = points;
+		testForConsecutivePoints(points);
+	}
+	
+	public void testForConsecutivePoints(List<Coord> points) {
 		Coord last = null;
 		for (Coord co : points) {
 			if (last != null && last.equals(co))
@@ -71,6 +76,18 @@
 		}
 	}
 
+	public void insertPointsAtStart(List<Coord> additionalPoints) {
+		testForConsecutivePoints(additionalPoints);
+		points.addAll(0, additionalPoints);
+		points.remove(additionalPoints.size()-1);	//End node exists now twice
+	}
+
+	public void insertPointsAtEnd(List<Coord> additionalPoints) {
+		testForConsecutivePoints(additionalPoints);
+		points.remove(points.size()-1); 
+		points.addAll(additionalPoints);
+	}
+
 	public boolean isDirection() {
 		return direction;
 	}
Index: src/uk/me/parabola/mkgmap/general/MapRoad.java
===================================================================
--- src/uk/me/parabola/mkgmap/general/MapRoad.java	(revision 1001)
+++ src/uk/me/parabola/mkgmap/general/MapRoad.java	(working copy)
@@ -88,10 +88,15 @@
 		this.roadDef.setNoThroughRouting();
 	}
 
+	//TODO: calculate from points list at constructor (if CoordNode)
 	public void setStartsWithNode(boolean s) {
 		this.roadDef.setStartsWithNode(s);
 	}
 
+	public void setEndsWithNode(boolean s) {
+		this.roadDef.setEndsWithNode(s);
+	}
+
 	public void setInternalNodes(boolean s) {
 		this.roadDef.setInternalNodes(s);
 	}
Index: src/uk/me/parabola/mkgmap/general/MapShape.java
===================================================================
--- src/uk/me/parabola/mkgmap/general/MapShape.java	(revision 1001)
+++ src/uk/me/parabola/mkgmap/general/MapShape.java	(working copy)
@@ -67,6 +67,20 @@
 		return contains(this.getPoints(), co, true);
 	}
 	
+/*	public void setPoints(List<Coord> points) {
+		assert points != null : "trying to set null points";
+
+		int n = points.size()-1;
+
+		// If the first point is identic with the last one, drop it
+		// The type MapShape means that this is a polygon, so we dont need the identic point any more.
+		while ((n > 0) && points.get(0).equals(points.get(n))) {
+			points.remove(n);
+			n--;
+		}
+	}
+*/
+
 	/* 
 	 * Checks if a point is contained within a shape.
 	 * 
@@ -84,6 +98,7 @@
 		if (points.size() < 3)
 			return false;
 
+
 		// complete the shape if we're dealing with a MapShape that is not closed
 		Coord start = points.get(0);
 		Coord end = points.get(points.size() - 1);
@@ -197,6 +212,4 @@
 		}
 		return false;
 	}
-					
-					
 }
Index: src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java
===================================================================
--- src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java	(revision 1001)
+++ src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java	(working copy)
@@ -49,7 +49,9 @@
 import uk.me.parabola.mkgmap.reader.osm.Rule;
 import uk.me.parabola.mkgmap.reader.osm.Style;
 import uk.me.parabola.mkgmap.reader.osm.Way;
+import uk.me.parabola.util.EnhancedProperties;
 
+
 /**
  * Convert from OSM to the mkgmap intermediate format using a style.
  * A style is a collection of files that describe the mappings to be used
@@ -68,6 +70,9 @@
 	private Area bbox;
 	private Set<Coord> boundaryCoords = new HashSet<Coord>();
 
+	//Suppresses end nodes at dead end streets.
+	private boolean suppressDeadEndNodes;
+
 	// restrictions associates lists of turn restrictions with the
 	// Coord corresponding to the restrictions' 'via' node
 	private final Map<Coord, List<RestrictionRelation>> restrictions = new HashMap<Coord, List<RestrictionRelation>>();
@@ -126,9 +131,11 @@
 		}
 	};
 
-	public StyledConverter(Style style, MapCollector collector) {
+	public StyledConverter(Style style, MapCollector collector, EnhancedProperties props) {
 		this.collector = collector;
 
+		suppressDeadEndNodes = props.containsKey("suppress-dead-end-nodes");
+
 		nameTagList = style.getNameTagList();
 		wayRules = style.getWayRules();
 		nodeRules = style.getNodeRules();
@@ -532,8 +539,11 @@
 		String wayName = way.getName();
 
 		// make sure the way has nodes at each end
-		points.get(0).incHighwayCount();
-		points.get(points.size() - 1).incHighwayCount();
+		// supress end nodes if allowed and not a boundary node
+		if (!suppressDeadEndNodes || boundaryCoords.contains(points.get(0)))
+			points.get(0).incHighwayCount();
+		if (!suppressDeadEndNodes || boundaryCoords.contains(points.get(points.size() - 1)))
+			points.get(points.size() - 1).incHighwayCount();
 
 		// collect the Way's nodes
 		for(int i = 0; i < points.size(); ++i) {
@@ -722,6 +732,7 @@
 			}
 
 			road.setStartsWithNode(nodeIndices.get(0) == 0);
+			road.setEndsWithNode(nodeIndices.get(nodeIndices.size()-1) == points.size()-1);
 			road.setInternalNodes(hasInternalNodes);
 		}
 
Index: src/uk/me/parabola/mkgmap/reader/osm/xml/Osm5MapDataSource.java
===================================================================
--- src/uk/me/parabola/mkgmap/reader/osm/xml/Osm5MapDataSource.java	(revision 1001)
+++ src/uk/me/parabola/mkgmap/reader/osm/xml/Osm5MapDataSource.java	(working copy)
@@ -32,6 +32,7 @@
 import uk.me.parabola.mkgmap.osmstyle.eval.SyntaxException;
 import uk.me.parabola.mkgmap.reader.osm.OsmConverter;
 import uk.me.parabola.mkgmap.reader.osm.Style;
+import uk.me.parabola.util.EnhancedProperties;
 
 import org.xml.sax.SAXException;
 
@@ -104,7 +105,7 @@
 	 */
 	private OsmConverter createStyler() {
 
-		Properties props = getConfig();
+		EnhancedProperties props = getConfig();
 		String loc = props.getProperty("style-file");
 		if (loc == null)
 			loc = props.getProperty("map-features");
@@ -120,7 +121,7 @@
 			style.applyOptionOverride(props);
 			setStyle(style);
 
-			converter = new StyledConverter(style, mapper);
+			converter = new StyledConverter(style, mapper, props);
 		} catch (SyntaxException e) {
 			System.err.println("Error in style: " + e.getMessage());
 			throw new ExitException("Could not open style " + name);
Index: src/uk/me/parabola/mkgmap/reader/osm/xml/Osm5XmlHandler.java
===================================================================
--- src/uk/me/parabola/mkgmap/reader/osm/xml/Osm5XmlHandler.java	(revision 1001)
+++ src/uk/me/parabola/mkgmap/reader/osm/xml/Osm5XmlHandler.java	(working copy)
@@ -393,6 +393,7 @@
 					Double.parseDouble(xmlattr.getValue("minlon")),
 					Double.parseDouble(xmlattr.getValue("maxlat")),
 					Double.parseDouble(xmlattr.getValue("maxlon")));
+			log.debug("Map bbox from xmlattr: " + bbox);
 		} catch (NumberFormatException e) {
 			// just ignore it
 			log.warn("NumberformatException: Cannot read bbox");
@@ -404,7 +405,6 @@
 		try {
 			setBBox(Double.parseDouble(f[0]), Double.parseDouble(f[1]),
 					Double.parseDouble(f[2]), Double.parseDouble(f[3]));
-			log.debug("Map bbox: " + bbox);
 		} catch (NumberFormatException e) {
 			// just ignore it
 			log.warn("NumberformatException: Cannot read bbox");
