I have a working solution for ensuring that the created point is placed
within the polygon when using --add-pois-to-areas, based on drawing the
polygon on to a small monochrome bitmap and then looking for the point that
is furthest from the surrounding area. I used a 9x9 bitmap for polygons
having a small number of points and 15x15 for longer polygons. There is
however a performance penalty. My standard map takes about 1 hour 20
minutes; using this algorithm the time increases by about 50% to about 2
hours. I am not currently able to commit changes to SVN (perhaps someone can
help out with that) but I have attached the code changes. I suggest that due
to the performance penalty, if we adopt this, then the --add-pois-to-areas
option be extended to be --add-pois-to-areas[=centre|optimised] or something
similar, with the default being centre and functioning as now and the
optimised option invoking the new code. Please try out the suggested change.
Note I don't expect this to work properly where a polygon is formed from a
multiploygon relation, but the code could quite easily be adapted for this
circumstance.


Regards,
Mike
Additions to Way.java:

import java.awt.image.BufferedImage;

        /*
         * 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;
        }


In function addPOItoPolygon in POIGeneratorHook.java, replace:

                if (poiCoord == null) {
                        // did not find any label coord
                        // use the common center point of the area
                        poiCoord = polygon.getCofG();
                }

with:

                if (poiCoord == null) {
                        // did not find any label coord
                        // find a suitable point within the area
                        poiCoord = polygon.getPointWithinArea();
                }


        
_______________________________________________
mkgmap-dev mailing list
[email protected]
http://www.mkgmap.org.uk/mailman/listinfo/mkgmap-dev

Reply via email to