Index: src/uk/me/parabola/mkgmap/general/NonRectangularClipper.java
===================================================================
--- src/uk/me/parabola/mkgmap/general/NonRectangularClipper.java	(revision 0)
+++ src/uk/me/parabola/mkgmap/general/NonRectangularClipper.java	(working copy)
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2008 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: 08-Nov-2008
+ */
+package uk.me.parabola.mkgmap.general;
+
+import java.util.List;
+
+import uk.me.parabola.imgfmt.app.Coord;
+
+/**
+ */
+public class NonRectangularClipper implements Clipper {
+	// Globals which should be set before calling these functions:
+	int polyCorners; // how many corners the polygon has
+	double[] polyX; // horizontal coordinates of corners
+	double[] polyY; // vertical coordinates of corners
+
+	//
+	// The following global arrays should be allocated before calling these
+	// functions:
+	//
+	private double[] constant;
+	private double[] multiple;
+
+	//
+	// (Globals are used in this example for purposes of speed. Change as
+	// desired.)
+	//
+	// USAGE:
+	// Call precalc_values() to initialize the constant[] and multiple[] arrays,
+	// then call pointInPolygon(x, y) to determine if the point is in the
+	// polygon.
+	//
+	// The function will return true if the point x,y is inside the polygon, or
+	// NO if it is not. If the point is exactly on the edge of the polygon,
+	// then the function may return YES or NO.
+	//
+	// Note that division by zero is avoided because the division is protected
+	// by the "if" clause which surrounds it.
+
+	 private void init() {
+
+		int i, j = polyCorners - 1;
+
+		for (i = 0; i < polyCorners; i++) {
+			if (polyY[j] == polyY[i]) {
+				constant[i] = polyX[i];
+				multiple[i] = 0;
+			} else {
+				constant[i] = polyX[i] - (polyY[i] * polyX[j])
+						/ (polyY[j] - polyY[i]) + (polyY[i] * polyX[i])
+						/ (polyY[j] - polyY[i]);
+				multiple[i] = (polyX[j] - polyX[i]) / (polyY[j] - polyY[i]);
+			}
+			j = i;
+		}
+	}
+
+	public NonRectangularClipper(List<Coord> polygon) {
+		this.polyCorners = polygon.size();
+		this.polyX = new double[polyCorners];
+		this.polyY = new double[polyCorners];
+		this.constant = new double[polyCorners];
+		this.multiple = new double[polyCorners];
+		for (int i = 0; i < polyCorners; i++){
+			Coord co = polygon.get(i);
+			polyX[i] = co.getHighPrecLon();
+			polyY[i] = co.getHighPrecLat();
+		}
+		init();
+	}
+
+	public void clipLine(MapLine line, LineAdder collector) {
+		//TODO
+	}
+
+	public void clipShape(MapShape shape, MapCollector collector) {
+		//TODO
+	}
+
+	public boolean contains(Coord location) {
+		double x = location.getHighPrecLon();
+		double y = location.getHighPrecLat();
+		int j = polyCorners - 1;
+		boolean oddNodes = false;
+
+		for (int i = 0; i < polyCorners; i++) {
+			if ((polyY[i] < y && polyY[j] >= y || polyY[j] < y && polyY[i] >= y)) {
+				oddNodes ^= (y * multiple[i] + constant[i] < x);
+			}
+			j = i;
+		}
+
+		return oddNodes;
+	}
+}
Index: src/uk/me/parabola/mkgmap/reader/osm/POIGeneratorHook.java
===================================================================
--- src/uk/me/parabola/mkgmap/reader/osm/POIGeneratorHook.java	(revision 3394)
+++ src/uk/me/parabola/mkgmap/reader/osm/POIGeneratorHook.java	(working copy)
@@ -15,6 +15,7 @@
 
 import java.util.AbstractMap;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.IdentityHashMap;
@@ -26,6 +27,7 @@
 import uk.me.parabola.imgfmt.app.Coord;
 import uk.me.parabola.log.Logger;
 import uk.me.parabola.util.EnhancedProperties;
