logo separator

[mkgmap-dev] [PATCH] POI Address + Area POIs v7 R942

From Ben Konrath ben at bagu.org on Sun Mar 1 03:23:48 GMT 2009

 Hi Berni,

Thanks for picking up my patch and for the improvements! :-) I noticed
that it was just committed to the poi branch so I'll give it test and
report back when I have some free time on Monday.

Cheers, Ben

On Sat, Feb 28, 2009 at 2:39 PM, Bernhard Heibler <bernhard at heibler.de> wrote:
> Hi,
>
> I have integrated Ben Konrath's Area POI  patch into my code. I did the
> following changes to Ben's code:
>
> - Replaced the hard coded area to poi type mapping with the build in rule
> sets. Shapes are checked against the point rules to add missing pois.
> - Replaced the linear search (to avoid duplicated pois) with tile based
> search
> - Add  buildings to polygons style
> - Moved my helper classes to general directory
>
> You have to add the --add-pois-to-areas to enable the Area POIs generation.
> I'm not sure if we should generate a new rule set in the style folder for
> this purpose. It works pretty good using the point rules but it might be
> confusing.
>
> Thanks Ben for providing the ray cast code to check for points in polygons.
>
> Thanks
> Berni.
>
> Index: test/uk/me/parabola/mkgmap/general/PointInShapeTest.java
> ===================================================================
> --- test/uk/me/parabola/mkgmap/general/PointInShapeTest.java
>  (.../upstream/mkgmap)   (revision 0)
> +++ test/uk/me/parabola/mkgmap/general/PointInShapeTest.java
>  (.../work_poiaddr_area/mkgmap)  (revision 280)
> @@ -0,0 +1,289 @@
> +/**
> + *
> + */
> +package uk.me.parabola.mkgmap.general;
> +
> +
> +import static org.junit.Assert.assertFalse;
> +import static org.junit.Assert.assertTrue;
> +
> +import java.util.Arrays;
> +import java.util.List;
> +
> +import org.junit.Before;
> +import org.junit.Test;
> +
> +import uk.me.parabola.imgfmt.app.Coord;
> +
> +/**
> + * @author ben
> + *
> + */
> +public class PointInShapeTest {
> +
> +       MapShape square, triangle, line;
> +       int squareSize = 4;
> +
> +       /**
> +        * @throws java.lang.Exception
> +        */
> +       @Before
> +       public void setUp() throws Exception {
> +               // Square
> +               List<Coord> points = Arrays.asList(
> +                               new Coord(0, 0),
> +                               new Coord(0, squareSize),
> +                               new Coord(squareSize, squareSize),
> +                               new Coord(squareSize, 0),
> +                               new Coord(0,0)
> +                               );
> +               square = new MapShape();
> +               square.setPoints(points);
> +
> +               // Triangle
> +               points = Arrays.asList(
> +                               new Coord(0,0),
> +                               new Coord(4,4),
> +                               new Coord(8,0),
> +                               new Coord(0,0)
> +                               );
> +               triangle = new MapShape();
> +               triangle.setPoints(points);
> +
> +               // Line
> +               points = Arrays.asList(
> +                               new Coord(2,5),
> +                               new Coord(12,1)
> +                               );
> +               line = new MapShape();
> +               line.setPoints(points);
> +       }
> +
> +       @Test
> +       public void testLinePointsInsideSquare() {
> +
> +               // inside square, 1 unit from corners
> +               List<Coord> points = Arrays.asList(
> +                               new Coord(1, squareSize/2),
> +                               new Coord(squareSize/2, squareSize - 1),
> +                               new Coord(squareSize - 1, squareSize/2),
> +                               new Coord(squareSize/2, 1)
> +                               );
> +               for (Coord coord : points) {
> +                       assertTrue("point (" + coord.getLatitude() + ", " +
> coord.getLongitude() + ") should be inside square",
> +                                       square.contains(coord));
> +               }
> +
> +               // on the line
> +               points = Arrays.asList(
> +                               new Coord(0, squareSize/2),
> +                               new Coord(squareSize/2, squareSize),
> +                               new Coord(squareSize, squareSize/2),
> +                               new Coord(squareSize/2, 0)
> +                               );
> +               for (Coord coord : points) {
> +                       assertTrue("point (" + coord.getLatitude() + ", " +
> coord.getLongitude() + ") should be outside square",
> +                                       square.contains(coord));
> +               }
> +       }
> +
> +       @Test
> +       public void testLinePointsOutsideSquare() {
> +
> +               // outside square, 1 unit from line
> +               List<Coord> points = Arrays.asList(
> +                               new Coord(-1, squareSize/2),
> +                               new Coord(squareSize/2, squareSize + 1),
> +                               new Coord(squareSize + 1, squareSize/2),
> +                               new Coord(squareSize/2, -1)
> +                               );
> +               for (Coord coord : points) {
> +                       assertFalse("point (" + coord.getLatitude() + ", " +
> coord.getLongitude() + ") should be outside square",
> +                                       square.contains(coord));
> +               }
> +       }
> +
> +       @Test
> +       public void testCornerPointsInsideSquare() {
> +               // corner points
> +               for (Coord cornerpoint : square.getPoints()) {
> +                       Coord co = new Coord(cornerpoint.getLatitude(),
> cornerpoint.getLongitude());
> +                       assertTrue("corner point (" + co.getLatitude() + ",
> " + co.getLongitude() + ") should be outside square",
> +                                       square.contains(co));
> +               }
> +
> +               // sub shape
> +               for (Coord cornerpoint : square.getPoints()) {
> +                       int xadd = cornerpoint.getLatitude() > 0 ? -1 : 1;
> +                       int yadd = cornerpoint.getLongitude() > 0 ? -1 : 1;
> +                       int x = cornerpoint.getLatitude() + xadd;
> +                       int y = cornerpoint.getLongitude() + yadd;
> +                       Coord co = new Coord(x, y);
> +                       assertTrue("point (" + x + ", " + y + ") should be
> inside square", square.contains(co));
> +               }
> +
> +               // tests above / below corner points, on the outside edge
> +               for (Coord cornerpoint : square.getPoints()) {
> +                       int xadd = cornerpoint.getLatitude() > 0 ? -1 : 1;
> +                       int x = cornerpoint.getLatitude() + xadd;
> +                       int y = cornerpoint.getLongitude();
> +                       Coord co = new Coord(x, y);
> +                       assertTrue("point (" + x + ", " + y + ") should be
> outside square",     square.contains(co));
> +               }
> +
> +               // tests to the right / left side of corner points, on
> square edge
> +               for (Coord cornerpoint : square.getPoints()) {
> +                       int yadd = cornerpoint.getLongitude() > 0 ? -1 : 1;
> +                       int x = cornerpoint.getLatitude();
> +                       int y = cornerpoint.getLongitude() + yadd;
> +                       Coord co = new Coord(x, y);
> +                       assertTrue("point (" + x + ", " + y + ") should be
> outside square", square.contains(co));
> +               }
> +       }
> +
> +       @Test
> +       public void testCornerPointsOutsideSquare() {
> +
> +               // tests above / below corner points, outside squate
> +               for (Coord cornerpoint : square.getPoints()) {
> +                       int yadd = cornerpoint.getLongitude() > 0 ? 1 : -1;
> +                       int x = cornerpoint.getLatitude();
> +                       int y = cornerpoint.getLongitude() + yadd;
> +                       Coord co = new Coord(x, y);
> +                       assertFalse("point (" + x + ", " + y + ") should be
> outside square",    square.contains(co));
> +               }
> +
> +               // tests to the right / left side of corner points, outside
> square
> +               for (Coord cornerpoint : square.getPoints()) {
> +                       int xadd = cornerpoint.getLatitude() > 0 ? 1 : -1;
> +                       int x = cornerpoint.getLatitude() + xadd;
> +                       int y = cornerpoint.getLongitude();
> +                       Coord co = new Coord(x, y);
> +                       assertFalse("point (" + x + ", " + y + ") should be
> outside square", square.contains(co));
> +               }
> +
> +               // super shape
> +               for (Coord cornerpoint : square.getPoints()) {
> +                       int xadd = cornerpoint.getLatitude() > 0 ? 1 : -1;
> +                       int yadd = cornerpoint.getLongitude() > 0 ? 1 : -1;
> +                       int x = cornerpoint.getLatitude() + xadd;
> +                       int y = cornerpoint.getLongitude() + yadd;
> +                       Coord co = new Coord(x, y);
> +                       assertFalse("point (" + x + ", " + y + ") should be
> outside square", square.contains(co));
> +               }
> +       }
> +
> +
> +       @Test
> +       public void testLinePointsInsideTriangle() {
> +               // inside triangle, above / below lines
> +               List<Coord> points = Arrays.asList(
> +                               new Coord(2,1),
> +                               new Coord(6,1),
> +                               new Coord(4,1)
> +                               );
> +               for (Coord coord : points) {
> +                       assertTrue("point (" + coord.getLatitude() + ", " +
> coord.getLongitude() + ") should be inside triangle",
> +                                       triangle.contains(coord));
> +               }
> +
> +               // on lines
> +               points = Arrays.asList(
> +                               new Coord(2,2),
> +                               new Coord(6,2),
> +                               new Coord(4,0)
> +                               );
> +               for (Coord coord : points) {
> +                       assertTrue("point (" + coord.getLatitude() + ", " +
> coord.getLongitude() + ") should be outside triangle",
> +                                       triangle.contains(coord));
> +               }
> +       }
> +
> +       @Test
> +       public void testLinePointsOutsideTriangle() {
> +               // outside triangle, above / below lines
> +               List<Coord> points = Arrays.asList(
> +                               new Coord(2,3),
> +                               new Coord(6,3),
> +                               new Coord(4,-1)
> +                               );
> +               for (Coord coord : points) {
> +                       assertFalse("point (" + coord.getLatitude() + ", " +
> coord.getLongitude() + ") should be outside triangle",
> +                                       triangle.contains(coord));
> +               }
> +       }
> +
> +       @Test
> +       public void testCornerPointsInsideTriangle() {
> +               // corner points
> +               for (Coord cornerpoint : triangle.getPoints()) {
> +                       assertTrue("point (" + cornerpoint.getLatitude() +
> ", " + cornerpoint.getLongitude() + ") should be outside triangle",
> +                                       triangle.contains(cornerpoint));
> +               }
> +
> +               // sub shape
> +               List<Coord> points = Arrays.asList(
> +                               new Coord(2,1),
> +                               new Coord(4,3),
> +                               new Coord(6,1)
> +                               );
> +               for (Coord coord : points) {
> +                       assertTrue("point (" + coord.getLatitude() + ", " +
> coord.getLongitude() + ") should be inside triangle",
> +                                       triangle.contains(coord));
> +               }
> +
> +               // beside points, on edge
> +               points = Arrays.asList(
> +                               new Coord(1,0),
> +                               new Coord(7,0)
> +                               );
> +               for (Coord coord : points) {
> +                       assertTrue("point (" + coord.getLatitude() + ", " +
> coord.getLongitude() + ") should be outside triangle",
> +                                       triangle.contains(coord));
> +               }
> +       }
> +
> +       @Test
> +       public void testCornerPointsOutsideTriangle() {
> +               // above points
> +               for (Coord coord : triangle.getPoints()) {
> +                       Coord co = new Coord(coord.getLatitude(),
> coord.getLongitude() + 1);
> +                       assertFalse("point (" + co.getLatitude() + ", " +
> co.getLongitude() + ") should be outside triangle",
> +                                       triangle.contains(co));
> +               }
> +
> +               // outside triangle, beside / below lines
> +               List<Coord> points = Arrays.asList(
> +                               new Coord(-1,0),
> +                               new Coord(0,-1),
> +                               new Coord(3,4),
> +                               new Coord(5,4),
> +                               new Coord(9,0),
> +                               new Coord(8,-1)
> +                               );
> +               for (Coord coord : points) {
> +                       assertFalse("point (" + coord.getLatitude() + ", " +
> coord.getLongitude() + ") should be outside triangle",
> +                                       triangle.contains(coord));
> +               }
> +
> +               // super shape
> +               for (Coord cornerpoint : triangle.getPoints()) {
> +                       int xadd = cornerpoint.getLatitude() > 0 ? 1 : -1;
> +                       int yadd = cornerpoint.getLongitude() > 0 ? 1 : -1;
> +                       int x = cornerpoint.getLatitude() + xadd;
> +                       int y = cornerpoint.getLongitude() + yadd;
> +                       Coord co = new Coord(x, y);
> +                       assertFalse("point (" + x + ", " + y + ") should be
> outside triangle", triangle.contains(co));
> +               }
> +
> +       }
> +
> +       @Test
> +       public void testLine() {
> +               // midpoint
> +               Coord co = new Coord(7,3);
> +               assertFalse("point (" + co.getLatitude() + ", " +
> co.getLongitude() + ") should be outside line",
> +                               line.contains(co));
> +
> +       }
> +}
> Index: src/uk/me/parabola/imgfmt/app/trergn/TREHeader.java
> ===================================================================
> --- src/uk/me/parabola/imgfmt/app/trergn/TREHeader.java
> (.../upstream/mkgmap)   (revision 280)
> +++ src/uk/me/parabola/imgfmt/app/trergn/TREHeader.java
> (.../work_poiaddr_area/mkgmap)  (revision 280)
> @@ -236,6 +236,10 @@
>        public void setMapId(int id) {
>                mapId = id;
>        }
> +
> +       public void setPoiDisplayFlags(byte poiDisplayFlags) {
> +               this.poiDisplayFlags = poiDisplayFlags;
> +       }
>
>        public int getMapInfoSize() {
>                return mapInfoSize;
> Index: src/uk/me/parabola/imgfmt/app/trergn/TREFile.java
> ===================================================================
> --- src/uk/me/parabola/imgfmt/app/trergn/TREFile.java
> (.../upstream/mkgmap)   (revision 280)
> +++ src/uk/me/parabola/imgfmt/app/trergn/TREFile.java
> (.../work_poiaddr_area/mkgmap)  (revision 280)
> @@ -315,6 +315,10 @@
>                header.setBounds(area);
>        }
>
> +       public void setPoiDisplayFlags(byte b) {
> +               header.setPoiDisplayFlags(b);
> +       }
> +
>        public String[] getCopyrights() {
>                if (!isReadable())
>                        throw new IllegalStateException("not open for
> reading");
> Index: src/uk/me/parabola/imgfmt/app/lbl/POIRecord.java
> ===================================================================
> --- src/uk/me/parabola/imgfmt/app/lbl/POIRecord.java
>  (.../upstream/mkgmap)   (revision 280)
> +++ src/uk/me/parabola/imgfmt/app/lbl/POIRecord.java
>  (.../work_poiaddr_area/mkgmap)  (revision 280)
> @@ -16,6 +16,7 @@
>  */
>  package uk.me.parabola.imgfmt.app.lbl;
>
> +import java.util.Vector;
>  import uk.me.parabola.imgfmt.app.ImgFileWriter;
>  import uk.me.parabola.imgfmt.app.Label;
>
> @@ -23,6 +24,94 @@
>  * @author Steve Ratcliffe
>  */
>  public class POIRecord {
> +
> +  class SimpleStreetPhoneNumber // Helper Class to encode Street Phone
> Numbers
> +       {
> +               /**
> +                       Street and Phone numbers can be stored in two
> different ways in the poi record
> +                       Simple Number that only contain digits are coded in
> base 11 coding.
> +                       This helper     class tries to code the given
> number. If the number contains other
> +                       chars like in 4a the coding fails and the caller has
> to use a Label instead
> +               */
> +
> +               private byte encodedNumber[] = null;
> +               private int  encodedSize = 0;
> +
> +               public boolean set(String number)
> +               {
> +                       int i = 0;
> +                       int j = 0;
> +                       int val = 0;
> +
> +                       // remove sourounding whitespaces to increase chance
> for simple encoding
> +
> +                       number = number.trim();
> +
> +                       encodedNumber  = new byte[(number.length()/2)+2];
> +
> +                       while(i < number.length())
> +                       {
> +                               int c1 = 0;
> +                               int c2 = 0;
> +
> +                               c1 = decodeChar(number.charAt(i));
> +                               i++;
> +
> +                               if(i < number.length())
> +                               {
> +                                       c2 = decodeChar(number.charAt(i));
> +                                       i++;
> +                               }
> +                               else
> +                                       c2 = 10;
> +
> +                               if(c1 < 0 || c1 > 10 || c2 < 0 || c2 > 10)
> // Only 0-9 and - allowed
> +                               {
> +                                       return false;
> +                               }
> +
> +                               val = c1 * 11 + c2;
>                             // Encode as base 11
> +
> +                               if(i < 3 || i >= number.length())  // first
> byte needs special marking with 0x80
> +                                       val = val | 0x80;
>                                      // If this is not set would be treated
> as label pointer
> +
> +                               encodedNumber[j++] = (byte)val;
> +                       }
> +
> +                       if((val & 0x80) == 0 || i < 3)  // terminate string
> with 0x80 if not done
> +                       {
> +                               val = 0xF8;
> +                               encodedNumber[j++] = (byte)val;
> +                       }
> +
> +                       encodedSize  = j;
> +
> +                       return true;
> +               }
> +
> +               public void write(ImgFileWriter writer)
> +               {
> +                       for(int i = 0; i < encodedSize; i++)
> +                               writer.put(encodedNumber[i]);
> +               }
> +
> +               public boolean isUsed()
> +               {
> +                       return (encodedSize > 0);
> +               }
> +
> +               public int getSize()
> +               {
> +                       return encodedSize;
> +               }
> +
> +               private int decodeChar(char ch)
> +               {
> +                       return (ch - '0');
> +               }
> +
> +       }
> +
>        public static final byte HAS_STREET_NUM = 0x01;
>        public static final byte HAS_STREET     = 0x02;
>        public static final byte HAS_CITY       = 0x04;
> @@ -43,13 +132,16 @@
>        private int offset = -1;
>        private Label poiName;
>
> -       private int streetNumber;
> +       private SimpleStreetPhoneNumber simpleStreetNumber = new
> SimpleStreetPhoneNumber();
> +       private SimpleStreetPhoneNumber simplePhoneNumber = new
> SimpleStreetPhoneNumber();
> +
>        private Label streetName;
>        private Label streetNumberName; // Used for numbers such as 221b
> +       private Label complexPhoneNumber; // Used for numbers such as 221b
> +
> +       private City city = null;
> +       private char zipIndex  = 0;
>
> -       private char cityIndex ;
> -       private char zipIndex  ;
> -
>        private String phoneNumber;
>
>        public void setLabel(Label label) {
> @@ -60,14 +152,35 @@
>                this.streetName = label;
>        }
>
> +       public boolean setSimpleStreetNumber(String streetNumber)
> +       {
> +               return simpleStreetNumber.set(streetNumber);
> +       }
> +
> +       public void setComplexStreetNumber(Label label)
> +       {
> +               streetNumberName = label;
> +       }
> +
> +       public boolean setSimplePhoneNumber(String phone)
> +       {
> +               return simplePhoneNumber.set(phone);
> +       }
> +
> +       public void setComplexPhoneNumber(Label label)
> +       {
> +               complexPhoneNumber = label;
> +       }
> +
> +
>        public void setZipIndex(int zipIndex)
>        {
>                this.zipIndex =  (char) zipIndex;
>        }
>
> -       public void setCityIndex(int cityIndex)
> +       public void setCity(City city)
>        {
> -               this.cityIndex  = (char) cityIndex;
> +               this.city = city;
>        }
>
>        void write(ImgFileWriter writer, byte POIGlobalFlags, int realofs,
> @@ -82,11 +195,21 @@
>                if (POIGlobalFlags != getPOIFlags())
>                        writer.put(getWrittenPOIFlags(POIGlobalFlags));
>
> +               if (streetNumberName != null)
> +               {
> +                       int labOff = streetNumberName.getOffset();
> +                       writer.put((byte)((labOff & 0x7F0000) >> 16));
> +                       writer.putChar((char)(labOff & 0xFFFF));
> +               }
> +          else if (simpleStreetNumber.isUsed())
> +                  simpleStreetNumber.write(writer);
> +
>                if (streetName != null)
>                        writer.put3(streetName.getOffset());
>
> -               if (cityIndex > 0)
> +               if (city != null)
>                {
> +                       char cityIndex = (char) city.getIndex();
>                        if(numCities > 255)
>                          writer.putChar(cityIndex);
>                        else
> @@ -100,44 +223,60 @@
>                        else
>                         writer.put((byte)zipIndex);
>                }
> +
> +               if (complexPhoneNumber != null)
> +               {
> +                       int labOff = complexPhoneNumber.getOffset();
> +                       writer.put((byte)((labOff & 0x7F0000) >> 16));
> +                       writer.putChar((char)(labOff & 0xFFFF));
> +               }
> +               else if (simplePhoneNumber.isUsed())
> +                  simplePhoneNumber.write(writer);
>        }
>
>        byte getPOIFlags() {
>                byte b = 0;
>                if (streetName != null)
>                        b |= HAS_STREET;
> -               if (cityIndex > 0)
> +               if (simpleStreetNumber.isUsed() || streetNumberName != null)
> +                  b |= HAS_STREET_NUM;
> +               if (city != null)
>                        b |= HAS_CITY;
>                if (zipIndex > 0)
> -                       b |= HAS_ZIP;
> +                       b |= HAS_ZIP;
> +               if (simplePhoneNumber.isUsed() || complexPhoneNumber !=
> null)
> +                  b |= HAS_PHONE;
>                return b;
>        }
>
>        byte getWrittenPOIFlags(byte POIGlobalFlags)
>        {
> -           int mask;
> -           int flag = 0;
> -           int j = 0;
> +                       int mask;
> +                       int flag = 0;
> +                       int j = 0;
>
> -           int usedFields = getPOIFlags();
> +                       int usedFields = getPOIFlags();
>
> -           /* the local POI flag is really tricky
> -              if a bit is not set in the global mask
> -              we have to skip this bit in the local mask.
> -              In other words the meaning of the local bits
> -              change influenced by the global bits */
> +                       /* the local POI flag is really tricky if a bit is
> not set in the global mask
> +                                       we have to skip this bit in the
> local mask. In other words the meaning of the local bits
> +                                       change influenced by the global bits
> */
> +
> +                       for(byte i = 0; i < 6; i++)
> +                       {
> +                               mask =  1 << i;
>
> -               for (byte i = 0; i < 6; i++) {
> -                       mask = 1 << i;
> -
> -                       if ((mask & POIGlobalFlags) == mask) {
> -                               if ((mask & usedFields) == mask)
> -                                       flag |= (1 << j);
> -                               j++;
> +                               if((mask & POIGlobalFlags) == mask)
> +                               {
> +                                       if((mask & usedFields) == mask)
> +                                               flag = flag | (1 << j);
> +                                       j++;
> +                               }
> +
>                        }
>
> -               }
> -           return (byte) flag;
> +                       flag = flag | 0x80; // gpsmapedit asserts for this
> bit set
> +
> +                       return (byte) flag;
>        }
>
>        /**
> @@ -150,9 +289,17 @@
>                int size = 3;
>                if (POIGlobalFlags != getPOIFlags())
>                        size += 1;
> +               if (simpleStreetNumber.isUsed())
> +                       size += simpleStreetNumber.getSize();
> +               if (streetNumberName != null)
> +                       size += 3;
> +               if (simplePhoneNumber.isUsed())
> +                       size += simplePhoneNumber.getSize();
> +               if (complexPhoneNumber != null)
> +                       size += 3;
>                if (streetName != null)
> -                       size += 3;
> -               if (cityIndex > 0)
> +                       size += 3;
> +               if (city != null)
>                {
>                        /*
>                          depending on how many cities are in the LBL block
> we have
> @@ -160,9 +307,9 @@
>                        */
>
>                        if(numCities > 255)
> -                         size += 2;
> +                               size += 2;
>                        else
> -                         size += 1;
> +                               size += 1;
>                }
>                if (zipIndex > 0)
>                {
> Index: src/uk/me/parabola/imgfmt/app/lbl/PlacesFile.java
> ===================================================================
> --- src/uk/me/parabola/imgfmt/app/lbl/PlacesFile.java
> (.../upstream/mkgmap)   (revision 280)
> +++ src/uk/me/parabola/imgfmt/app/lbl/PlacesFile.java
> (.../work_poiaddr_area/mkgmap)  (revision 280)
> @@ -20,6 +20,8 @@
>  import java.util.LinkedHashMap;
>  import java.util.List;
>  import java.util.Map;
> +import java.util.SortedMap;
> +import java.util.TreeMap;
>
>  import uk.me.parabola.imgfmt.app.ImgFileWriter;
>  import uk.me.parabola.imgfmt.app.Label;
> @@ -33,7 +35,8 @@
>  public class PlacesFile {
>        private final Map<String, Country> countries = new
> LinkedHashMap<String, Country>();
>        private final Map<String, Region> regions = new LinkedHashMap<String,
> Region>();
> -       private final List<City> cities = new ArrayList<City>();
> +       private final Map<String, City> cities = new LinkedHashMap<String,
> City>();
> +       private final SortedMap<String, City> cityList = new TreeMap<String,
> City>();
>        private final Map<String, Zip> postalCodes = new
> LinkedHashMap<String, Zip>();
>        private final List<POIRecord> pois = new ArrayList<POIRecord>();
>
> @@ -62,8 +65,12 @@
>                        r.write(writer);
>                placeHeader.endRegions(writer.position());
>
> -               for (City c : cities)
> +               for (String s : cityList.keySet())
> +               {
> +                       City c = cityList.get(s);
>                        c.write(writer);
> +               }
> +
>                placeHeader.endCity(writer.position());
>
>                int poistart = writer.position();
> @@ -79,56 +86,122 @@
>        }
>
>        Country createCountry(String name, String abbr) {
> -               Country c = new Country(countries.size()+1);
> -
> +
>                String s = abbr != null ? name + (char)0x1d + abbr : name;
> +
> +               Country c = countries.get(s);
> +
> +               if(c == null)
> +               {
> +                  c = new Country(countries.size()+1);
>
> -               Label l = lblFile.newLabel(s);
> -               c.setLabel(l);
> -
> -               countries.put(name, c);
> +                  Label l = lblFile.newLabel(s);
> +                  c.setLabel(l);
> +                countries.put(s, c);
> +               }
>                return c;
>        }
>
>        Region createRegion(Country country, String name, String abbr) {
> -               Region r = new Region(country, regions.size()+1);
> -
> +
>                String s = abbr != null ? name + (char)0x1d + abbr : name;
>
> -               Label l = lblFile.newLabel(s);
> -               r.setLabel(l);
> -
> -               regions.put(name, r);
> +               String uniqueRegionName =
> s.toUpperCase().concat(Long.toString(country.getIndex()));
> +
> +               Region r = regions.get(uniqueRegionName);
> +
> +               if(r == null)
> +               {
> +                 r = new Region(country, regions.size()+1);
> +                 Label l = lblFile.newLabel(s);
> +                 r.setLabel(l);
> +                 regions.put(uniqueRegionName, r);
> +               }
>                return r;
>        }
>
> -       City createCity(Country country, String name) {
> -               City c = new City(country, cities.size()+1);
> +       City createCity(Country country, String name, boolean unique) {
> +
> +               String uniqueCityName =
> name.toUpperCase().concat("_C").concat(Long.toString(country.getIndex()));
> +
> +               City c = null;
>
> -               Label l = lblFile.newLabel(name);
> -               c.setLabel(l);  // label may be ignored if pointref is set
> +               if(!unique)
> +                       c = cities.get(uniqueCityName);
> +
> +               if(c == null)
> +               {
> +                       c = new City(country);
>
> -               cities.add(c);
> +                       Label l = lblFile.newLabel(name);
> +                       c.setLabel(l);
> +
> +                       /*
> +                                Adding 0 in between is important to get
> right sort order !!!
> +                                We have to make sure that "Kirchdorf" gets
> sorted before "Kirchdorf am Inn"
> +                                If this order is not correct nuvi would not
> find right city
> +                       */
> +
> +                 cityList.put(name + " 0" + c, c);
> +                       cities.put(uniqueCityName, c);
> +               }
> +
>                return c;
> -       }
> +  }
>
> -       City createCity(Region region, String name) {
> -               City c = new City(region, cities.size()+1);
> +       City createCity(Region region, String name, boolean unique) {
> +
> +               String uniqueCityName =
> name.toUpperCase().concat("_R").concat(Long.toString(region.getIndex()));
> +
> +               City c = null;
>
> -               Label l = lblFile.newLabel(name);
> -               c.setLabel(l);  // label may be ignored if pointref is set
> +               if(!unique)
> +                       c = cities.get(uniqueCityName);
> +
> +               if(c == null)
> +               {
> +                  c = new City(region);
>
> -               cities.add(c);
> +                       Label l = lblFile.newLabel(name);
> +                       c.setLabel(l);
> +
> +                       /*
> +                                Adding 0 in between is important to get
> right sort order !!!
> +                                We have to make sure that "Kirchdorf" gets
> sorted before "Kirchdorf am Inn"
> +                                If this order is not correct nuvi would not
> find right city
> +                       */
> +
> +                       cityList.put(name + " 0" + c, c);
> +                       cities.put(uniqueCityName, c);
> +               }
> +
>                return c;
>        }
>
> +       private void sortCities()
> +       {
> +               int index = 1;
> +
> +               for (String s : cityList.keySet())
> +               {
> +                       City c = cityList.get(s);
> +                       c.setIndex(index++);
> +               }
> +       }
> +
>        Zip createZip(String code) {
> -               Zip z = new Zip(postalCodes.size()+1);
> +
> +               Zip z = postalCodes.get(code);
> +
> +               if(z == null)
> +               {
> +                  z = new Zip(postalCodes.size()+1);
>
> -               Label l = lblFile.newLabel(code);
> -               z.setLabel(l);
> +                  Label l = lblFile.newLabel(code);
> +                  z.setLabel(l);
>
> -               postalCodes.put(code, z);
> +                  postalCodes.put(code, z);
> +               }
>                return z;
>        }
>
> @@ -146,6 +219,9 @@
>        }
>
>        void allPOIsDone() {
> +
> +               sortCities();
> +
>                poisClosed = true;
>
>                byte poiFlags = 0;
> Index: src/uk/me/parabola/imgfmt/app/lbl/City.java
> ===================================================================
> --- src/uk/me/parabola/imgfmt/app/lbl/City.java (.../upstream/mkgmap)
> (revision 280)
> +++ src/uk/me/parabola/imgfmt/app/lbl/City.java
> (.../work_poiaddr_area/mkgmap)  (revision 280)
> @@ -30,7 +30,7 @@
>        private static final int POINT_REF = 0x8000;
>        private static final int REGION_IS_COUNTRY = 0x4000;
>
> -       private final int index;
> +       private int index = -1;
>
>        private final Region region;
>        private final Country country;
> @@ -49,13 +49,13 @@
>        // null if the location is being specified.
>        private Label label;
>
> -       public City(Region region, int index) {
> +       public City(Region region) {
>                this.region = region;
>                this.country = null;
>                this.index = index;
>        }
>
> -       public City(Country country, int index) {
> +       public City(Country country) {
>                this.country = country;
>                this.region = null;
>                this.index = index;
> @@ -83,9 +83,15 @@
>        }
>
>        public int getIndex() {
> +               if (index == -1)
> +                       throw new IllegalStateException("Offset not known
> yet.");
>                return index;
>        }
>
> +       public void setIndex(int index) {
> +               this.index = index;
> +       }
> +
>        public void setLabel(Label label) {
>                pointRef = false;
>                this.label = label;
> Index: src/uk/me/parabola/imgfmt/app/lbl/LBLFile.java
> ===================================================================
> --- src/uk/me/parabola/imgfmt/app/lbl/LBLFile.java
>  (.../upstream/mkgmap)   (revision 280)
> +++ src/uk/me/parabola/imgfmt/app/lbl/LBLFile.java
>  (.../work_poiaddr_area/mkgmap)  (revision 280)
> @@ -147,14 +147,18 @@
>            return places.createRegion(country, region, abbr);
>        }
>
> -       public City createCity(Region region, String city) {
> -               return places.createCity(region, city);
> +       public City createCity(Region region, String city, boolean unique) {
> +               return places.createCity(region, city, unique);
>        }
>
> -       public City createCity(Country country, String city) {
> -               return places.createCity(country, city);
> +       public City createCity(Country country, String city, boolean unique)
> {
> +               return places.createCity(country, city, unique);
>        }
>
> +       public Zip createZip(String code) {
> +               return places.createZip(code);
> +       }
> +
>        public void allPOIsDone() {
>                places.allPOIsDone();
>        }
> Index: src/uk/me/parabola/imgfmt/app/map/Map.java
> ===================================================================
> --- src/uk/me/parabola/imgfmt/app/map/Map.java  (.../upstream/mkgmap)
> (revision 280)
> +++ src/uk/me/parabola/imgfmt/app/map/Map.java
>  (.../work_poiaddr_area/mkgmap)  (revision 280)
> @@ -225,6 +225,14 @@
>                treFile.addPolygonOverview(ov);
>        }
>
> +       /**
> +        * Set the point of interest flags.
> +        * @param flags The POI flags.
> +        */
> +       public void setPoiDisplayFlags(int flags) {
> +               treFile.setPoiDisplayFlags((byte) flags);
> +       }
> +
>        public void addMapObject(MapObject item) {
>                rgnFile.addMapObject(item);
>        }
> Index: src/uk/me/parabola/mkgmap/build/Locator.java
> ===================================================================
> --- src/uk/me/parabola/mkgmap/build/Locator.java
>  (.../upstream/mkgmap)   (revision 0)
> +++ src/uk/me/parabola/mkgmap/build/Locator.java
>  (.../work_poiaddr_area/mkgmap)  (revision 280)
> @@ -0,0 +1,477 @@
> +/*
> + * Copyright (C) 2009 Bernhard Heibler
> + *
> + *  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.
> + *
> + *  The Locator tries to fill missing country, region, postal coude
> information
> + *
> + *     The algorithm works like this:
> + *
> + *     1. Step: Go through all cities an check if they have useful country
> region info
> + *     The best case is if the tags is_in:country and is_in:county are
> present thats easy.
> + *     Some cities have is_in information that can be used. We check for
> three different
> + *     formats:
> + *
> + *     County, State, Country, Continent
> + *     County, State, Country
> + *     Continent, Country, State, County, ...
> + *
> + *     In "openGeoDb countries" this info is pretty reliable since it was
> imported from a
> + *     external db into osm. Other countries have very sparse is_in info.
> + *
> + *     All this cities that have such info will end up in "city" list. All
> which lack
> + *     such information in "location" list.
> + *
> + *     2. Step: Go through the "location" list and check if the is_in info
> has some relations
> + *     to the cities we have info about.
> + *
> + *     Especially hamlets often have no full is_in information. They only
> have one entry in
> + *     is_in that points to the city they belong to. I will check if I can
> find the name
> + *     of this city in the "City" list. If there are more with the same
> name I use the
> + *     closest one. If we can't find the exact name I use fuzzy name
> search. Thats a
> + *     workaround for german umlaute since sometimes there are used in the
> tags and
> + *     sometimes there are written as ue ae oe.
> + *
> + *     3. Step: Do the same like in step 2 once again. This is used to
> support at least
> + *     one level of recursion in is_in relations.
> + *
> + *  If there is still no info found I use brute force and use the
> information from the
> + *     next city. Has to be used for countries with poor is_in tagging.
> + *
> + * Author: Bernhard Heibler
> + * Create date: 02-Jan-2009
> + */
> +
> +package uk.me.parabola.mkgmap.build;
> +
> +import java.util.Collection;
> +import uk.me.parabola.mkgmap.general.MapPoint;
> +import uk.me.parabola.mkgmap.general.MapPointFastFindMap;
> +import uk.me.parabola.mkgmap.general.MapPointMultiMap;
> +
> +import java.util.Vector;
> +
> +public class Locator {
> +
> +       private final MapPointFastFindMap cityMap
>             = new MapPointFastFindMap();
> +       private final MapPointMultiMap          fuzzyCityMap
>    = new MapPointMultiMap();
> +       private final java.util.Vector<MapPoint> placesMap  =  new
> Vector<MapPoint>();
> +
> +       private LocatorConfig locConfig = new LocatorConfig();
> +
> +       static double totalTime = 0;
> +       static long totalFinds = 0;
> +       private int autoFillLevel = 0;
> +
> +       public void addLocation(MapPoint p)
> +       {
> +               resolveIsInInfo(p); // Preprocess the is_in field
> +
> +               if(autoFillLevel < 1 &&  p.getCity() == null)
> +               {
> +                       // Without autofill city name is the name of the tag
> +                       p.setCity(p.getName());
> +               }
> +
> +               if(p.getCity() != null)
> +               {
> +                       cityMap.put(p.getCity(), p);
> +
> +                       fuzzyCityMap.put(fuzzyDecode(p.getCity()),p);
> +
> +                       if(p.getName() != null &&
> p.getCity().equals(p.getName()) == false) // Name variants ?
> +
> fuzzyCityMap.put(fuzzyDecode(p.getName()),p);
> +               }
> +               else
> +               {
> +                       // All other places which do not seam to be a real
> city has to resolved later
> +                       placesMap.add(p);
> +               }
> +
> +       }
> +
> +
> +
> +       public void setAutoFillLevel(int level)
> +       {
> +               autoFillLevel = level;
> +       }
> +
> +       public void setDefaultCountry(String country, String abbr)
> +       {
> +               locConfig.setDefaultCountry(country, abbr);
> +       }
> +
> +       public String fixCountryString(String country)
> +  {
> +               return locConfig.fixCountryString(country);
> +       }
> +
> +       private String isCountry(String country)
> +  {
> +               return locConfig.isCountry(country);
> +       }
> +
> +       public String getCountryCode(String country)
> +  {
> +               return locConfig.getCountryCode(country);
> +  }
> +
> +       public int getPOIDispFlag(String country)
> +  {
> +               return locConfig.getPoiDispFlag(country);
> +  }
> +
> +       private boolean isOpenGeoDBCountry(String country)
> +       {
> +               // Countries that have open geo db data in osm
> +               // Right now this are only germany, austria and swizerland
> +               return locConfig.isOpenGeoDBCountry(country);
> +       }
> +
> +       private boolean isContinent(String continent)
> +  {
> +               return locConfig.isContinent(continent);
> +       }
> +
> +
> +       /**
> +        * resolveIsInInfo tries to get country and region info out of the
> is_in field
> +        * @param p     Point to process
> +        */
> +       private void resolveIsInInfo(MapPoint p)
> +       {
> +               if(p.getCountry() != null)
> +                       p.setCountry(fixCountryString(p.getCountry()));
> +
> +               if(p.getCountry() != null && p.getRegion() != null &&
> p.getCity() == null)
> +               {
> +                       p.setCity(p.getName());
> +                       return;
> +               }
> +
> +               if(p.getIsIn() != null)
> +               {
> +                       String cityList[] = p.getIsIn().split(",");
> +
> +                       //System.out.println(p.getIsIn());
> +
> +                       // is_in content is not well defined so we try our
> best to get some info out of it
> +                       // Format 1 popular in Germany:
> "County,State,Country,Continent"
> +
> +                       if(cityList.length > 1 &&
> +                               isContinent(cityList[cityList.length-1]))
>     // Is last a continent ?
> +                       {
> +                               // The one before contient should be the
> country
> +
> p.setCountry(fixCountryString(cityList[cityList.length-2].trim()));
> +
> +                               // aks the config which info to use for
> region info
> +                               int offset =
> locConfig.getRegionOffset(p.getCountry()) + 1;
> +
> +                               if(cityList.length > offset)
> +
> p.setRegion(cityList[cityList.length-(offset+1)].trim());
> +
> +                       }
> +
> +                       // Format 2 other way round:
> "Continent,Country,State,County"
> +
> +                       if(cityList.length > 1 && isContinent(cityList[0]))
>     // Is first a continent ?
> +                       {
> +                               // The one before contient should be the
> country
> +                       p.setCountry(fixCountryString(cityList[1].trim()));
> +
> +                               int offset =
> locConfig.getRegionOffset(p.getCountry()) + 1;
> +
> +                               if(cityList.length > offset)
> +
> p.setRegion(cityList[offset].trim());
> +                       }
> +
> +                       // Format like this "County,State,Country"
> +
> +                       if(p.getCountry() == null && cityList.length > 0)
> +                       {
> +                               // I don't like to check for a list of
> countries but I don't want other stuff in country field
> +
> +                               String countryStr =
> isCountry(cityList[cityList.length-1]);
> +
> +                               if(countryStr != null)
> +                               {
> +                                       p.setCountry(countryStr);
> +
> +                                       int offset =
> locConfig.getRegionOffset(countryStr) + 1;
> +
> +                                       if(cityList.length > offset)
> +
> p.setRegion(cityList[cityList.length-(offset+1)].trim());
> +                               }
> +                       }
> +               }
> +
> +               if(p.getCountry() != null && p.getRegion() != null &&
> p.getCity() == null)
> +               {
> +                       // In OpenGeoDB Countries I don't want to mess up
> the info which city is a real independent
> +                       // Community in all other countries I just have to
> do it
> +
> +                       if(isOpenGeoDBCountry(p.getCountry()) == false)
> +                               p.setCity(p.getName());
> +               }
> +       }
> +
> +       public MapPoint findNextPoint(MapPoint p)
> +       {
> +               long   startTime = System.nanoTime();
> +
> +               MapPoint nextPoint = null;
> +
> +               nextPoint = cityMap.findNextPoint(p);
> +
> +               totalFinds++;
> +               totalTime = totalTime + ((System.nanoTime() -
> startTime)/1e9);
> +               return nextPoint;
> +       }
> +
> +       public  MapPoint findByCityName(MapPoint p)
> +       {
> +               MapPoint near   = null;
> +               Double minDist  = Double.MAX_VALUE;
> +               Collection <MapPoint> nextCityList = null;
> +
> +               if(p.getCity() == null)
> +                       return null;
> +
> +               nextCityList = cityMap.getList(p.getCity());
> +
> +               if(nextCityList != null)
> +               {
> +                       for (MapPoint nextCity: nextCityList)
> +                       {
> +                               Double dist =
> p.getLocation().distance(nextCity.getLocation());
> +
> +                               if(dist < minDist)
> +                               {
> +                                       minDist = dist;
> +                                       near = nextCity;
> +                               }
> +                       }
> +               }
> +
> +               nextCityList =
> fuzzyCityMap.getList(fuzzyDecode(p.getCity()));
> +
> +               if(nextCityList != null)
> +               {
> +                       for (MapPoint nextCity: nextCityList)
> +                       {
> +                               Double dist =
> p.getLocation().distance(nextCity.getLocation());
> +
> +                               if(dist < minDist)
> +                               {
> +                                       minDist = dist;
> +                                       near = nextCity;
> +                               }
> +                       }
> +               }
> +
> +               if(near != null && minDist < 30000) // Wrong hit more the 30
> km away ?
> +                       return near;
> +               else
> +                       return null;
> +       }
> +
> +       private MapPoint findCity(MapPoint place, boolean fuzzy)
> +       {
> +               MapPoint near   = null;
> +               Double minDist  = Double.MAX_VALUE;
> +               Collection <MapPoint> nextCityList = null;
> +
> +               String isIn = place.getIsIn();
> +
> +               if(isIn != null)
> +               {
> +                       String cityList[] = isIn.split(",");
> +
> +                       // Go through the isIn string and check if we find a
> city with this name
> +                       // Lets hope we find the next bigger city
> +
> +                       for(int i = 0; i < cityList.length; i++)
> +                 {
> +                       String biggerCityName=cityList[i].trim();
> +
> +
> +                               if(fuzzy == false)
> +                                       nextCityList =
> cityMap.getList(biggerCityName);
> +                               else
> +                                 nextCityList =
> fuzzyCityMap.getList(fuzzyDecode(biggerCityName));
> +
> +                               if(nextCityList != null)
> +                               {
> +                                       for (MapPoint nextCity:
> nextCityList)
> +                                       {
> +                                               Double dist =
> place.getLocation().distance(nextCity.getLocation());
> +
> +                                               if(dist < minDist)
> +                                               {
> +                                                       minDist = dist;
> +                                                       near = nextCity;
> +                                       }
> +                                       }
> +                               }
> +                       }
> +
> +                       if (autoFillLevel > 3)  // Some debug output to find
> suspicios relations
> +                       {
> +
> +                               if(near != null && minDist > 30000)
> +                         {
> +                                       System.out.println("Locator: " +
> place.getName() + " is far away from " +
> +
> near.getName() +        " " + (minDist/1000.0) + " km is_in" +
>  place.getIsIn());
> +                                               if(nextCityList != null)
> +                                               System.out.println("Number
> of cities with this name: " + nextCityList.size());
> +                         }
> +
> +                               //if(near != null && fuzzy)
> +                         //{
> +                               // System.out.println("Locator: " +
> place.getName() + " may belong to " +
> +                               //                       near.getName() + "
> is_in" + place.getIsIn());
> +                               //}
> +                       }
> +               }
> +
> +               return near;
> +       }
> +
> +       public void resolve() {
> +
> +               if(autoFillLevel < 0)
> +                       return;                 // Nothing to do if autofill
> is fulli disabled
> +
> +               if(autoFillLevel > 2)
> +               {
> +                       System.out.println("\nLocator City   Map contains "
> + cityMap.size() + " entries");
> +                       System.out.println("Locator Places Map contains " +
> placesMap.size() + " entries");
> +               }
> +
> +               int runCount = 0;
> +               int maxRuns = 2;
> +               int unresCount;
> +
> +               do
> +               {
> +                       unresCount=0;
> +
> +                       for (int i = 0; i < placesMap.size(); i++)
> +                       {
> +                               MapPoint place = placesMap.get(i);
> +
> +                               if(place != null)
> +                               {
> +
> +                                       // first lets try exact name
> +
> +                                       MapPoint near = findCity(place,
> false);
> +
> +
> +                                       // if this didn't worked try to
> workaround german umlaute
> +
> +                               if(near == null)
> +                                       near = findCity(place, true);
> +
> +                               if(autoFillLevel > 3 && near == null &&
> (runCount + 1) == maxRuns)
> +                               {
> +                                               if(place.getIsIn() != null)
> +                                               System.out.println("Locator:
> CAN't locate " + place.getName() + " is_in " +     place.getIsIn()
> +                                                               + " " +
> place.getLocation().toOSMURL());
> +                               }
> +
> +
> +                                       if(near != null)
> +                                       {
> +
> place.setCity(near.getCity());
> +                                               place.setZip(near.getZip());
> +                       }
> +                                       else if (autoFillLevel > 1 &&
> (runCount + 1) == maxRuns)
> +                                       {
> +                                                       // In the last
> resolve run just take info from the next known city
> +                                                       near =
> cityMap.findNextPoint(place);
> +                                       }
> +
> +
> +                                       if(near != null)
> +                                       {
> +                                               if(place.getRegion() ==
> null)
> +
> place.setRegion(near.getRegion());
> +
> +                                               if(place.getCountry() ==
> null)
> +
> place.setCountry(near.getCountry());
> +
> +                                       }
> +
> +                                       if(near == null)
> +                                               unresCount++;
> +
> +                               }
> +                       }
> +
> +                       for (int i = 0; i < placesMap.size(); i++)
> +                       {
> +                               MapPoint place = placesMap.get(i);
> +
> +                               if (place != null)
> +                               {
> +                                       if( place.getCity() != null)
> +                                       {
> +                                       cityMap.put(place.getName(),place);
> +
> fuzzyCityMap.put(fuzzyDecode(place.getName()),place);
> +                                               placesMap.set(i, null);
> +                                       }
> +                                       else if(autoFillLevel < 2 &&
> (runCount + 1) == maxRuns)
> +                                       {
> +
> place.setCity(place.getName());
> +
> cityMap.put(place.getName(),place);
> +                                       }
> +                               }
> +                       }
> +
> +                       runCount++;
> +
> +                       if(autoFillLevel > 2)
> +                               System.out.println("Locator City   Map
> contains " + cityMap.size() +
> +                               " entries after resolver run " + runCount +
> " Still unresolved " + unresCount);
> +
> +               }
> +               while(unresCount > 0 && runCount < maxRuns);
> +
> +       }
> +
> +       public void printStat()
> +       {
> +          System.out.println("Locator Find called: " + totalFinds + "
> time");
> +          System.out.println("Locator Find time:   " + totalTime + " s");
> +
> +          cityMap.printStat();
> +       }
> +
> +       private String fuzzyDecode(String stringToDecode)
> +  {
> +
> +               if(stringToDecode == null)
> +                       return stringToDecode;
> +
> +               String decodeString = stringToDecode.toUpperCase().trim();
> +
> +               // German umlaut resolution
> +               decodeString =
> decodeString.replaceAll("Ä","AE").replaceAll("Ü","UE").replaceAll("Ö","OE");
> +
> +               //if(decodeString.equals(stringToDecode) == false)
> +               //      System.out.println(stringToDecode + " -> " +
> decodeString);
> +
> +               return (decodeString);
> +       }
> +
> +}
> +
> Index: src/uk/me/parabola/mkgmap/build/MapBuilder.java
> ===================================================================
> --- src/uk/me/parabola/mkgmap/build/MapBuilder.java
> (.../upstream/mkgmap)   (revision 280)
> +++ src/uk/me/parabola/mkgmap/build/MapBuilder.java
> (.../work_poiaddr_area/mkgmap)  (revision 280)
> @@ -21,12 +21,12 @@
>  import java.util.Collections;
>  import java.util.HashMap;
>  import java.util.List;
> -import java.util.SortedMap;
> -import java.util.TreeMap;
>
>  import uk.me.parabola.imgfmt.app.Coord;
>  import uk.me.parabola.imgfmt.app.lbl.City;
>  import uk.me.parabola.imgfmt.app.lbl.Country;
> +import uk.me.parabola.imgfmt.app.lbl.Zip;
> +import uk.me.parabola.imgfmt.app.Label;
>  import uk.me.parabola.imgfmt.app.lbl.LBLFile;
>  import uk.me.parabola.imgfmt.app.lbl.POIRecord;
>  import uk.me.parabola.imgfmt.app.lbl.Region;
> @@ -83,26 +83,55 @@
>        private static final int CLEAR_TOP_BITS = (32 - 15);
>
>        private final java.util.Map<MapPoint,POIRecord> poimap = new
> HashMap<MapPoint,POIRecord>();
> -       private final SortedMap<String, Object> sortedCities = new
> TreeMap<String, Object>();
> +       private final java.util.Map<MapPoint,City> cityMap = new
> HashMap<MapPoint,City>();
> +
>        private boolean doRoads;
>
> +       private Locator locator = new Locator();
>        private Country country;
>        private Region  region;
>
> -       private String countryName = "UNITED KINGDOM";
> -       private String countryAbbr = "GBR";
> +       private String countryName = "COUNTRY";
> +       private String countryAbbr = "ABC";
>        private String regionName;
>        private String regionAbbr;
> +       private int             locationAutofillLevel = 0;
> +       private boolean poiAddresses = true;
> +       private int             poiDisplayFlags = 0;
>
>        public MapBuilder() {
>                regionName = null;
>        }
>
>        public void config(EnhancedProperties props) {
> +
> +               String autoFillPar;
> +
>                countryName = props.getProperty("country-name", countryName);
>                countryAbbr = props.getProperty("country-abbr", countryAbbr);
>                regionName = props.getProperty("region-name", null);
>                regionAbbr = props.getProperty("region-abbr", null);
> +
> +               if(props.getProperty("no-poi-address", null) != null)
> +                       poiAddresses = false;
> +
> +               autoFillPar = props.getProperty("location-autofill", null);
> +
> +               if(autoFillPar != null)
> +               {
> +                       try
> +                       {
> +                               locationAutofillLevel =
> Integer.parseInt(autoFillPar);
> +                       }
> +                       catch (Exception e)
> +                       {
> +                               locationAutofillLevel = 1;
> +                       }
> +               }
> +
> +               locator.setAutoFillLevel(locationAutofillLevel);
> +
> +
>        }
>
>        /**
> @@ -123,6 +152,7 @@
>                if(regionName != null)
>                        region = lblFile.createRegion(country, regionName,
> regionAbbr);
>
> +               processCities(map, src);
>                processPOIs(map, src);
>                //preProcessRoads(map, src);
>                processOverviews(map, src);
> @@ -157,52 +187,211 @@
>        }
>
>        /**
> -        * First stage of handling POIs
> +        * Processing of Cities
>         *
> -        * POIs need to be handled first, because we need the offsets
> -        * in the LBL file.
> +        * Fills the city list in lbl block that is required for find by
> name
> +        * It also builds up information that is required to get address
> info
> +        * for the POIs
>         *
>         * @param map The map.
>         * @param src The map data.
>         */
> -       private void processPOIs(Map map, MapDataSource src) {
> +       private void processCities(Map map, MapDataSource src) {
>                LBLFile lbl = map.getLblFile();
>
> -               // gpsmapedit doesn't sort the city names so to be
> -               // friendly we generate the city objects in alphabetic
> -               // order - to do that we first build a map from city
> -               // name to the associated MapPoint - we don't want to
> -               // be fooled by duplicate names so suffix the name
> -               // with the object to make it unique
> +               locator.setDefaultCountry(countryName, countryAbbr);
> +
> +               // collect the names of the cities
>                for (MapPoint p : src.getPoints()) {
>                        if(p.isCity() && p.getName() != null)
> -                               sortedCities.put(p.getName() + "@" + p, p);
> +                               locator.addLocation(p); // Put the city info
> the map for missing info
>                }
>
> -               // now loop through the sorted keys and retrieve
> -               // the MapPoint associated with the key - now we
> -               // can create the City object and remember it for later
> -               for (String s : sortedCities.keySet()) {
> -                   MapPoint p = (MapPoint)sortedCities.get(s);
> -                   City c;
> -                   if(region != null)
> -                       c = lbl.createCity(region, p.getName());
> -                   else
> -                       c = lbl.createCity(country, p.getName());
> -                   sortedCities.put(s, c);
> +               if(locationAutofillLevel > 0)
> +                       locator.resolve(); // Try to fill missing
> information that include search of next city
> +
> +               for (MapPoint p : src.getPoints())
> +               {
> +                       if(p.isCity() && p.getName() != null)
> +                       {
> +                               Country thisCountry;
> +                               Region  thisRegion;
> +                               City            thisCity;
> +
> +                               String CountryStr = p.getCountry();
> +                               String RegionStr  = p.getRegion();
> +
> +                               if(CountryStr != null)
> +                                       thisCountry =
> lbl.createCountry(CountryStr, locator.getCountryCode(CountryStr));
> +                               else
> +                                       thisCountry = country;
> +
> +                               if(RegionStr != null)
> +                               {
> +                                       thisRegion =
> lbl.createRegion(thisCountry,RegionStr, null);
> +                               }
> +                               else
> +                                       thisRegion = region;
> +
> +                               if(thisRegion != null)
> +                                       thisCity =
> lbl.createCity(thisRegion, p.getName(), true);
> +                               else
> +                                       thisCity =
> lbl.createCity(thisCountry, p.getName(), true);
> +
> +                               cityMap.put(p, thisCity);
> +                       }
>                }
> -               // if point has a nearest city, create a POIRecord to
> -               // reference it
> +       }
> +
> +       private void processPOIs(Map map, MapDataSource src) {
> +
> +               LBLFile lbl = map.getLblFile();
> +               long poiAddrCountr = 0;
> +               boolean checkedForPoiDispFlag = false;
> +               boolean doAutofill;
> +
>                for (MapPoint p : src.getPoints()) {
> -                       MapPoint nearestCityPoint = p.getNearestCityPoint();
> -                       if(nearestCityPoint != null && p.getName() != null)
> {
> -                               POIRecord r = lbl.createPOI(p.getName());
> -                               City nearestCity =
> (City)sortedCities.get(nearestCityPoint.getName() + "@" + nearestCityPoint);
> -                               r.setCityIndex(nearestCity.getIndex());
> +
> +                       if(p.isCity() == false &&
> +                                (p.isRoadNamePOI() || poiAddresses))
> +                       {
> +                               if(locationAutofillLevel > 0 ||
> p.isRoadNamePOI())
> +                                       doAutofill = true;
> +                               else
> +                                       doAutofill = false;
> +
> +
> +                               String CountryStr = p.getCountry();
> +                               String RegionStr  = p.getRegion();
> +                               String ZipStr     = p.getZip();
> +                               String CityStr    = p.getCity();
> +                               boolean guessed   = false;
> +
> +                               if(CityStr != null || ZipStr != null
> ||RegionStr != null || CountryStr != null)
> +                                       poiAddrCountr++;
> +
> +                               if(CountryStr != null)
> +                                       CountryStr =
> locator.fixCountryString(CountryStr);
> +
> +                               if(CountryStr == null || RegionStr == null
> || (ZipStr == null && CityStr == null))
> +                               {
> +                                               MapPoint nextCity =
> locator.findByCityName(p);
> +
> +                                               if(doAutofill && nextCity ==
> null)
> +                                                       nextCity =
> locator.findNextPoint(p);
> +
> +                                               if(nextCity != null)
> +                                               {
> +                                                       guessed = true;
> +
> +                                                       if (CountryStr ==
> null) CountryStr = nextCity.getCountry();
> +                                                       if (RegionStr ==
> null)  RegionStr  = nextCity.getRegion();
> +
> +                                                       if(doAutofill)
> +                                                       {
> +                                                               if(ZipStr ==
> null)
> +                                                               {
> +
> String CityZipStr = nextCity.getZip();
> +
> +                                                                       //
> Ignore list of Zips seperated by ;
> +
> +
> if(CityZipStr != null && CityZipStr.indexOf(',') < 0)
> +
>     ZipStr = CityZipStr;
> +                                                               }
> +
> +                                                               if(CityStr
> == null) CityStr = nextCity.getCity();
> +                                                       }
> +
> +                                               }
> +                               }
> +
> +
> +                               if(CountryStr != null &&
> checkedForPoiDispFlag == false)
> +                               {
> +                                       // Different countries require
> different address notation
> +
> +                                       poiDisplayFlags =
> locator.getPOIDispFlag(CountryStr);
> +                                       checkedForPoiDispFlag = true;
> +                               }
> +
> +
> +                               if(p.isRoadNamePOI() && CityStr != null)
> +                               {
> +                                               // If it is road POI add
> city name and street name into address info
> +                                               p.setStreet(p.getName());
> +                                               p.setName(p.getName() + "/"
> + CityStr);
> +                               }
> +
> +                               POIRecord r = lbl.createPOI(p.getName());
> +
> +                               if(CityStr != null)
> +                               {
> +                                       Country thisCountry;
> +                                       Region  thisRegion;
> +                                       City            city;
> +
> +                                       if(CountryStr != null)
> +                                               thisCountry =
> lbl.createCountry(CountryStr, locator.getCountryCode(CountryStr));
> +                                       else
> +                                               thisCountry = country;
> +
> +                                       if(RegionStr != null)
> +                                               thisRegion =
> lbl.createRegion(thisCountry,RegionStr, null);
> +                                       else
> +                                               thisRegion = region;
> +
> +                                       if(thisRegion != null)
> +                                               city =
> lbl.createCity(thisRegion, CityStr, false);
> +                                       else
> +                                               city =
> lbl.createCity(thisCountry, CityStr, false);
> +
> +                               r.setCity(city);
> +
> +                               }
> +
> +                               if (ZipStr != null)
> +                               {
> +                                       Zip zip = lbl.createZip(ZipStr);
> +                                       r.setZipIndex(zip.getIndex());
> +                               }
> +
> +                               if(p.getStreet() != null)
> +                               {
> +                                       Label streetName =
> lbl.newLabel(p.getStreet());
> +                                       r.setStreetName(streetName);
> +                               }
> +                               else if (guessed == true &&
> locationAutofillLevel > 0)
> +                               {
> +                                       Label streetName = lbl.newLabel("FIX
> MY ADDRESS");
> +                                       r.setStreetName(streetName);
> +                               }
> +
> +                               if(p.getHouseNumber() != null)
> +                               {
> +
> if(r.setSimpleStreetNumber(p.getHouseNumber()) == false)
> +                                       {
> +                                               Label streetNumber =
> lbl.newLabel(p.getHouseNumber());
> +
> r.setComplexStreetNumber(streetNumber);
> +                                       }
> +                               }
> +
> +                               if(p.getPhone() != null)
> +                               {
> +
> if(r.setSimplePhoneNumber(p.getPhone()) == false)
> +                                       {
> +                                               Label phoneNumber =
> lbl.newLabel(p.getPhone());
> +
> r.setComplexPhoneNumber(phoneNumber);
> +                                       }
> +                               }
> +
>                                poimap.put(p, r);
>                        }
>                }
> +
> +               //System.out.println(poiAddrCountr + " POIs have address
> info");
> +
>                lbl.allPOIsDone();
> +
>        }
>
>        /**
> @@ -369,6 +558,9 @@
>                // The bounds of the map.
>                map.setBounds(src.getBounds());
>
> +               if(poiDisplayFlags != 0)
>                    // POI requested alterate address notation
> +                       map.setPoiDisplayFlags(poiDisplayFlags);
> +
>                // You can add anything here.
>                // But there has to be something, otherwise the map does not
> show up.
>                //
> @@ -461,7 +653,8 @@
>                                        // retrieve the City created earlier
> for
>                                        // this point and store the point
> info
>                                        // in it
> -                                       City c = (City)sortedCities.get(name
> + "@" + point);
> +                                       City c = (City)cityMap.get(point);
> +
>                                        if(pointIndex > 255) {
>                                                System.err.println("Can't set
> city point index for " + name + " (too many indexed points in division)\n");
>                                        } else {
> Index: src/uk/me/parabola/mkgmap/build/LocatorConfig.java
> ===================================================================
> --- src/uk/me/parabola/mkgmap/build/LocatorConfig.java
>  (.../upstream/mkgmap)   (revision 0)
> +++ src/uk/me/parabola/mkgmap/build/LocatorConfig.java
>  (.../work_poiaddr_area/mkgmap)  (revision 280)
> @@ -0,0 +1,303 @@
> +/*
> + * Copyright (C) 2009 Bernhard Heibler
> + *
> + *  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.
> + *
> + *  The Locator tries to fill missing country, region, postal coude
> information
> + *
> + *  To do so we analyse the is_in information and if this doesn't helps us
> we
> + *  try to get info from the next known city
> + *
> + * Author: Bernhard Heibler
> + * Create date: 02-Jan-2009
> + */
> +package uk.me.parabola.mkgmap.build;
> +
> +import org.w3c.dom.*;
> +import javax.xml.parsers.*;
> +import java.io.*;
> +
> +import java.util.HashMap;
> +
> +public class LocatorConfig {
> +
> +       private final java.util.Map<String,String>  variantMap = new
> HashMap<String,String>();
> +       private final java.util.Map<String,String>  abrMap = new
> HashMap<String,String>();
> +       private final java.util.Map<String,Boolean> geoDbMap = new
> HashMap<String,Boolean>();
> +       private final java.util.Map<String,Integer>  regOffsetMap = new
> HashMap<String,Integer>();
> +       private final java.util.Map<String,Integer>  poiDispFlagMap = new
> HashMap<String,Integer>();
> +       private final java.util.Map<String,Boolean> continentMap = new
> HashMap<String,Boolean>();
> +
> +
> +       public LocatorConfig()
> +       {
> +    loadConfig("/LocatorConfig.xml");
> +       }
> +
> +       private void loadConfig(String fileName)
> +       {
> +               try
> +               {
> +                       DocumentBuilder builder =
> DocumentBuilderFactory.newInstance().newDocumentBuilder();
> +
> +                       InputStream inStream;
> +
> +                       try
> +      {
> +                         inStream = new FileInputStream("resources/" +
> fileName);
> +                       }
> +                       catch (Exception ex)
> +                       {
> +                               inStream = null;
> +                       }
> +
> +                       if(inStream == null)    // If not loaded from disk
> use from jar file
> +                               inStream =
> this.getClass().getResourceAsStream(fileName);
> +
> +                       Document document = builder.parse(inStream);
> +
> +                       Node rootNode = document.getDocumentElement();
> +
> +                       if(rootNode.getNodeName().equals("locator"))
> +                       {
> +                                 Node cNode = rootNode.getFirstChild();
> +
> +                                       while(cNode != null)
> +                                       {
> +
> if(cNode.getNodeName().equals("continent"))
> +                                               {
> +                                                       NamedNodeMap attr =
> cNode.getAttributes();
> +                                                       Node nameTag = null;
> +
> +                                                       if(attr != null)
> +                                                       {
> +                                                               nameTag =
> attr.getNamedItem("name");
> +                                                               if(nameTag
> != null)
> +
> addContinent(nameTag.getNodeValue());
> +                                                       }
> +
> +                                               }
> +
> +
> if(cNode.getNodeName().equals("country"))
> +                                               {
> +                                                       NamedNodeMap attr =
> cNode.getAttributes();
> +                                                       Node nameTag = null;
> +
> +                                                       if(attr != null)
> +                                                       {
> +                                                               nameTag =
> attr.getNamedItem("name");
> +
> +                                                               Node abrTag
> = attr.getNamedItem("abr");
> +
> +                                                               if(abrTag !=
> null && nameTag != null)
> +
> addAbr(nameTag.getNodeValue(),abrTag.getNodeValue());
> +
> +                                                               if(abrTag ==
> null && nameTag != null)
> +
> addAbr(nameTag.getNodeValue(),"");
> +
> +                                                               Node geoTag
> = attr.getNamedItem("geodb");
> +
> +                                                               if(nameTag
> != null && geoTag != null)
> +                                                               {
> +
> if(geoTag.getNodeValue().equals("1"))
> +
>     addOpenGeoDb(nameTag.getNodeValue());
> +                                                               }
> +
> +                                                               Node
> regionOffsetTag = attr.getNamedItem("regionOffset");
> +
> +
> if(regionOffsetTag != null && nameTag != null)
> +                                                               {
> +
> addRegionOffset(nameTag.getNodeValue(),Integer.parseInt(regionOffsetTag.getNodeValue()));
> +                                                               }
> +
> +                                                               Node
> poiDispTag = attr.getNamedItem("poiDispFlag");
> +
> +
> if(poiDispTag != null && nameTag != null)
> +                                                               {
> +
> addPoiDispTag(nameTag.getNodeValue(),Integer.decode(poiDispTag.getNodeValue()));
> +                                                               }
> +                                                       }
> +
> +                                                       Node cEntryNode =
> cNode.getFirstChild();
> +                                                       while(cEntryNode !=
> null)
> +                                                       {
> +
> if(cEntryNode.getNodeName().equals("variant"))
> +                                                               {
> +                                                                       Node
> nodeText = cEntryNode.getFirstChild();
> +
> +
> if(nodeText != null && nameTag != null)
> +
>     addVariant(nameTag.getNodeValue(), nodeText.getNodeValue());
> +
> +                                                               }
> +                                                               cEntryNode =
> cEntryNode.getNextSibling();
> +                                                       }
> +                                               }
> +
> +                                               cNode =
> cNode.getNextSibling();
> +                                       }
> +                       }
> +                       else
> +                       {
> +                               System.out.println(fileName + "contains
> invalid root tag " + rootNode.getNodeName());
> +                       }
> +       }
> +               catch (Exception ex)
> +               {
> +                       ex.printStackTrace();
> +                       //System.out.println("Something is wrong here");
> +               }
> +       return;
> + }
> +
> +       private void addVariant(String country, String variant)
> +  {
> +               String cStr = country.toUpperCase().trim();
> +               String vStr = variant.toUpperCase().trim();
> +
> +               //System.out.println(vStr + " -> " + cStr);
> +
> +               variantMap.put(vStr,cStr);
> +  }
> +
> +       private void addAbr(String country, String abr)
> +  {
> +               String cStr = country.toUpperCase().trim();
> +               String aStr = abr.toUpperCase().trim();
> +
> +               //System.out.println(cStr + " -> " + aStr);
> +
> +               abrMap.put(cStr,aStr);
> +  }
> +
> +       private void addRegionOffset(String country, Integer offset)
> +  {
> +               String cStr = country.toUpperCase().trim();
> +
> +               //System.out.println(cStr + " -> " + offset);
> +
> +               regOffsetMap.put(cStr,offset);
> +  }
> +
> +       private void addPoiDispTag(String country, Integer flag)
> +  {
> +               String cStr = country.toUpperCase().trim();
> +
> +               //System.out.println(cStr + " -> " + flag);
> +
> +               poiDispFlagMap.put(cStr,flag);
> +  }
> +
> +       private void addOpenGeoDb(String country)
> +  {
> +               String cStr = country.toUpperCase().trim();
> +
> +               //System.out.println(cStr + " openGeoDb");
> +
> +               geoDbMap.put(cStr,true);
> +
> +  }
> +
> +       private void addContinent(String continent)
> +  {
> +               String cStr = continent.toUpperCase().trim();
> +
> +               //System.out.println(cStr + " continent");
> +
> +               continentMap.put(cStr,true);
> +
> +  }
> +
> +
> +       public void setDefaultCountry(String country, String abbr)
> +       {
> +               addAbr(country, abbr);
> +       }
> +
> +  public String fixCountryString(String country)
> +  {
> +               String cStr = country.toUpperCase().trim();
> +
> +               String fixedString = variantMap.get(cStr);
> +
> +               if(fixedString != null)
> +                       return fixedString;
> +               else
> +                       return(cStr);
> +       }
> +
> +       public String isCountry(String country)
> +  {
> +               String cStr = fixCountryString(country);
> +
> +               if(getCountryCode(cStr) != null)
> +                       return cStr;
> +               else
> +                       return null;
> +
> +       }
> +
> +       public String getCountryCode(String country)
> +  {
> +               String cStr = country.toUpperCase().trim();
> +               return abrMap.get(cStr);
> +  }
> +
> +       public int getRegionOffset(String country)
> +  {
> +               String cStr = country.toUpperCase().trim();
> +
> +               Integer regOffset = regOffsetMap.get(cStr);
> +
> +               if(regOffset != null)
> +                       return regOffset;
> +               else
> +                       return 1; // Default is 1 the next string after
> before country
> +  }
> +
> +       public int getPoiDispFlag(String country)
> +  {
> +               String cStr = country.toUpperCase().trim();
> +
> +               Integer flag = poiDispFlagMap.get(cStr);
> +
> +               if(flag != null)
> +                       return flag;
> +               else
> +                       return 0; // Default is 1 the next string after
> before country
> +  }
> +
> +       public boolean isOpenGeoDBCountry(String country)
> +       {
> +               // Countries that have open geo db data in osm
> +               // Right now this are only germany, austria and swizerland
> +
> +               String cStr = country.toUpperCase().trim();
> +
> +               if(geoDbMap.get(cStr) != null)
> +                       return true;
> +
> +               return false;
> +       }
> +
> +       public boolean isContinent(String continent)
> +  {
> +               String s = continent.toUpperCase().trim();
> +
> +               if(continentMap.get(s) != null)
> +                       return(true);
> +
> +               return false;
> +       }
> +
> +
> +
> +
> +}
> +
> Index: src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java
> ===================================================================
> --- src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java
> (.../upstream/mkgmap)   (revision 280)
> +++ src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java
> (.../work_poiaddr_area/mkgmap)  (revision 280)
> @@ -253,6 +253,11 @@
>                shape.setPoints(way.getPoints());
>
>                clipper.clipShape(shape, collector);
> +
> +               GType pointType = nodeRules.resolveType(way);
> +
> +               if(pointType != null)
> +                       shape.setPoiType(pointType.getType());
>        }
>
>        private void addPoint(Node node, GType gt) {
> @@ -271,6 +276,50 @@
>                ms.setType(gt.getType());
>                ms.setMinResolution(gt.getMinResolution());
>                ms.setMaxResolution(gt.getMaxResolution());
> +
> +               // Now try to get some address info for POIs
> +
> +               String city         = element.getTag("addr:city");
> +               String zip          = element.getTag("addr:postcode");
> +               String street       = element.getTag("addr:street");
> +               String houseNumber  = element.getTag("addr:housenumber");
> +               String phone                = element.getTag("phone");
> +               String isIn             = element.getTag("is_in");
> +               String country      = element.getTag("is_in:country");
> +               String region       = element.getTag("is_in:county");
> +
> +               if(country != null)
> +                       country = element.getTag("addr:country");
> +
> +               if(zip == null)
> +                 zip = element.getTag("openGeoDB:postal_codes");
> +
> +               if(city == null)
> +                 city = element.getTag("openGeoDB:sort_name");
> +
> +               if(city != null)
> +                 ms.setCity(city);
> +
> +               if(zip != null)
> +                 ms.setZip(zip);
> +
> +               if(street != null)
> +                 ms.setStreet(street);
> +
> +               if(houseNumber != null)
> +                 ms.setHouseNumber(houseNumber);
> +
> +               if(isIn != null)
> +                 ms.setIsIn(isIn);
> +
> +               if(phone != null)
> +                 ms.setPhone(phone);
> +
> +               if(country != null)
> +                 ms.setCountry(country);
> +
> +               if(region != null)
> +                 ms.setRegion(region);
>        }
>
>        void addRoad(Way way, GType gt) {
> Index: src/uk/me/parabola/mkgmap/reader/polish/PolishMapDataSource.java
> ===================================================================
> --- src/uk/me/parabola/mkgmap/reader/polish/PolishMapDataSource.java
>  (.../upstream/mkgmap)   (revision 280)
> +++ src/uk/me/parabola/mkgmap/reader/polish/PolishMapDataSource.java
>  (.../work_poiaddr_area/mkgmap)  (revision 280)
> @@ -362,6 +362,23 @@
>                        } catch (NumberFormatException e) {
>                                endLevel = 0;
>                        }
> +               } else if (name.equals("ZipCode")) {
> +                 elem.setZip(recode(value));
> +               } else if (name.equals("CityName")) {
> +                 elem.setCity(recode(value));
> +               } else if (name.equals("StreetDesc")) {
> +                 elem.setStreet(recode(value));
> +               } else if (name.equals("HouseNumber")) {
> +                 elem.setHouseNumber(recode(value));
> +               } else if (name.equals("is_in")) {
> +                 elem.setIsIn(recode(value));
> +               } else if (name.equals("Phone")) {
> +                 elem.setPhone(recode(value));
> +               } else if (name.equals("CountryName")) {
> +                 elem.setCountry(recode(value));
> +               } else if (name.equals("RegionName")) {
> +                       //System.out.println("RegionName " + value);
> +                 elem.setRegion(recode(value));
>                } else {
>                        return false;
>                }
> Index: src/uk/me/parabola/mkgmap/main/MapMaker.java
> ===================================================================
> --- src/uk/me/parabola/mkgmap/main/MapMaker.java
>  (.../upstream/mkgmap)   (revision 280)
> +++ src/uk/me/parabola/mkgmap/main/MapMaker.java
>  (.../work_poiaddr_area/mkgmap)  (revision 280)
> @@ -36,6 +36,8 @@
>  import uk.me.parabola.mkgmap.general.LoadableMapDataSource;
>  import uk.me.parabola.mkgmap.general.MapLine;
>  import uk.me.parabola.mkgmap.general.MapPoint;
> +import uk.me.parabola.mkgmap.general.MapShape;
> +import uk.me.parabola.mkgmap.general.MapPointFastFindMap;
>  import uk.me.parabola.mkgmap.reader.plugin.MapReader;
>
>  /**
> @@ -49,6 +51,7 @@
>        public String makeMap(CommandArgs args, String filename) {
>                try {
>                        LoadableMapDataSource src = loadFromFile(args,
> filename);
> +                       makeAreaPOIs(args, src);
>                        makeRoadNamePOIS(args, src);
>                        return makeMap(args, src);
>                } catch (FormatException e) {
> @@ -135,6 +138,44 @@
>                return src;
>        }
>
> +       private void makeAreaPOIs(CommandArgs args, LoadableMapDataSource
> src) {
> +               String s =
> args.getProperties().getProperty("add-pois-to-areas");
> +               if (s != null) {
> +
> +                       MapPointFastFindMap poiMap = new
> MapPointFastFindMap();
> +
> +                       for (MapPoint point : src.getPoints())
> +                       {
> +                               if(point.isRoadNamePOI() == false) // Don't
> put road pois in this list
> +                                       poiMap.put(null, point);
> +                       }
> +
> +                       for (MapShape shape : src.getShapes()) {
> +                               String shapeName = shape.getName();
> +
> +                               int pointType = shape.getPoiType();
> +
> +                               // only make a point if the shape has a name
> and we know what type of point to make
> +                               if (pointType == 0)
> +                                       continue;
> +
> +                               // check if there is not already a poi in
> that shape
> +
> +                               if(poiMap.findPointInShape(shape, pointType)
> == null)
> +                               {
> +                                       MapPoint newPoint = new MapPoint();
> +                                       newPoint.setName(shapeName);
> +                                       newPoint.setType(pointType);
> +
> newPoint.setLocation(shape.getLocation()); // TODO use centriod
> +                                       src.getPoints().add(newPoint);
> +                                       log.info("created POI ", shapeName,
> "from shape");
> +                               }
> +                       }
> +               }
> +
> +       }
> +
> +
>        void makeRoadNamePOIS(CommandArgs args, LoadableMapDataSource src) {
>                String rnp =
> args.getProperties().getProperty("road-name-pois", null);
>                // are road name POIS wanted?
> @@ -277,26 +318,10 @@
>                }
>
>                String name = road.getName();
> -               MapPoint nearestCity = null;
> -               if(cities != null) {
> -                       double shortestDistance = 10000000;
> -                       for(MapPoint mp : cities) {
> -                               double distance =
> coord.distance(mp.getLocation());
> -                               if(distance < shortestDistance) {
> -                                       shortestDistance = distance;
> -                                       nearestCity = mp;
> -                               }
> -                       }
> -               }
> -
>                MapPoint rnp = new MapPoint();
>
> -               if(nearestCity != null && nearestCity.getName() != null) {
> -                       //rnp.setNearestCityPoint(nearestCity);
> -                       name += "/" + nearestCity.getName();
> -               }
> -
>                rnp.setName(name);
> +               rnp.setRoadNamePOI(true);
>                rnp.setType(type);
>                rnp.setLocation(coord);
>                return rnp;
> Index: src/uk/me/parabola/mkgmap/general/MapPointMultiMap.java
> ===================================================================
> --- src/uk/me/parabola/mkgmap/general/MapPointMultiMap.java
> (.../upstream/mkgmap)   (revision 0)
> +++ src/uk/me/parabola/mkgmap/general/MapPointMultiMap.java
> (.../work_poiaddr_area/mkgmap)  (revision 280)
> @@ -0,0 +1,65 @@
> +/*
> + * Copyright (C) 2009 Bernhard Heibler
> + *
> + *  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.
> + *
> + *     This is multimap to store city information for the Address Locator
> + *
> + *
> + * Author: Bernhard Heibler
> + * Create date: 02-Jan-2009
> + */
> +
> +package uk.me.parabola.mkgmap.general;
> +
> +
> +import java.util.Vector;
> +import java.util.HashMap;
> +import java.util.Collection;
> +
> +public class MapPointMultiMap{
> +
> +       private final java.util.Map<String,Vector<MapPoint>> map  = new
> HashMap<String,Vector<MapPoint>>();
> +
> +       public MapPoint put(String name, MapPoint p)
> +       {
> +               Vector<MapPoint> list;
> +
> +               list = map.get(name);
> +
> +               if(list == null){
> +
> +                  list = new Vector<MapPoint>();
> +                  list.add(p);
> +                  map.put(name, list);
> +               }
> +               else
> +                  list.add(p);
> +
> +               return p;
> +       }
> +
> +       public MapPoint get(String name)
> +       {
> +               Vector<MapPoint> list;
> +
> +               list = map.get(name);
> +
> +               if(list != null)
> +                               return list.elementAt(0);
> +               else
> +                 return null;
> +       }
> +
> +       public Collection<MapPoint> getList(String name)
> +       {
> +               return map.get(name);
> +       }
> +}
> \ No newline at end of file
> Index: src/uk/me/parabola/mkgmap/general/MapPointFastFindMap.java
> ===================================================================
> --- src/uk/me/parabola/mkgmap/general/MapPointFastFindMap.java
>  (.../upstream/mkgmap)   (revision 0)
> +++ src/uk/me/parabola/mkgmap/general/MapPointFastFindMap.java
>  (.../work_poiaddr_area/mkgmap)  (revision 280)
> @@ -0,0 +1,238 @@
> +/*
> + * Copyright (C) 2009 Bernhard Heibler
> + *
> + *  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.
> + *
> + *     This is multimap to store city information for the Address Locator
> + *  tt provides also a fast tile based nearest point search function
> + *
> + *
> + * Author: Bernhard Heibler
> + * Create date: 02-Jan-2009
> + */
> +
> +package uk.me.parabola.mkgmap.general;
> +
> +
> +import java.util.Collection;
> +import java.util.HashMap;
> +import java.util.Vector;
> +import java.util.List;
> +import uk.me.parabola.imgfmt.app.Coord;
> +
> +public class MapPointFastFindMap{
> +
> +       private final java.util.Map<String,Vector<MapPoint>> map  = new
> HashMap<String,Vector<MapPoint>>();
> +       private final java.util.Map<Long,Vector<MapPoint>> posMap = new
> HashMap<Long,Vector<MapPoint>>();
> +       private final java.util.Vector<MapPoint> points  =  new
> Vector<MapPoint>();
> +
> +       private static final  long POS_HASH_DIV = 8000;         // the
> smaller -> more tiles
> +       private static final  long POS_HASH_MUL = 10000;                //
> multiplicator for latitude to create hash
> +
> +       public MapPoint put(String name, MapPoint p)
> +       {
> +               Vector<MapPoint> list;
> +
> +               if(name != null)
> +               {
> +                       list = map.get(name);
> +
> +                       if(list == null){
> +
> +                               list = new Vector<MapPoint>();
> +                               list.add(p);
> +                               map.put(name, list);
> +                       }
> +                       else
> +                               list.add(p);
> +
> +                       points.add(p);
> +               }
> +
> +               long posHash  =
>  getPosHashVal(p.getLocation().getLatitude(),
> p.getLocation().getLongitude());
> +
> +               list = posMap.get(posHash);
> +
> +               if(list == null)
> +               {
> +                       list = new Vector<MapPoint>();
> +                       list.add(p);
> +                       posMap.put(posHash, list);
> +               }
> +               else
> +                       list.add(p);
> +
> +               return p;
> +       }
> +
> +       public MapPoint get(String name)
> +       {
> +               Vector<MapPoint> list;
> +
> +               list = map.get(name);
> +
> +               if(list != null)
> +                               return list.elementAt(0);
> +               else
> +                 return null;
> +       }
> +
> +       public Collection<MapPoint> getList(String name)
> +       {
> +               return map.get(name);
> +       }
> +
> +       public long size()
> +       {
> +               return points.size();
> +       }
> +
> +       public Collection<MapPoint> values()
> +       {
> +               return points;
> +       }
> +
> +       public MapPoint get(int index)
> +       {
> +               return points.get(index);
> +       }
> +
> +       public MapPoint set(int index, MapPoint p)
> +       {
> +               return points.set(index, p);
> +       }
> +
> +       public boolean remove(MapPoint p)
> +       {
> +               return points.remove(p);
> +       }
> +
> +
> +       public MapPoint findNextPoint(MapPoint p)
> +       {
> +               /* tile based search
> +
> +               to prevent expensive linear search over all points we put
> the points
> +               into tiles. We just search the tiles the point is in linear
> and the
> +               sourounding tiles. If we don't find a point we have to
> search further
> +               arround the central tile
> +
> +               */
> +
> +               Vector<MapPoint> list;
> +               double minDist = Double.MAX_VALUE;
> +               MapPoint nextPoint = null;
> +
> +               if(posMap.size() < 1)  // No point in list
> +                  return nextPoint;
> +
> +               long centLatitIdx = p.getLocation().getLatitude()  /
> POS_HASH_DIV ;
> +               long centLongiIdx = p.getLocation().getLongitude() /
> POS_HASH_DIV ;
> +               long delta = 1;
> +
> +               long latitIdx;
> +               long longiIdx;
> +               long posHash;
> +
> +               do
> +               {
> +                       // in the first step we only check our tile and the
> tiles sourinding us
> +
> +                       for(latitIdx = centLatitIdx - delta; latitIdx <=
> centLatitIdx + delta; latitIdx++)
> +                   for(longiIdx = centLongiIdx - delta; longiIdx <=
> centLongiIdx + delta; longiIdx++)
> +                   {
> +                       if(delta < 2
> +                                               || latitIdx == centLatitIdx
> - delta
> +                                               || latitIdx == centLatitIdx
> + delta
> +                                               || longiIdx == centLongiIdx
> - delta
> +                                               || longiIdx == centLongiIdx
> + delta)
> +                                       {
> +
> +                                               posHash = latitIdx *
> POS_HASH_MUL + longiIdx;
> +
> +                                               list = posMap.get(posHash);
> +
> +                                               if(list != null)
> +                                               {
> +
> +                                                       for (MapPoint
> actPoint: list)
> +                                                       {
> +                                                               double
> distance =  actPoint.getLocation().distance(p.getLocation());
> +
> +                                                               if(distance
> < minDist)
> +                                                               {
> +
> nextPoint = actPoint;
> +
> minDist = distance;
> +
> +                                                               }
> +                                                       }
> +                                               }
> +                                       }
> +                       }
> +                       delta ++; // We have to look in tiles farer away
> +               }
> +               while(nextPoint == null);
> +
> +               return nextPoint;
> +       }
> +
> +       public MapPoint findPointInShape(MapShape shape, int pointType)
> +       {
> +               Vector<MapPoint> list;
> +               List<Coord>     points = shape.getPoints();
> +               MapPoint nextPoint = null;
> +               long lastHashValue = -1;
> +               long posHash;
> +
> +               if(posMap.size() < 1)  // No point in list
> +                  return nextPoint;
> +
> +               for(int i=0; i < points.size(); i++)
> +               {
> +                       posHash =
> getPosHashVal(points.get(i).getLatitude(),points.get(i).getLongitude());
> +
> +                       if(posHash == lastHashValue) // Have we already
> checked this tile ?
> +                               continue;
> +
> +                       lastHashValue = posHash;
> +
> +                       list = posMap.get(posHash);
> +
> +                       if(list != null)
> +                       {
> +                               for (MapPoint actPoint: list)
> +                               {
> +                                       if(pointType == 0 ||
> actPoint.getType() == pointType)
> +                                       {
> +                                               if(shape.contains(
> actPoint.getLocation()))
> +                                                       return actPoint;
> +                                       }
> +                               }
> +                       }
> +               }
> +
> +               return null;
> +       }
> +
> +       private long getPosHashVal(long lat, long lon)
> +       {
> +               long latitIdx  =  lat /  POS_HASH_DIV ;
> +               long longiIdx  =  lon /  POS_HASH_DIV ;
> +
> +               //System.out.println("LatIdx " + latitIdx + " LonIdx " +
> longiIdx);
> +
> +               return latitIdx * POS_HASH_MUL + longiIdx;
> +       }
> +
> +       public void printStat()
> +       {
> +               System.out.println("Locator PosHashmap contains " +
> posMap.size() + " tiles");
> +       }
> +}
> \ No newline at end of file
> Index: src/uk/me/parabola/mkgmap/general/MapElement.java
> ===================================================================
> --- src/uk/me/parabola/mkgmap/general/MapElement.java
> (.../upstream/mkgmap)   (revision 280)
> +++ src/uk/me/parabola/mkgmap/general/MapElement.java
> (.../work_poiaddr_area/mkgmap)  (revision 280)
> @@ -15,6 +15,9 @@
>  */
>  package uk.me.parabola.mkgmap.general;
>
> +import java.util.Map;
> +import java.util.HashMap;
> +
>  import uk.me.parabola.imgfmt.app.Coord;
>
>  /**
> @@ -29,6 +32,13 @@
>
>        private int minResolution = 24;
>        private int maxResolution = 24;
> +
> +       private String zipCode;
> +       private String city;
> +       private String region;
> +       private String country;
> +
> +       private final Map<String, String> attributes = new HashMap<String,
> String>();
>
>        protected MapElement() {
>        }
> @@ -53,9 +63,84 @@
>        }
>
>        public void setName(String name) {
> -               this.name = name;
> +         if(name != null)
> +               this.name = name.toUpperCase();
>        }
>
> +       public String getCity() {
> +               return city;
> +       }
> +
> +       public void setCity(String city) {
> +         if(city != null)
> +               this.city = city.toUpperCase();
> +       }
> +
> +       public String getZip() {
> +               return zipCode;
> +       }
> +
> +       public void setZip(String zip) {
> +               this.zipCode = zip;
> +       }
> +
> +       public String getCountry() {
> +               return country;
> +       }
> +
> +       public void setCountry(String country) {
> +         if(country != null)
> +               this.country = country.toUpperCase();
> +       }
> +
> +       public String getRegion() {
> +               return region;
> +       }
> +
> +       public void setRegion(String region) {
> +         if(region != null)
> +               this.region = region.toUpperCase();
> +       }
> +
> +       public String getStreet() {
> +               return attributes.get("street");
> +       }
> +
> +       public void setStreet(String street) {
> +               attributes.put("street", street);
> +       }
> +
> +       public String getPhone() {
> +               return attributes.get("phone");
> +       }
> +
> +       public void setPhone(String phone) {
> +
> +         if(phone.startsWith("00"))
> +               {
> +                 phone = phone.replaceFirst("00","+");
> +               }
> +               attributes.put("phone", phone);
> +       }
> +
> +       public String getHouseNumber() {
> +               return attributes.get("houseNumber");
> +       }
> +
> +       public void setHouseNumber(String houseNumber) {
> +               attributes.put("houseNumber", houseNumber);
> +       }
> +
> +       public String getIsIn() {
> +               return attributes.get("isIn");
> +       }
> +
> +       public void setIsIn(String isIn) {
> +         if(isIn != null)
> +               attributes.put("isIn", isIn.toUpperCase());
> +       }
> +
> +
>        /**
>         * This is the type code that goes in the .img file so that the GPS
> device
>         * knows what to display.
> Index: src/uk/me/parabola/mkgmap/general/MapPoint.java
> ===================================================================
> --- src/uk/me/parabola/mkgmap/general/MapPoint.java
> (.../upstream/mkgmap)   (revision 280)
> +++ src/uk/me/parabola/mkgmap/general/MapPoint.java
> (.../work_poiaddr_area/mkgmap)  (revision 280)
> @@ -27,6 +27,7 @@
>  public class MapPoint extends MapElement {
>        private Coord location;
>        private MapPoint nearestCityPoint;
> +       private boolean isRoadNamePoi = false;
>
>        public MapPoint() {
>        }
> @@ -63,11 +64,11 @@
>                return type >= 0x0100 && type <= 0x1100;
>        }
>
> -       public void setNearestCityPoint(MapPoint nearestCityPoint) {
> -               this.nearestCityPoint = nearestCityPoint;
> +       public void setRoadNamePOI(boolean isRoadNamePoi) {
> +               this.isRoadNamePoi = isRoadNamePoi;
>        }
>
> -       public MapPoint getNearestCityPoint() {
> -               return nearestCityPoint;
> +       public boolean isRoadNamePOI() {
> +               return this.isRoadNamePoi;
>        }
>  }
> Index: src/uk/me/parabola/mkgmap/general/MapShape.java
> ===================================================================
> --- src/uk/me/parabola/mkgmap/general/MapShape.java
> (.../upstream/mkgmap)   (revision 280)
> +++ src/uk/me/parabola/mkgmap/general/MapShape.java
> (.../work_poiaddr_area/mkgmap)  (revision 280)
> @@ -15,6 +15,11 @@
>  */
>  package uk.me.parabola.mkgmap.general;
>
> +import java.util.ArrayList;
> +import java.util.List;
> +
> +import uk.me.parabola.imgfmt.app.Coord;
> +
>  /**
>  * A shape or polygon is just the same as a line really as far as I can
> tell.
>  * There are some things that you cannot do with them semantically.
> @@ -23,6 +28,8 @@
>  */
>  public class MapShape extends MapLine {// So top code can link objects from
> here
>
> +       private int poiType = 0;
> +
>        public MapShape() {
>        }
>
> @@ -38,5 +45,158 @@
>                throw new IllegalArgumentException(
>                                "can't set a direction on a polygon");
>        }
> +
> +       public void setPoiType(int type)
> +       {
> +               this.poiType = type;
> +       }
> +
> +       public int getPoiType()
> +       {
> +               return this.poiType;
> +       }
> +
> +       /**
> +        * Checks if a point is contained within this shape. Points on the
> +        * edge of the shape are considered inside.
> +        *
> +        * @param co point to check
> +        * @return true if point is in shape, false otherwise
> +        */
> +       public boolean contains(Coord co) {
> +               return contains(this.getPoints(), co, true);
> +       }
> +
> +       /*
> +        * Checks if a point is contained within a shape.
> +        *
> +        * @param points points that define the shape
> +        * @param target point to check
> +        * @param onLineIsInside if a point on the line should be considered
> inside the shape
> +        * @return true if point is contained within the shape, false if the
> target point is outside the shape
> +        */
> +       private static boolean contains(List<Coord> points, Coord target,
> boolean onLineIsInside) {
> +               // implementation of the Ray casting algorithm as described
> here:
> +               // http://en.wikipedia.org/wiki/Point_in_polygon
> +               // with inspiration from:
> +               // http://www.visibone.com/inpoly/
> +               boolean inside = false;
> +               if (points.size() < 3)
> +                       return false;
>
> +               // complete the shape if we're dealing with a MapShape that
> is not closed
> +               Coord start = points.get(0);
> +               Coord end = points.get(points.size() - 1);
> +               if (!start.equals(end)) {
> +                       // make copy of the shape's geometry
> +                       List<Coord> pointsTemp = new
> ArrayList<Coord>(points.size() + 1);
> +                       for (Coord coord : points) {
> +                               pointsTemp.add(new
> Coord(coord.getLatitude(), coord.getLongitude()));
> +                       }
> +                       pointsTemp.add(new Coord(start.getLatitude(),
> start.getLongitude()));
> +                       points = pointsTemp;
> +               }
> +
> +               int xtarget = target.getLatitude();
> +               int ytarget = target.getLongitude();
> +
> +               for (int i = 0; i < points.size() - 1; i++) {
> +
> +                       // apply transformation points to change target
> point to (0,0)
> +                       int x0 = points.get(i).getLatitude() - xtarget;
> +                       int y0 = points.get(i).getLongitude() - ytarget;
> +                       int x1 = points.get(i+1).getLatitude() - xtarget;
> +                       int y1 = points.get(i+1).getLongitude() - ytarget;
> +
> +                       // ensure that x0 is smaller than x1 so that we can
> just check to see if the line intersects the y axis easily
> +                       if (x0 > x1) {
> +                               int xtemp = x0;
> +                               int ytemp = y0;
> +                               x0 = x1;
> +                               y0 = y1;
> +                               x1 = xtemp;
> +                               y1 = ytemp;
> +                       }
> +
> +                       // use (0,0) as target because points already
> transformed
> +                       if (isPointOnLine(x0, y0, x1, y1, 0, 0))
> +                               return onLineIsInside;
> +
> +                       // explanation of if statement
> +                       //
> +                       // (x0 < 0 && x1 >= 0):
> +                       // are the x values between the y axis? only include
> points from the right
> +                       // with this check so that corners aren't counted
> twice
> +                       //
> +                       // (y0 * (x1 - x0) > (y1 - y0) * x0):
> +                       // from y  = mx + b:
> +                       //   => b = y0 ((y1 - y0) / (x1 - x0)) * x0
> +                       // for intersection,    b > 0
> +                       // from y = mx + b,     b = y - mx
> +                       //                  =>  y - mx > 0
> +                       //                  => y0 - ((y1 - y0) / (x1 - x0))
> * x0 > 0
> +                       //                  => y0 > ((y1 - y0) / (x1 - x0))
> * x0
> +                       // from 'if (x0 > x1)',  x1 >= x0
> +                       //                  => x1 - x0 >=0
> +                       //                  => y0 * (x1 - x0) > (y1 - y0) *
> x0
> +                       if ((x0 < 0 && x1 >= 0) && (y0 * (x1 - x0)) > ((y1 -
> y0) * x0))
> +                               inside = !inside;
> +               }
> +
> +               return inside;
> +       }
> +
> +       /*
> +        * Checks if a point is on a line.
> +        *
> +        * @param x0 x value of first point in line
> +        * @param y0 y value of first point in line
> +        * @param x1 x value of second point in line
> +        * @param y1 y value of second point in line
> +        * @param xt x value of target point
> +        * @param yt y value of target point
> +        * @return return true if point is on the line, false if the point
> isn't on the line
> +        */
> +       private static boolean isPointOnLine(int x0, int y0, int x1, int y1,
> int xt, int yt) {
> +               // this implementation avoids using doubles
> +               // apply transformation points to change target point to
> (0,0)
> +               x0 = x0 - xt;
> +               y0 = y0 - yt;
> +               x1 = x1 - xt;
> +               y1 = y1 - yt;
> +
> +               // ensure that x0 is smaller than x1 so that we can just
> check to see if the line intersects the y axis easily
> +               if (x0 > x1) {
> +                       int xtemp = x0;
> +                       int ytemp = y0;
> +                       x0 = x1;
> +                       y0 = y1;
> +                       x1 = xtemp;
> +                       y1 = ytemp;
> +               }
> +
> +               // if a point is on the edge of shape (on a line), it's
> considered outside the shape
> +               // special case if line is on y-axis
> +               if (x0 == 0 && x1 == 0) {
> +                       // ensure that y0 is smaller than y1 so that we can
> just check if the line intersects the x axis
> +                       if (y0 > y1) {
> +                               int xtemp = x0;
> +                               int ytemp = y0;
> +                               x0 = x1;
> +                               y0 = y1;
> +                               x1 = xtemp;
> +                               y1 = ytemp;
> +                       }
> +                       // test to see if we have a vertical line touches
> x-axis
> +                       if (y0 <= 0 && y1 >= 0)
> +                               return true;
> +                       // checks if point is on the line, see comments in
> contain() for derivation of similar
> +                       // formula - left as an exercise to the reader ;)
> +               } else if ((x0 <= 0 && x1 >= 0) && (y0 * (x1 - x0)) == ((y1
> - y0) * x0)) {
> +                       return true;
> +               }
> +               return false;
> +       }
> +
> +
>  }
> Index: build.xml
> ===================================================================
> --- build.xml   (.../upstream/mkgmap)   (revision 280)
> +++ build.xml   (.../work_poiaddr_area/mkgmap)  (revision 280)
> @@ -78,8 +78,9 @@
>   <target name="build" depends="compile" >
>                <copy todir="${build.classes}">
>                        <fileset dir="${resources}">
> -                               <include name="*.csv"/>
> +                               <include name="*.csv"/>
>                                <include name="*.properties"/>
> +                               <include name="*.xml"/>
>                                <include name="**/*.trans"/>
>                                <include name="styles/**"/>
>                                <include name="help/**"/>
> @@ -151,6 +152,7 @@
>                                manifest="${resources}/MANIFEST.MF">
>                        <include name="**/*.class"/>
>                        <include name="*.csv"/>
> +                       <include name="*.xml"/>
>                        <include name="*.properties"/>
>                        <include name="**/*.trans"/>
>                        <include name="styles/**"/>
> Index: resources/styles/default/polygons
> ===================================================================
> --- resources/styles/default/polygons   (.../upstream/mkgmap)   (revision
> 280)
> +++ resources/styles/default/polygons   (.../work_poiaddr_area/mkgmap)
>  (revision 280)
> @@ -6,6 +6,8 @@
>  amenity=supermarket [0x08 resolution 21]
>  amenity=university [0x0a resolution 18]
>
> +building=yes [0x13 resolution 18]
> +
>  landuse=allotments [0x4e resolution 20]
>  landuse=cemetary [0x1a resolution 18]
>  landuse=cemetery [0x1a resolution 18]
> Index: resources/LocatorConfig.xml
> ===================================================================
> --- resources/LocatorConfig.xml (.../upstream/mkgmap)   (revision 0)
> +++ resources/LocatorConfig.xml (.../work_poiaddr_area/mkgmap)  (revision
> 280)
> @@ -0,0 +1,38 @@
> +<?xml version="1.0" encoding="UTF-8" ?>
> +<locator>
> +       <country name="Deutschland" abr="DEU" geodb="1" regionOffset="3"
> poiDispFlag="0xc">
> +               <variant>Bundesrepublik Deutschland</variant>
> +               <variant>Germany</variant>
> +               <variant>DE</variant>
> +       </country>
> +       <country name="Österreich" abr="AUT" geodb="1" poiDispFlag="0xc">
> +               <variant>Austria</variant>
> +               <variant>AT</variant>
> +       </country>
> +       <country name="Schweiz" abr="CHE" geodb="1" poiDispFlag="0xc">
> +               <variant>Switzerland</variant>
> +               <variant>CH</variant>
> +       </country>
> +       <country name="United Kingdom" abr="GBR">
> +               <variant>UK</variant>
> +               <variant>GB</variant>
> +       </country>
> +       <country name="Italia" abr="ITA" regionOffset="2">
> +               <variant>Italy</variant>
> +               <variant>IT</variant>
> +       </country>
> +       <country name="France" abr="FRA">
> +       </country>
> +       <continent name="Europe">
> +       </continent>
> +       <continent name="Africa">
> +       </continent>
> +       <continent name="Asia">
> +       </continent>
> +       <continent name="North America">
> +       </continent>
> +       <continent name="South America">
> +       </continent>
> +       <continent name="Oceania">
> +       </continent>
> +</locator>
> Index: resources/help/en/options
> ===================================================================
> --- resources/help/en/options   (.../upstream/mkgmap)   (revision 280)
> +++ resources/help/en/options   (.../work_poiaddr_area/mkgmap)  (revision
> 280)
> @@ -120,6 +120,11 @@
>        Generate a POI for each named road. By default, the POIs'
>        Garmin type code is 0x640a. If desired, a different type code
>        can be specified with this option.
> +
> +--add-pois-to-areas
> +       Generate a POI for each area. The POIs are created after the style
> +       is applied and only for polygon types that have a reasonable point
> +       equivalent.
>
>  --tdbfile
>        Write a .tdb file.
> @@ -134,6 +139,24 @@
>        same area, you can see through this map and see the lower map too.
>        Useful for contour line maps among other things.
>
> +--no-poi-address
> +       Disable address / phone information to POIs. Address info is read
> according to
> +       the "Karlsruhe" tagging schema. Automatic filling of missing
> information could
> +       be enabled using the "location-autofill" option.
> +
> +--location-autofill=''number''
> +       Controls how country region info is gathered for cities / streets
> and pois
> +
> +       0       (Default) The country region info is gathered by analysis of
> the cities is_in tags.
> +               If no country region info is present the default passed
> default country region is used.
> +
> +       1       Additional analysis of partial is_in info to get relations
> between hamlets and cities
> +
> +       2       Brute force search for nearest city with info if all methods
> before failed. Warning
> +               cities my end up in the wrong country/region.
> +
> +       3       Enables debug output about suspicious relations that might
> cause wrong country region info
> +
>  --version
>        Output program version.
>
>
> _______________________________________________
> mkgmap-dev mailing list
> mkgmap-dev at lists.mkgmap.org.uk
> http://www.mkgmap.org.uk/mailman/listinfo/mkgmap-dev
>


More information about the mkgmap-dev mailing list