Index: src/uk/me/parabola/splitter/EnhancedDensityMap.java
===================================================================
--- src/uk/me/parabola/splitter/EnhancedDensityMap.java	(revision 428)
+++ src/uk/me/parabola/splitter/EnhancedDensityMap.java	(working copy)
@@ -13,6 +13,7 @@
 package uk.me.parabola.splitter;
 
 import java.awt.Rectangle;
+import java.util.BitSet;
 
 /**
  * Contains info that is needed by the {@link Tile} class. For a given
@@ -26,13 +27,17 @@
 	private final DensityMap densityMap;
 	private int[][] xyMap;
 	private int[][] yxMap;
+	private BitSet xyInPolygon;
 	private double[] aspectRatioFactor;
 	private int minAspectRatioFactorPos;
 	private int maxNodesInDensityMapGridElement = Integer.MIN_VALUE;
+	private int maxNodesInDensityMapGridElementInPoly = Integer.MIN_VALUE;
+	private java.awt.geom.Area polygonArea;
 
 	public EnhancedDensityMap(DensityMap densities, java.awt.geom.Area polygonArea) {
 		this.densityMap = densities;
-		prepare(polygonArea);
+		this.polygonArea = polygonArea;
+		prepare();
 	}
 
 	
@@ -40,12 +45,8 @@
 	 * If a polygon is given, filter the density data Compute once complex
 	 * trigonometric results for needed for proper aspect ratio calculations.
 	 * 
-	 * @param polygonArea
-	 *            if not null, this is used to filter the data in the density
-	 *            map so that grid elements outside of the polygon are 
-	 *            reduced
 	 */
-	private void prepare(java.awt.geom.Area polygonArea){
+	private void prepare(){
 		// performance: calculate only once the needed complex math results
 		aspectRatioFactor = new double[densityMap.getHeight()+1]; 
 		int minLat = densityMap.getBounds().getMinLat(); 
@@ -69,6 +70,8 @@
 		int width = densityMap.getWidth();
 		int height = densityMap.getHeight();
 		xyMap = new int [width][height];
+		if (polygonArea != null)
+			xyInPolygon = new BitSet(width * height);
 		int shift = densityMap.getShift();
 		for (int x = 0; x < width; x++){
 			int polyXPos = densityMap.getBounds().getMinLong() +  (x << shift);
@@ -77,14 +80,17 @@
 				int count = densityMap.getNodeCount(x, y);
 				if (polygonArea != null){
 					int polyYPos = densityMap.getBounds().getMinLat() + (y << shift);
-					if (polygonArea.intersects(polyXPos, polyYPos, 1<<shift, 1<<shift))
-						count = Math.max(1, count);
-					else 
-						count = 0;
+					if (polygonArea.intersects(polyXPos, polyYPos, 1<<shift, 1<<shift)){
+						xyInPolygon.set(x * height + y);
+						if (count > maxNodesInDensityMapGridElementInPoly){
+							maxNodesInDensityMapGridElementInPoly = count;
+						}
+					}
 				}
 				if (count > 0){
 					if (count > maxNodesInDensityMapGridElement)
 						maxNodesInDensityMapGridElement = count;
+
 					xCol[y] = count;
 				}
 			}
@@ -99,6 +105,12 @@
 		}
 	}
 
+	public boolean isGridElemInPolygon (int x, int y){
+		if (polygonArea == null)
+			return true;
+		return xyInPolygon.get(x* densityMap.getHeight() + y);
+	}
+	
 	// calculate aspect ratio of a tile which is a view on the densityMap
 	public double getAspectRatio(Rectangle r) {
 		double ratio;
@@ -140,4 +152,13 @@
 	public int getMaxNodesInDensityMapGridElement() {
 		return maxNodesInDensityMapGridElement;
 	}
+
+	public int getMaxNodesInDensityMapGridElementInPoly() {
+		return maxNodesInDensityMapGridElementInPoly;
+	}
+
+	public java.awt.geom.Area getPolygonArea() {
+		return polygonArea;
+	}
+	
 }
Index: src/uk/me/parabola/splitter/SplittableDensityArea.java
===================================================================
--- src/uk/me/parabola/splitter/SplittableDensityArea.java	(revision 428)
+++ src/uk/me/parabola/splitter/SplittableDensityArea.java	(working copy)
@@ -46,6 +46,7 @@
 	private long minNodes;
 	private final int startSearchLimit;
 	private int searchLimit;