+import uk.me.parabola.util.GpxCreator;
 
 /**
  * Adds a POI for each area and multipolygon with the same tags in case the add-pois-to-areas option
@@ -257,7 +259,19 @@
 		if (poiCoord == null) {
 			// did not find any label coord
 			// use the common center point of the area
+			long t1 = System.currentTimeMillis();
 			poiCoord = polygon.getCofG();
+			long dt1 = System.currentTimeMillis() - t1;
+			long t2 = System.currentTimeMillis();
+			Coord poiCoord2 = polygon.getPointWithinArea();
+			long dt2 = System.currentTimeMillis() - t2;
+			long t3 = System.currentTimeMillis();
+			Coord poiCoord3 = polygon.getCenterOfPolygon();
+			long dt3 = System.currentTimeMillis() - t3;
+			if (dt2 > 3 || dt3 > 3){
+//				System.out.println(dt1 + " " + dt2 + " " + dt3 + " " + polygon.getId() + " "  + polygon.getPoints().size());
+//				GpxCreator.createGpx("e:/ld/test"+polygon.getId(), polygon.getPoints(), Arrays.asList(poiCoord, poiCoord2, poiCoord3));
+			}
 		}
 		
 		Node poi = createPOI(polygon, poiCoord, AREA2POI_TAG); 
Index: src/uk/me/parabola/mkgmap/reader/osm/Way.java
===================================================================
--- src/uk/me/parabola/mkgmap/reader/osm/Way.java	(revision 3394)
+++ src/uk/me/parabola/mkgmap/reader/osm/Way.java	(working copy)
@@ -17,13 +17,16 @@
 package uk.me.parabola.mkgmap.reader.osm;
 
 import java.awt.*;
+import java.awt.image.BufferedImage;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Random;
 
 import uk.me.parabola.imgfmt.Utils;
 import uk.me.parabola.imgfmt.app.Coord;
 import uk.me.parabola.log.Logger;
+import uk.me.parabola.mkgmap.general.NonRectangularClipper;
 
 /**
  * Represent a OSM way in the 0.5 api.  A way consists of an ordered list of
@@ -249,4 +252,260 @@
 	public void setViaWay(boolean isViaWay) {
 		this.isViaWay = isViaWay;
 	}
+	
+	/*
+	 * Find a point that is farthest from the edge of the shape
+	 * by drawing the shape onto a small monochrome bitmap and
+	 * then finding the point that is farthest from a black pixel.
+	 * The shape is scaled differently in X and Y to fit a square bitmap.
+	 * The size of the bitmap depends on the number of points in the shape.
+	 */
+	public Coord getPointWithinArea() {
+		// if shape is not closed or must be convex, use getCofG
+		if ((points.size() < 5) || !isClosed())
+			return getCofG();
+		
+		// first get the coordinates of the rectangle containing the way
+		int minLat = Integer.MAX_VALUE;
+		int minLon = Integer.MAX_VALUE;
+		int maxLat = Integer.MIN_VALUE;
+		int maxLon = Integer.MIN_VALUE;
+		for(Coord p : points) {
+			int lat = p.getHighPrecLat();
+			int lon = p.getHighPrecLon();
+			minLat = Math.min(minLat, lat);
+			minLon = Math.min(minLon, lon);
+			maxLat = Math.max(maxLat, lat);
+			maxLon = Math.max(maxLon, lon);
+		}
+		if ((maxLon == minLon) || (maxLat == minLat))
+			return Coord.makeHighPrecCoord((maxLat + minLat) / 2, (maxLon + minLon) / 2);
+
+		// choose a bitmap resolution based on the number of points
+		int bitmapSize = points.size() < 10 ? 9 : 15;
+		int halfBitmapSize = bitmapSize / 2;
+		int halfBitmapSize2 = bitmapSize - halfBitmapSize;
+		// create a polygon scaled to fit the resolution
+		double xScale = (double)(bitmapSize - 1) / (double)(maxLon - minLon);
+		double yScale = (double)(bitmapSize - 1) / (double)(maxLat - minLat);
+		Polygon polygon = new Polygon();
+		for(Coord p : points)
+			polygon.addPoint((int)((p.getHighPrecLon() - minLon) * xScale), (int)((p.getHighPrecLat() - minLat) * yScale));
+		// draw the polygon as a white shape on a black background
+		BufferedImage image = new BufferedImage (bitmapSize, bitmapSize, BufferedImage.TYPE_BYTE_BINARY);
+		Graphics2D graphics = image.createGraphics();
+		graphics.setColor(Color.black);
+		graphics.fillRect(0, 0, bitmapSize, bitmapSize);
+		graphics.setColor(Color.white);
+		graphics.fillPolygon(polygon);
+		// set the default coordinate to the middle of the bitmap
+		int bestX = halfBitmapSize;
+		int bestY = halfBitmapSize;
+		// examine each point in the bitmap, to see whether it is farthest from a black pixel
+		// start at the centre point and move outwards in concentric squares
+		// we can stop looking when we are closer to the edge than the biggest distance
+		int biggestSquaredDistanceToBlack = getSquaredDistanceToBlack(image, bestX, bestY);
+		for (int start = 1; (start <= halfBitmapSize) && (biggestSquaredDistanceToBlack < (halfBitmapSize2 - start) * (halfBitmapSize2 - start)); start++) {
+			for (int i = 0; i <= start; i++) {
+				int x = halfBitmapSize + i;
+				int y = halfBitmapSize + start;
+				int squaredDistanceToBlack = getSquaredDistanceToBlack(image, x, y);
+				if (biggestSquaredDistanceToBlack < squaredDistanceToBlack) {
+					biggestSquaredDistanceToBlack = squaredDistanceToBlack;
+					bestX = x;
+					bestY = y;
+				}
+				x = halfBitmapSize - i;
+				y = halfBitmapSize - start;
+				squaredDistanceToBlack = getSquaredDistanceToBlack(image, x, y);
+				if (biggestSquaredDistanceToBlack < squaredDistanceToBlack) {
+					biggestSquaredDistanceToBlack = squaredDistanceToBlack;
+					bestX = x;
+					bestY = y;
+				}
+				x = halfBitmapSize + start;
+				y = halfBitmapSize - i;
+				squaredDistanceToBlack = getSquaredDistanceToBlack(image, x, y);
+				if (biggestSquaredDistanceToBlack < squaredDistanceToBlack) {
+					biggestSquaredDistanceToBlack = squaredDistanceToBlack;
+					bestX = x;
+					bestY = y;
+				}
+				x = halfBitmapSize - start;
+				y = halfBitmapSize + i;
+				squaredDistanceToBlack = getSquaredDistanceToBlack(image, x, y);
+				if (biggestSquaredDistanceToBlack < squaredDistanceToBlack) {
+					biggestSquaredDistanceToBlack = squaredDistanceToBlack;
+					bestX = x;
+					bestY = y;
+				}
+				if ( i > 0) {
+					x = halfBitmapSize - i;
+					y = halfBitmapSize + start;
+					squaredDistanceToBlack = getSquaredDistanceToBlack(image, x, y);
+					if (biggestSquaredDistanceToBlack < squaredDistanceToBlack) {
+						biggestSquaredDistanceToBlack = squaredDistanceToBlack;
+						bestX = x;
+						bestY = y;
+					}
+					x = halfBitmapSize + i;
+					y = halfBitmapSize - start;
+					squaredDistanceToBlack = getSquaredDistanceToBlack(image, x, y);
+					if (biggestSquaredDistanceToBlack < squaredDistanceToBlack) {
+						biggestSquaredDistanceToBlack = squaredDistanceToBlack;
+						bestX = x;
+						bestY = y;
+					}
+					x = halfBitmapSize + start;
+					y = halfBitmapSize + i;
+					squaredDistanceToBlack = getSquaredDistanceToBlack(image, x, y);
+					if (biggestSquaredDistanceToBlack < squaredDistanceToBlack) {
+						biggestSquaredDistanceToBlack = squaredDistanceToBlack;
+						bestX = x;
+						bestY = y;
+					}
+					x = halfBitmapSize - start;
+					y = halfBitmapSize - i;
+					squaredDistanceToBlack = getSquaredDistanceToBlack(image, x, y);
+					if (biggestSquaredDistanceToBlack < squaredDistanceToBlack) {
+						biggestSquaredDistanceToBlack = squaredDistanceToBlack;
+						bestX = x;
+						bestY = y;
+					}
+				}
+			}
+		}
+		return Coord.makeHighPrecCoord(minLat + (int)Math.round((bestY + 0.5) / yScale), minLon + (int)Math.round((bestX + 0.5) / xScale));
+	}
+	
+	private int getSquaredDistanceToBlack(BufferedImage image, int x, int y)
+	{
+		if ((image.getRGB(x, y) & 0xffffff) == 0)
+			return 0;
+
+		int bitmapSize = image.getWidth();
+		int halfBitmapSize = bitmapSize / 2;
+		for (int delta = 1; delta <= halfBitmapSize; delta++) {
+			if ((x < delta) ||
+				(x + delta >= bitmapSize) ||
+				(y < delta) ||
+				(y + delta >= bitmapSize) ||
+				((image.getRGB(x + delta, y) & 0xffffff) == 0) ||
+				((image.getRGB(x - delta, y) & 0xffffff) == 0) ||
+				((image.getRGB(x, y + delta) & 0xffffff) == 0) ||
+				((image.getRGB(x, y - delta) & 0xffffff) == 0))
+				return delta * delta;
+			
+			for (int delta2 = 1; delta2 < delta; delta2++) {
+				if (((image.getRGB(x + delta, y + delta2) & 0xffffff) == 0) ||
+					((image.getRGB(x - delta, y + delta2) & 0xffffff) == 0) ||
+					((image.getRGB(x + delta2, y + delta) & 0xffffff) == 0) ||
+					((image.getRGB(x + delta2, y - delta) & 0xffffff) == 0) ||
+					((image.getRGB(x + delta, y - delta2) & 0xffffff) == 0) ||
+					((image.getRGB(x - delta, y - delta2) & 0xffffff) == 0) ||
+					((image.getRGB(x - delta2, y + delta) & 0xffffff) == 0) ||
+					((image.getRGB(x - delta2, y - delta) & 0xffffff) == 0))
+					return delta * delta + delta2 * delta2;							
+			}
+			if (((image.getRGB(x + delta, y + delta) & 0xffffff) == 0) ||
+				((image.getRGB(x - delta, y + delta) & 0xffffff) == 0) ||
+				((image.getRGB(x + delta, y - delta) & 0xffffff) == 0) ||
+				((image.getRGB(x - delta, y - delta) & 0xffffff) == 0))
+				return 2 * delta * delta;			
+		}
+		return 2 * halfBitmapSize * halfBitmapSize;
+	}
+
+	/**
+	 * Calculate a point that is within a polygon described
+	 * by the points. The point is likely to be near the center
+	 * of the largest inner circle. 
+	 * @return
+	 */
+	public Coord getCenterOfPolygon(){
+		if ((points.size() < 5) || !hasEqualEndPoints())
+			return getCofG();
+		Coord center = null;
+		final int k = 15;
+		double maximin_distance = 0;
+		double accuracy = Double.MAX_VALUE;
+		// stop if result is not be more than 10 map units from perfect one 
+		
+		int minLon30 = Integer.MAX_VALUE;
+		int maxLon30 = Integer.MIN_VALUE;
+		int minLat30 = Integer.MAX_VALUE;
+		int maxLat30 = Integer.MIN_VALUE;
+		for (Coord co: points){
+			int lon30 = co.getHighPrecLon();
+			if (minLon30 > lon30)
+				minLon30 = lon30;
+			if (maxLon30 < lon30)
+				maxLon30 = lon30;
+			int lat30 = co.getHighPrecLat();
+			if (minLat30 > lat30)
+				minLat30 = lat30;
+			if (maxLat30 < lat30)
+				maxLat30 = lat30;
+		}
+		
+		final double minimum_accuracy = 2 << Coord.DELTA_SHIFT; // TODO Find useful value 
+		Random random = new Random(101);
+		ArrayList<Coord> tested = new ArrayList<>();
+		ArrayList<Coord> improved = new ArrayList<>();
+		int loop = 0;
+		NonRectangularClipper test = new NonRectangularClipper(points);
+		long t1 = System.currentTimeMillis();
+		while (accuracy > minimum_accuracy){
+			// begin loop through nodes
+			int consecutive_misses = 0;
+			boolean foundBetter = false;
+			while (consecutive_misses < k){
+				// select coordinates at random within bounds
+				int lon30 = minLon30 + random.nextInt(maxLon30 - minLon30);
+				int lat30 = minLat30 + random.nextInt(maxLat30 - minLat30);
+				Coord node = Coord.makeHighPrecCoord(lat30, lon30);
+				if (test.contains(node) == false)
+					continue;
+				tested.add(node);
+				double smallest_distance = Double.MAX_VALUE;
+				// loop through edges and find shortest distance
+				for (int i = 0; i+1 < points.size(); i++){
+					double dist = node.shortestDistToLineSegment(points.get(i), points.get(i+1));
+					if (dist < smallest_distance){
+						smallest_distance = dist;
+					}
+				}
+				// maximize the minimum distance through iterations
+				// and keep track of the consecutive number of times
+				// that a smallest distance has not been found
+				if (smallest_distance > maximin_distance){
+					maximin_distance = smallest_distance;
+					center = node;
+					improved.add(center);
+					foundBetter = true;
+					consecutive_misses = 0;
+				} else {
+					++consecutive_misses;
+				}
+			}
+//			GpxCreator.createGpx("e:/ld/ivl_t_"+ loop, points, tested);
+//			if (foundBetter)
+//				GpxCreator.createGpx("e:/ld/ivl_i_"+ loop, points, improved);
+			tested.clear();
+			loop++;
+			// calculate current level of accuracy based on the
+			// smallest distance between the upper and lower bound
+
+			accuracy = Math.min(maxLon30 - minLon30, maxLat30 - minLat30);
+			// update the bounds of the region
+			final double SQRT_2_2 = Math.sqrt(2.0) * 2;
+			int ivl_x = (int)Math.round((double)(maxLon30 - minLon30) / SQRT_2_2);
+			minLon30 = center.getHighPrecLon() - ivl_x;  
+			maxLon30 = center.getHighPrecLon() + ivl_x;
+			int ivl_y = (int)Math.round((double)(maxLat30 - minLat30) / SQRT_2_2);
+			minLat30 = center.getHighPrecLat() - ivl_y;  
+			maxLat30 = center.getHighPrecLat() + ivl_y;
+		}
+		return center;		
+	} 	
 }
