Index: src/uk/me/parabola/mkgmap/filters/PolygonSizeSplitterFilter.java
===================================================================
--- src/uk/me/parabola/mkgmap/filters/PolygonSizeSplitterFilter.java	(revision 1805)
+++ src/uk/me/parabola/mkgmap/filters/PolygonSizeSplitterFilter.java	(working copy)
@@ -97,7 +97,7 @@
 		if (shape.getType() == 0x4a)
 			return true;
 		Area bounds = shape.getBounds();
-		return bounds.getMaxDimention() < maxSize
+		return bounds.getMaxDimension() < maxSize
 				&& bounds.getWidth() < 0x7fff
 				&& bounds.getHeight() < 0x7fff
 				;
Index: src/uk/me/parabola/mkgmap/filters/SizeFilter.java
===================================================================
--- src/uk/me/parabola/mkgmap/filters/SizeFilter.java	(revision 1805)
+++ src/uk/me/parabola/mkgmap/filters/SizeFilter.java	(working copy)
@@ -44,7 +44,7 @@
 		MapLine line = (MapLine) element;
 
 		// Drop things that are too small to get displayed
-		if (line.getBounds().getMaxDimention() < minSize)
+		if (line.getBounds().getMaxDimension() < minSize)
 			return;
 
 		next.doFilter(line);
Index: src/uk/me/parabola/mkgmap/filters/LineSizeSplitterFilter.java
===================================================================
--- src/uk/me/parabola/mkgmap/filters/LineSizeSplitterFilter.java	(revision 1805)
+++ src/uk/me/parabola/mkgmap/filters/LineSizeSplitterFilter.java	(working copy)
@@ -68,14 +68,14 @@
 
 		MapLine line = (MapLine) element;
 
-		if (line.getBounds().getMaxDimention() < maxSize) {
+		if (line.getBounds().getMaxDimension() < maxSize) {
 			next.doFilter(element);
 			return;
 		}
 
 		if(line instanceof MapRoad) {
 			MapRoad road = ((MapRoad)line);
-			log.error("Way " + road.getRoadDef() + " has a max dimension of " + line.getBounds().getMaxDimention() + " and is about to be split (routing will be broken)");
+			log.error("Way " + road.getRoadDef() + " has a max dimension of " + line.getBounds().getMaxDimension() + " and is about to be split (routing will be broken)");
 		}
 
 		List<Coord> points = line.getPoints();
Index: src/uk/me/parabola/mkgmap/build/MapBuilder.java
===================================================================
--- src/uk/me/parabola/mkgmap/build/MapBuilder.java	(revision 1805)
+++ src/uk/me/parabola/mkgmap/build/MapBuilder.java	(working copy)
@@ -22,6 +22,7 @@
 import java.util.HashMap;
 import java.util.List;
 
+import uk.me.parabola.imgfmt.app.Area;
 import uk.me.parabola.imgfmt.app.Coord;
 import uk.me.parabola.imgfmt.app.Exit;
 import uk.me.parabola.imgfmt.app.Label;
@@ -524,6 +525,143 @@
 		}
 	}
 