+	private double maxOutsidePolygonRatio = 0.5; // TODO: maybe reduce it when a good solution was found
 
 	private final DensityMap allDensities;
 	private EnhancedDensityMap extraDensityInfo;
@@ -330,9 +331,13 @@
 	 */
 	private void prepare(java.awt.geom.Area polygonArea){
 		extraDensityInfo = new EnhancedDensityMap(allDensities, polygonArea);
-		if (!beQuiet)
+		if (!beQuiet){
 			System.out.println("Highest node count in a single grid element is "
-							+ Utils.format(extraDensityInfo.getMaxNodesInDensityMapGridElement()));
+					+ Utils.format(extraDensityInfo.getMaxNodesInDensityMapGridElement()));
+			if (polygonArea != null)
+			System.out.println("Highest node count in a single grid element within the bounding polygon is "
+					+ Utils.format(extraDensityInfo.getMaxNodesInDensityMapGridElementInPoly()));
+		}
 		if (polygonArea != null)
 			trimTiles = true;
 
@@ -611,7 +616,14 @@
 		} else if (tile.width < 2 && tile.height < 2) {
 			return null;
 		} 
+		if (tile.outsidePolygon()){
+			return new Solution(maxNodes);
+		}
 		if (addAndReturn){
+			double outsidePolygonRatio = tile.calcOutsidePolygonRatio();
+			if (outsidePolygonRatio > maxOutsidePolygonRatio ){
+				return null;
+			}
 			Solution solution = new Solution(maxNodes);
 			solution.add(tile);  // can't split further
 			return solution;
@@ -678,6 +690,10 @@
 				continue;
 
 			Tile[] parts = smi.getParts();
+			if (trimTiles){
+				parts[0] = parts[0].trim();
+				parts[1] = parts[1].trim();
+			}
 			if (parts[0].count > parts[1].count){
 				// first try the less populated part
 				Tile help = parts[0];
@@ -684,10 +700,6 @@
 				parts[0] = parts[1];
 				parts[1] = help;
 			}
-			if (trimTiles){
-				parts[0] = parts[0].trim();
-				parts[1] = parts[1].trim();
-			}
 			Solution [] sols = new Solution[2];
 			int countOK = 0;
 			for (int i = 0; i < 2; i++){
Index: src/uk/me/parabola/splitter/Tile.java
===================================================================
--- src/uk/me/parabola/splitter/Tile.java	(revision 428)
+++ src/uk/me/parabola/splitter/Tile.java	(working copy)
@@ -468,40 +468,142 @@
 		 * @return the trimmed version of the tile.
 		 */
 		public Tile trim() {
+			long sumRemovedColCounts = 0;
+			long sumRemovedRowCounts = 0;
 			int minX = -1;
 			for (int i = 0; i < width; i++) {
-				if (getColSum(i) > 0){
+				long colSum = getColSum(i) ; 
+				boolean needed = (densityInfo.getPolygonArea() == null) ? colSum > 0 : (colOutsidePolygon(i) == false);
+				if (needed){
 					minX = x + i;
 					break;
 				}
+				sumRemovedColCounts += colSum;
 			}
 			int maxX = -1;
 			for (int i = width - 1; i >= 0; i--) {
-				if (getColSum(i) > 0){
+				long colSum = getColSum(i) ; 
+				boolean needed = (densityInfo.getPolygonArea() == null) ? colSum > 0 : (colOutsidePolygon(i) == false);
+				if (needed){
 					maxX = x + i;
 					break;
 				}
+				sumRemovedColCounts += colSum;
 			}
 			int minY = -1;
 			for (int i = 0; i < height; i++) {
-				if (getRowSum(i) > 0){
+				long rowSum = getRowSum(i);
+				boolean needed = (densityInfo.getPolygonArea() == null) ? rowSum > 0 : (rowOutsidePolygon(i) == false);
+				if (needed){
 					minY = y + i;
 					break;
 				}
+				sumRemovedRowCounts += rowSum;
 			}
 			int maxY = -1;
 			for (int i = height - 1; i >= 0; i--) {
-				if (getRowSum(i) > 0){
+				long rowSum = getRowSum(i);
+				boolean needed = (densityInfo.getPolygonArea() == null) ? rowSum > 0 : (rowOutsidePolygon(i) == false);
+				if (needed){
 					maxY = y + i;
 					break;
 				}
+				sumRemovedRowCounts += rowSum;
 			}
-
 			assert minX <= maxX;
 			assert minY <= maxY;
-			return new Tile(densityInfo, minX, minY, maxX - minX + 1, maxY - minY + 1, count);
+			assert maxX >= 0;
+			assert maxY >= 0;
+			long newCount = count;
+			int modWidth = maxX - minX + 1;
+			int modHeight = maxY - minY + 1;
+			if (densityInfo.getPolygonArea() != null){
+				if (modWidth != width || modHeight != height){
+					// tile was trimmed, try hard to avoid a new costly calculation of the count value
+					if (width == modWidth){
+						newCount = count - sumRemovedRowCounts; 
+					} else if (height == modHeight){
+						newCount = count - sumRemovedColCounts;
+					} else {
+//						System.out.printf("ouch: %d %d %d %d (%d) -> %d %d %d %d\n",x,y,width,height,count,minX,minY, maxX - minX + 1, maxY - minY + 1 );
+						return new Tile (densityInfo, new Rectangle(minX, minY, modWidth, modHeight));
+					}
+				}
+			}
+			return new Tile(densityInfo, minX, minY, modWidth, modHeight, newCount);
 		}
+
+		private boolean rowOutsidePolygon(int row) {
+			if (densityInfo.getPolygonArea() == null)
+				return false;
+			// performance critical part, check corners first
+			if (densityInfo.isGridElemInPolygon(x, y + row) || densityInfo.isGridElemInPolygon(x + width-1, y + row))
+				return false;
+			// check rest of row
+			for (int i = 1; i < width-1; i++) {
+				if (densityInfo.isGridElemInPolygon(x + i, y + row))
+					return false;
+			}
+			return true;
+		}
+
+		private boolean colOutsidePolygon(int col) {
+			if (densityInfo.getPolygonArea() == null)
+				return false;
+			// performance critical part, check corners first
+			if (densityInfo.isGridElemInPolygon(x + col, y) || densityInfo.isGridElemInPolygon(x + col, y + height - 1))
+				return false;
+			// check rest of column
+			for (int i = 1; i < height - 1; i++) {
+				if (densityInfo.isGridElemInPolygon(x + col, y + i))
+					return false;
+			}
+			return true;
+		}
+
+		public boolean outsidePolygon(){
+			java.awt.geom.Area polygonArea = densityInfo.getPolygonArea();
+			if (polygonArea == null)
+				return false;
+			if (polygonArea.intersects(getRealBBox()))
+				return false;
+			return true;
+		}
+
+		/**
+		 * Count the number of grid elements which are outside of the polygon area,
+		 * divide it by the total number of grid elements covered by this tile to
+		 * get a value between 0 and 1 (including).
+		 * @return
+		 */
+		public double calcOutsidePolygonRatio (){
+			if (densityInfo.getPolygonArea() == null)
+				return 0;
+			Rectangle realBBox = getRealBBox();
+//			if (densityInfo.getPolygonArea().contains(realBBox) )
+//				return 0;
+			// check special case: tile may contain the polygon
+			Rectangle polyBBox = densityInfo.getPolygonArea().getBounds();
+			if (realBBox.contains(polyBBox)){
+				return 0;
+			}
+			int countOutside = 0;
+			for (int i = x; i < x+width; i++){
+				for (int j = y; j < y+height; j++){
+					if (densityInfo.isGridElemInPolygon(i,j) == false)
+						countOutside++;
+				}
+			}
+			double ratio = (double) countOutside  / (width * height) ;
+			return ratio;
+		}
 		
+		public Rectangle getRealBBox(){
+			int shift = densityInfo.getDensityMap().getShift();
+			int polyYPos = densityInfo.getDensityMap().getBounds().getMinLat() + (y << shift);
+			int polyXPos = densityInfo.getDensityMap().getBounds().getMinLong() + (x << shift);
+			return new Rectangle(polyXPos, polyYPos, width<<shift, height<<shift);
+		}
 		
 		@Override
 		public String toString(){
