[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 >
- Previous message: [mkgmap-dev] [PATCH] POI Address + Area POIs v7 R942
- Next message: [mkgmap-dev] [PATCH] POI Address + Area POIs v7 R942
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
More information about the mkgmap-dev mailing list