+	private static class FilteredSource implements MapDataSource {
+
+		private final MapDataSource org;
+		private final List<MapLine> lines;
+		private final List<MapShape> shapes;
+		
+		public FilteredSource(MapDataSource org) {
+			this.org = org;
+			this.lines = new ArrayList<MapLine>();
+			this.shapes = new ArrayList<MapShape>();
+		}
+		
+		@Override
+		public Area getBounds() {
+			return org.getBounds();
+		}
+
+		@Override
+		public List<MapPoint> getPoints() {
+			return org.getPoints();
+		}
+
+		@Override
+		public List<MapLine> getLines() {
+			return lines;
+		}
+		
+		public void addLine(MapLine line) {
+			lines.add(line);
+		}
+
+		@Override
+		public List<MapShape> getShapes() {
+			return shapes;
+		}
+		
+		public void addShape(MapShape shape) {
+			shapes.add(shape);
+		}
+
+		@Override
+		public RoadNetwork getRoadNetwork() {
+			return org.getRoadNetwork();
+		}
+
+		@Override
+		public List<Overview> getOverviews() {
+			return org.getOverviews();
+		}
+
+//		@Override
+//		public void config(EnhancedProperties props) {
+//			org.config(props);
+//		}
+//
+//		@Override
+//		public boolean isFileSupported(String name) {
+//			return org.isFileSupported(name);
+//		}
+//
+//		@Override
+//		public void load(String name) throws FileNotFoundException,
+//				FormatException {
+//			org.load(name);
+//		}
+//
+//		@Override
+//		public LevelInfo[] mapLevels() {
+//			return org.mapLevels();
+//		}
+//
+//		@Override
+//		public String[] copyrightMessages() {
+//			return org.copyrightMessages();
+//		}
+	}
+	
+	private static class ElementPreAddFilter extends BaseFilter implements MapFilter {
+		private final FilteredSource source;
+		
+		public ElementPreAddFilter(FilteredSource source) {
+			this.source= source;
+		}
+
+		public void doFilter(MapElement element, MapFilterChain next) {
+			if (element instanceof MapShape) {
+				source.addShape((MapShape) element);
+			} else if (element instanceof MapLine) {
+				source.addLine((MapLine) element);
+			} else {
+				log.error("Unsupported element type "+element.getClass());
+			}
+		}
+	}
+	
+	private MapDataSource runPreFilters(MapDataSource src, Zoom zoom) {
+		// Run line filters
+		
+		FilteredSource filteredSrc = new FilteredSource(src);
+		
+		int res = zoom.getResolution();
+		
+		FilterConfig config = new FilterConfig();
+		config.setResolution(res);
+		
+		List<MapLine> lines = src.getLines();
+		
+		if (mergeLines && res < 24) {
+			LineMergeFilter merger = new LineMergeFilter();
+			lines = merger.merge(lines);
+		}
+
+		LayerFilterChain lineFilters = new LayerFilterChain(config);
+		lineFilters.addFilter(new LineSplitterFilter());
+		lineFilters.addFilter(new RemoveEmpty());
+		lineFilters.addFilter(new ElementPreAddFilter(filteredSrc));
+		for (MapLine line : lines) {
+			lineFilters.startFilter(line);
+		}	
+		
+		
+		LayerFilterChain shapeFilters = new LayerFilterChain(config);
+		shapeFilters.addFilter(new PolygonSplitterFilter());
+		shapeFilters.addFilter(new RemoveEmpty());
+		shapeFilters.addFilter(new ElementPreAddFilter(filteredSrc));
+		for (MapShape shape : src.getShapes()) {
+			shapeFilters.startFilter(shape);
+		}
+		
+		log.info("After pre filtering at resolution",res);
+		log.info("Lines:",src.getLines().size(), filteredSrc.getLines().size());
+		log.info("Shapes:",src.getShapes().size(), filteredSrc.getShapes().size());
+		
+		return filteredSrc;
+	}
+	
+	
 	/**
 	 * Drive the map generation by stepping through the levels, generating the
 	 * subdivisions for the level and filling in the map elements that should
@@ -542,15 +680,14 @@
 		LevelInfo levelInfo = levels[0];
 
 		// If there is already a top level zoom, then we shouldn't add our own
-		Subdivision topdiv;
+		Zoom topZoom;
 		if (levelInfo.isTop()) {
 			// There is already a top level definition.  So use the values from it and
 			// then remove it from the levels definition.
 
 			levels = Arrays.copyOfRange(levels, 1, levels.length);
 
-			Zoom zoom = map.createZoom(levelInfo.getLevel(), levelInfo.getBits());
-			topdiv = makeTopArea(src, map, zoom);
+			topZoom = map.createZoom(levelInfo.getLevel(), levelInfo.getBits());
 		} else {
 			// We have to automatically create the definition for the top zoom level.
 			int maxBits = getMaxBits(src);
@@ -560,12 +697,15 @@
 				maxBits = levelInfo.getBits() - 1;
 
 			// Create the empty top level
-			Zoom zoom = map.createZoom(levelInfo.getLevel() + 1, maxBits);
-			topdiv = makeTopArea(src, map, zoom);
+			topZoom = map.createZoom(levelInfo.getLevel() + 1, maxBits);
 		}
 
+		MapDataSource filteredSrc = runPreFilters(src, topZoom);
+		
+		Subdivision topdiv = makeTopArea(filteredSrc, map, topZoom);
+
 		// We start with one map data source.
-		List<SourceSubdiv> srcList = Collections.singletonList(new SourceSubdiv(src, topdiv));
+		List<SourceSubdiv> srcList = Collections.singletonList(new SourceSubdiv(filteredSrc, topdiv));
 
 		// Now the levels filled with features.
 		for (LevelInfo linfo : levels) {
@@ -875,14 +1015,6 @@
 		FilterConfig config = new FilterConfig();
 		config.setResolution(res);
 
-
-		//TODO: Maybe this is the wrong place to do merging.
-		// Maybe more efficient if merging before creating subdivisions.
-		if (mergeLines && res < 24) {
-			LineMergeFilter merger = new LineMergeFilter();
-			lines = merger.merge(lines);
-		}
-
 		LayerFilterChain filters = new LayerFilterChain(config);
 		if (enableLineCleanFilters && (res < 24)) {
 			filters.addFilter(new PreserveHorizontalAndVerticalLinesFilter());
@@ -932,7 +1064,6 @@
 			if(reducePointError > 0)
 				filters.addFilter(new DouglasPeuckerFilter(reducePointError));
 		}
-		filters.addFilter(new PolygonSplitterFilter());
 		filters.addFilter(new RemoveEmpty());
 		filters.addFilter(new ShapeAddFilter(div, map));
 
@@ -969,7 +1100,7 @@
 	 *         whole map.
 	 */
 	private int getMaxBits(MapDataSource src) {
-		int topshift = Integer.numberOfLeadingZeros(src.getBounds().getMaxDimention());
+		int topshift = Integer.numberOfLeadingZeros(src.getBounds().getMaxDimension());
 		int minShift = Math.max(CLEAR_TOP_BITS - topshift, 0);
 		return 24 - minShift;
 	}
