Index: src/uk/me/parabola/mkgmap/reader/osm/boundary/BoundaryPreparer.java
===================================================================
--- src/uk/me/parabola/mkgmap/reader/osm/boundary/BoundaryPreparer.java	(revision 2227)
+++ src/uk/me/parabola/mkgmap/reader/osm/boundary/BoundaryPreparer.java	(working copy)
@@ -109,20 +109,17 @@
 	 * @see java.lang.Thread#run()
 	 */
 	public void run() {
-		if (onePass == false && boundaryFilename != null) {
-		long t1 = System.currentTimeMillis();
-		boolean prepOK = createRawData();
-		long t2 = System.currentTimeMillis();
-		log.error("BoundaryPreparer pass 1 took " + (t2-t1) + " ms");
+			if (onePass == false && boundaryFilename != null) {
+				long t1 = System.currentTimeMillis();
+				boolean prepOK = createRawData();
+				long t2 = System.currentTimeMillis();
+				System.out.println("BoundaryPreparer pass 1 took " + (t2-t1) + " ms");
 
-		if (!prepOK){
-			return;
-		}
-		}
-		long t3 = System.currentTimeMillis();
-		workoutBoundaryRelations(inDir, outDir);
-		long t4 = System.currentTimeMillis();
-		log.error("BoundaryPreparer workoutBoundaryRelations() took " + (t4-t3) + " ms");
+				if (!prepOK){
+					return;
+				}
+			}
+			workoutBoundaryRelations(inDir, outDir);
 	}
 
 	private boolean createRawData(){
Index: src/uk/me/parabola/mkgmap/reader/osm/boundary/BoundarySaver.java
===================================================================
--- src/uk/me/parabola/mkgmap/reader/osm/boundary/BoundarySaver.java	(revision 2227)
+++ src/uk/me/parabola/mkgmap/reader/osm/boundary/BoundarySaver.java	(working copy)
@@ -15,6 +15,7 @@
 import java.awt.Rectangle;
 import java.awt.geom.Area;
 import java.awt.geom.PathIterator;
+import java.awt.geom.Rectangle2D;
 import java.io.BufferedOutputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.DataOutputStream;
@@ -193,38 +194,61 @@
 		log.debug("Remaining", openStreams.size(), "open streams.");
 	}
 
+	private Map<String, Area> splitArea(Area areaToSplit) {
+		return splitArea(areaToSplit, new HashMap<String, Area>());
+	}
+	
 	/**
-	 * Split a given area into the raster tiles
-	 * @param areaToSplit
-	 * @return
+	 * Split a given area into the raster tiles. 
+	 * @param areaToSplit the area (will be destroyed)
+	 * @param splits a map the splitted tiles are added to
+	 * @return the map with the splitted tiles
 	 */
-	private Map<String, Area> splitArea(Area areaToSplit) {
-		Map<String, Area> splittedAreas = new HashMap<String, Area>();
-		Rectangle areaBounds = areaToSplit.getBounds();
+	private Map<String, Area> splitArea(Area areaToSplit, Map<String, Area> splits) {
+		if (areaToSplit.isEmpty())
+			return splits;
+		
+		// use high precision bounds with later rounding to avoid some little rounding
+		// errors (49999.99999999 instead of 50000.0)
+		Rectangle2D areaBounds = areaToSplit.getBounds2D();
+		int sMinLong = BoundaryUtil.getSplitBegin((int)Math.round(areaBounds.getMinX()));
+		int sMinLat = BoundaryUtil.getSplitBegin((int)Math.round(areaBounds.getMinY()));
+		int sMaxLong = BoundaryUtil.getSplitEnd((int)Math.round(areaBounds.getMaxX()));
+		int sMaxLat = BoundaryUtil.getSplitEnd((int)Math.round(areaBounds.getMaxY()));
 
-		for (int latSplit = BoundaryUtil.getSplitBegin(areaBounds.y); latSplit <= BoundaryUtil
-				.getSplitBegin(areaBounds.y + areaBounds.height); latSplit += BoundaryUtil.RASTER) {
-			for (int lonSplit = BoundaryUtil.getSplitBegin(areaBounds.x); lonSplit <= BoundaryUtil
-					.getSplitBegin(areaBounds.x + areaBounds.width); lonSplit += BoundaryUtil.RASTER) {
-				Rectangle splitRect = new Rectangle(lonSplit, latSplit,
-						BoundaryUtil.RASTER, BoundaryUtil.RASTER);
-				Area tileCover;
-				// avoid costly intersect() call when area fits into split
-				// rectangle
-				if (splitRect.contains(areaToSplit.getBounds()))
-					tileCover = new Area(areaToSplit);
-				else {
-					tileCover = new Area(splitRect);
-					tileCover.intersect(areaToSplit);
-				}
-				if (tileCover.isEmpty() == false) {
-					splittedAreas.put(BoundaryUtil.getKey(latSplit, lonSplit),
-							tileCover);
-				}
+		int dLon = sMaxLong- sMinLong;
+		int dLat = sMaxLat - sMinLat;
+		if (dLon > BoundaryUtil.RASTER || dLat > BoundaryUtil.RASTER) {
+			// split into two halves
+			Area a1;
+			Area a2;
+			if (dLon > dLat) {
+				int midLon = BoundaryUtil.getSplitEnd(sMinLong+dLon/2);
+				a1 = new Area(new Rectangle(sMinLong, sMinLat, midLon-sMinLong, dLat));
+				a2 = new Area(new Rectangle(midLon, sMinLat, sMaxLong-midLon, dLat));
+			} else {
+				int midLat = BoundaryUtil.getSplitEnd(sMinLat+dLat/2);
+				a1 = new Area(new Rectangle(sMinLong, sMinLat, dLon, midLat-sMinLat));
+				a2 = new Area(new Rectangle(sMinLong, midLat, dLon, sMaxLat-midLat));
 			}
+
+			// intersect with the both halves
+			// and split both halves recursively
+
+			a1.intersect(areaToSplit);
+			splitArea(a1, splits);
+			// a1 is no longer needed => GC
+			a1 = null;
+			
+			a2.intersect(areaToSplit);
+			splitArea(a2, splits);
+			
+		} else {
+			// the area fully fits into one raster tile
+			splits.put(BoundaryUtil.getKey(sMinLat, sMinLong), areaToSplit);
 		}
+		return splits;
 
-		return splittedAreas;
 	}
 
 	private void openStream(StreamInfo streamInfo, boolean newFile) {
Index: src/uk/me/parabola/mkgmap/main/Main.java
===================================================================
--- src/uk/me/parabola/mkgmap/main/Main.java	(revision 2227)
+++ src/uk/me/parabola/mkgmap/main/Main.java	(working copy)
@@ -340,6 +340,7 @@
 		ExecutorCompletionService<Object> cmplService = new ExecutorCompletionService<Object>(threadPool);
 		
 		log.info("Start preparers");
+		long t1 = System.currentTimeMillis();
 		for (Preparer preparer : preparers) {
 
 			boolean usePreparer = preparer.init(args.getProperties(),
@@ -355,11 +356,14 @@
 				try {
 					cmplService.take();
 				} catch (InterruptedException exp) {
+					System.err.println(exp.getMessage());
 				}
 			} while (((ThreadPoolExecutor) threadPool).getActiveCount() > 0);
 		}
 
 		preparers.clear();
+		long t2 = System.currentTimeMillis();
+		System.out.println("prepares finished after " + (t2-t1) + " ms");
 
 		for (FilenameTask task : futures) {
 			threadPool.execute(task);