Index: src/uk/me/parabola/mkgmap/build/MapArea.java
===================================================================
--- src/uk/me/parabola/mkgmap/build/MapArea.java	(revision 1805)
+++ src/uk/me/parabola/mkgmap/build/MapArea.java	(working copy)
@@ -17,9 +17,12 @@
 package uk.me.parabola.mkgmap.build;
 
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.List;
 
 import uk.me.parabola.imgfmt.app.Area;
+import uk.me.parabola.imgfmt.app.Coord;
 import uk.me.parabola.imgfmt.app.trergn.Overview;
 import uk.me.parabola.log.Logger;
 import uk.me.parabola.mkgmap.filters.FilterConfig;
@@ -34,6 +37,7 @@
 import uk.me.parabola.mkgmap.general.MapPoint;
 import uk.me.parabola.mkgmap.general.MapShape;
 import uk.me.parabola.mkgmap.general.RoadNetwork;
+import uk.me.parabola.util.GpxCreator;
 
 /**
  * A sub area of the map.  We have to divide the map up into areas to meet the
@@ -194,21 +198,147 @@
 		addToBounds(area);
 	}
 
+	
+	private final class LatitudeComparator implements Comparator<Coord> {
+
+		@Override
+		public int compare(Coord o1, Coord o2) {
+			if (o1 == o2) {
+				return 0;
+			}
+			if (o1.getLatitude() == o2.getLatitude()) {
+				return o1.getLongitude() - o2.getLongitude();
+			} else {
+				return o1.getLatitude() - o2.getLatitude();
+			}
+		}
+
+	}
+	
+	public MapArea[] splitDynamically(int nLong, int nLat, int resolution) {
+		log.info("Split dynamically",nLong,nLat,resolution);
+		
+		List<Coord> coordList = new ArrayList<Coord>(points.size()+lines.size()+shapes.size()); 
+		
+		for (MapPoint p : this.points) {
+			coordList.add(p.getLocation());
+		}
+
+		for (MapLine l : this.lines) {
+			// Drop any zero sized lines.
+			if (l.getBounds().getMaxDimension() <= 0)
+				continue;
+
+			coordList.add(l.getLocation());
+		}
+
+		for (MapShape e : this.shapes) {
+			coordList.add(e.getLocation());
+		}
+
+		if (coordList.size() < nLong) {
+			log.info("Cannot split into",nLong,"longitude parts because there is only",coordList.size(),"element");
+			nLong = coordList.size();
+		}
+		if (coordList.size() < nLat) {
+			log.info("Cannot split into",nLat,"latitude parts because there is only",coordList.size(),"element");
+			nLat = coordList.size();
+		}
+		
+		// sort it in longitude order - this is the sort order of the Coord class                   
+		Collections.sort(coordList);
+		log.info("Sorted long list:", coordList);
+		ArrayList<Integer> longSplitPoints = new ArrayList<Integer>(nLong);
+		for (int n = 0; n < nLong; n++) {
+			int pos = (int)((double)(n+1)/nLong*coordList.size())-1;
+			log.info("n",n,"nLong",nLong,"size",coordList.size(),"pos",pos);
+			longSplitPoints.add(coordList.get(pos).getLongitude());
+		}
+		log.info("Split long points:", longSplitPoints);
+
+		// sort in latitude order
+		Collections.sort(coordList, new LatitudeComparator());
+		log.info("Sorted lat list:", coordList);
+		ArrayList<Integer> latSplitPoints = new ArrayList<Integer>(nLat);
+		for (int n = 0; n < nLat; n++) {
+			int pos = (int)((double)(n+1)/nLat*coordList.size())-1;
+			log.info("n",n,"nLat",nLat,"size",coordList.size(),"pos",pos);
+			latSplitPoints.add(coordList.get(pos).getLatitude());
+		}
+		log.info("Split lat points:", latSplitPoints);
+
+		MapArea[] areas = new MapArea[nLat*nLong];
+		int iArea = 0;
+		int latStart = minLat;
+		for (Integer lat : latSplitPoints) {
+			int longStart = minLon;
+			for (Integer lon : longSplitPoints) {
+				areas[iArea] = new MapArea(new Area(latStart,
+						longStart, lat, lon), resolution);
+				iArea++;
+				longStart = lon;
+			}
+			latStart = lat;
+		}
+		
+		// Now sprinkle each map element into the correct map area.
+		for (MapPoint p : this.points) {
+			int pLat = Collections.binarySearch(latSplitPoints, p.getLocation().getLatitude());
+			int pLong = Collections.binarySearch(longSplitPoints, p.getLocation().getLongitude());
+			if (pLat < 0) {
+				pLat = (pLat+1)*-1;
+			}
+			if (pLong < 0) {
+				pLong = (pLong+1)*-1;
+			}
+			areas[pLat*nLong+pLong].addPoint(p);
+		}
+
+		for (MapLine l : this.lines) {
+			// Drop any zero sized lines.
+			if (l.getBounds().getMaxDimension() <= 0)
+				continue;
+			int pLat = Collections.binarySearch(latSplitPoints, l.getLocation().getLatitude());
+			int pLong = Collections.binarySearch(longSplitPoints, l.getLocation().getLongitude());
+			if (pLat < 0) {
+				pLat = (pLat+1)*-1;
+			}
+			if (pLong < 0) {
+				pLong = (pLong+1)*-1;
+			}
+			areas[pLat*nLong+pLong].addLine(l);
+		}
+
+		for (MapShape e : this.shapes) {
+			int pLat = Collections.binarySearch(latSplitPoints, e.getLocation().getLatitude());
+			int pLong = Collections.binarySearch(longSplitPoints, e.getLocation().getLongitude());
+			if (pLat < 0) {
+				pLat = (pLat+1)*-1;
+			}
+			if (pLong < 0) {
+				pLong = (pLong+1)*-1;
+			}
+			areas[pLat*nLong+pLong].addShape(e);
+		}
+		
+		return areas;
+	}
+	
 	/**
 	 * Split this area into several pieces. All the map elements are reallocated
 	 * to the appropriate subarea.  Usually this instance would now be thrown
 	 * away and the new sub areas used instead.
 	 *
-	 * @param nx The number of pieces in the x (longitude) direction.
-	 * @param ny The number of pieces in the y direction.
+	 * @param nLong The number of pieces in the x (longitude) direction.
+	 * @param nLat The number of pieces in the y direction.
 	 * @param resolution The resolution of the level.
 	 * @return An array of the new MapArea's.
 	 */
-	public MapArea[] split(int nx, int ny, int resolution) {
-		Area[] areas = bounds.split(nx, ny);
-		MapArea[] mapAreas = new MapArea[nx * ny];
-		log.info("Splitting area " + bounds + " into " + nx + "x" + ny + " pieces at resolution " + resolution);
-		for (int i = 0; i < nx * ny; i++) {
+	public MapArea[] split(int nLong, int nLat, int resolution) {
+		Area[] areas = bounds.split(nLong, nLat);
+		MapArea[] mapAreas = new MapArea[nLong * nLat];
+		log.info("Splitting area", bounds, "into",nLong + "x" + nLat , "pieces at resolution", resolution);
+		for (int i = 0; i < nLong * nLat; i++) {
 			mapAreas[i] = new MapArea(areas[i], resolution);
 			if (log.isDebugEnabled())
 				log.debug("area before", mapAreas[i].getBounds());
@@ -221,21 +351,21 @@
 
 		// Now sprinkle each map element into the correct map area.
 		for (MapPoint p : this.points) {
-			MapArea area1 = pickArea(mapAreas, p, xbase, ybase, nx, ny, dx, dy);
+			MapArea area1 = pickArea(mapAreas, p, xbase, ybase, nLong, nLat, dx, dy);
 			area1.addPoint(p);
 		}
 
 		for (MapLine l : this.lines) {
 			// Drop any zero sized lines.
-			if (l.getBounds().getMaxDimention() <= 0)
+			if (l.getBounds().getMaxDimension() <= 0)
 				continue;
 
-			MapArea area1 = pickArea(mapAreas, l, xbase, ybase, nx, ny, dx, dy);
+			MapArea area1 = pickArea(mapAreas, l, xbase, ybase, nLong, nLat, dx, dy);
 			area1.addLine(l);
 		}
 
 		for (MapShape e : this.shapes) {
-			MapArea area1 = pickArea(mapAreas, e, xbase, ybase, nx, ny, dx, dy);
+			MapArea area1 = pickArea(mapAreas, e, xbase, ybase, nLong, nLat, dx, dy);
 			area1.addShape(e);
 		}
 
@@ -469,6 +599,9 @@
 		addSize(l, l.hasExtendedType()? XT_LINE_KIND : LINE_KIND);
 	}
 
+	
+	private final Coord xCoord = new Coord(2714271,1029713);
+	private int xs = 0;
 	/**
 	 * Add a single shape to this map area.
 	 *
@@ -478,6 +611,12 @@
 		shapes.add(s);
 		addToBounds(s.getBounds());
 		addSize(s, s.hasExtendedType()? XT_SHAPE_KIND : SHAPE_KIND);
+		
+		if (s.getLocation().equals(xCoord)) {
+		log.error("Shape "+s);
+		GpxCreator.createGpx(GpxCreator.getGpxBaseName()+(++xs), s.getPoints());
+		}
+
 	}
 
 	/**
Index: src/uk/me/parabola/mkgmap/build/MapSplitter.java
===================================================================
--- src/uk/me/parabola/mkgmap/build/MapSplitter.java	(revision 1805)
+++ src/uk/me/parabola/mkgmap/build/MapSplitter.java	(working copy)
@@ -23,6 +23,7 @@
 import uk.me.parabola.imgfmt.app.trergn.Zoom;
 import uk.me.parabola.log.Logger;
 import uk.me.parabola.mkgmap.general.MapDataSource;
+import uk.me.parabola.util.GpxCreator;
 
 /**
  * The map must be split into subdivisions.  To do this we start off with
@@ -104,6 +105,8 @@
 		return alist.toArray(results);
 	}
 
+	private static int mi = 0;
+	
 	/**
 	 * Adds map areas to a list.  If an area has too many features, then it
 	 * is split into 4 and this routine is called recursively to add the new
@@ -119,13 +122,18 @@
 			Area bounds = area.getBounds();
 			int[] sizes = area.getEstimatedSizes();
 			if(log.isInfoEnabled()) {
-				String padding = depth + "                                            ";
+				String padding = depth + "                                                  ";
 				log.info(padding.substring(0, (depth + 1) * 2) + 
 						 bounds.getWidth() + "x" + bounds.getHeight() +
 						 ", res = " + res +
 						 ", points = " + area.getNumPoints() + "/" + sizes[MapArea.POINT_KIND] +
 						 ", lines = " + area.getNumLines() + "/" + sizes[MapArea.LINE_KIND] +
-						 ", shapes = " + area.getNumShapes() + "/" + sizes[MapArea.SHAPE_KIND]);
+						 ", shapes = " + area.getNumShapes() + "/" + sizes[MapArea.SHAPE_KIND]+
+						 ", xtpoint = " + sizes[MapArea.XT_POINT_KIND]+
+						 ", xtlines = " + sizes[MapArea.XT_LINE_KIND]+
+						 ", xtshapes = " + sizes[MapArea.XT_SHAPE_KIND]
+						 
+				);
 			}
 
 			if (area.getNumLines() > MAX_NUM_LINES ||
@@ -136,22 +144,24 @@
 				sizes[MapArea.XT_POINT_KIND] > MAX_XT_POINTS_SIZE ||
 				sizes[MapArea.XT_LINE_KIND] > MAX_XT_LINES_SIZE ||
 				sizes[MapArea.XT_SHAPE_KIND] > MAX_XT_SHAPES_SIZE) {
-				if (area.getBounds().getMaxDimention() > 10) {
+				if (area.getBounds().getMaxDimension() > 10) {
 					if (log.isDebugEnabled())
 						log.debug("splitting area", area);
 					MapArea[] sublist;
-					if(bounds.getWidth() > bounds.getHeight())
-						sublist = area.split(2, 1, res);
-					else
-						sublist = area.split(1, 2, res);
+//					if(bounds.getWidth() > bounds.getHeight())
+//						sublist = area.splitDynamically(2, 1, res);
+//					else
+						sublist = area.splitDynamically(2, 2, res);
 					addAreasToList(sublist, alist, depth + 1);
 					continue;
 				} else {
+					String base = GpxCreator.getGpxBaseName();
+					GpxCreator.createAreaGpx(base+(++mi), area.getBounds());
 					log.error("Area too small to split at " + area.getBounds().getCenter().toOSMURL() + " (reduce the density of points, length of lines, etc.)");
 				}
 			}
 
-			log.debug("adding area unsplit", ",has points" + area.hasPoints());
+			log.debug("adding area unsplit, has points", area.hasPoints());
 
 			MapArea[] sublist = area.split(1, 1, res);
 			assert sublist.length == 1: sublist.length;
Index: src/uk/me/parabola/imgfmt/app/Area.java
===================================================================
--- src/uk/me/parabola/imgfmt/app/Area.java	(revision 1809)
+++ src/uk/me/parabola/imgfmt/app/Area.java	(working copy)
@@ -148,7 +148,7 @@
 	 *
 	 * @return The largest dimension in map units.
 	 */
-	public int getMaxDimention() {
+	public int getMaxDimension() {
 		return Math.max(getWidth(), getHeight());
 	}
 
