Hi,
here is the updated POI address patch for the current trunk R942:
- Poi address information is now added by default
-- Option -no-poi-address added to switch the feature off
- Check if zip tag contains a list of zips added
- By default heuristic is disabled. Exception are:
-- Name of nearest city is added to street POIs (if street POIs are enabled)
-- If POI contains a city name tag. Country region info is taken from
city with same name in the neighborhood
- You can use the --location-autofill=X option to enable heuristic if
you like
Thanks
Berni.
Index: src/uk/me/parabola/imgfmt/app/trergn/TREHeader.java
===================================================================
--- src/uk/me/parabola/imgfmt/app/trergn/TREHeader.java (.../upstream/mkgmap) (revision 271)
+++ src/uk/me/parabola/imgfmt/app/trergn/TREHeader.java (.../work_poiaddr/mkgmap) (revision 271)
@@ -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 271)
+++ src/uk/me/parabola/imgfmt/app/trergn/TREFile.java (.../work_poiaddr/mkgmap) (revision 271)
@@ -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 271)
+++ src/uk/me/parabola/imgfmt/app/lbl/POIRecord.java (.../work_poiaddr/mkgmap) (revision 271)
@@ -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 271)
+++ src/uk/me/parabola/imgfmt/app/lbl/PlacesFile.java (.../work_poiaddr/mkgmap) (revision 271)
@@ -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 271)
+++ src/uk/me/parabola/imgfmt/app/lbl/City.java (.../work_poiaddr/mkgmap) (revision 271)
@@ -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 271)
+++ src/uk/me/parabola/imgfmt/app/lbl/LBLFile.java (.../work_poiaddr/mkgmap) (revision 271)
@@ -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 271)
+++ src/uk/me/parabola/imgfmt/app/map/Map.java (.../work_poiaddr/mkgmap) (revision 271)
@@ -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/MapPointMultiMap.java
===================================================================
--- src/uk/me/parabola/mkgmap/build/MapPointMultiMap.java (.../upstream/mkgmap) (revision 0)
+++ src/uk/me/parabola/mkgmap/build/MapPointMultiMap.java (.../work_poiaddr/mkgmap) (revision 271)
@@ -0,0 +1,66 @@
+/*
+ * 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.build;
+
+
+import java.util.Vector;
+import java.util.HashMap;
+import java.util.Collection;
+import uk.me.parabola.mkgmap.general.MapPoint;
+
+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/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/mkgmap) (revision 271)
@@ -0,0 +1,474 @@
+/*
+ * 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 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 271)
+++ src/uk/me/parabola/mkgmap/build/MapBuilder.java (.../work_poiaddr/mkgmap) (revision 271)
@@ -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/MapPointFastFindMap.java
===================================================================
--- src/uk/me/parabola/mkgmap/build/MapPointFastFindMap.java (.../upstream/mkgmap) (revision 0)
+++ src/uk/me/parabola/mkgmap/build/MapPointFastFindMap.java (.../work_poiaddr/mkgmap) (revision 271)
@@ -0,0 +1,196 @@
+/*
+ * 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.build;
+
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Vector;
+import uk.me.parabola.mkgmap.general.MapPoint;
+
+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>();
+
+ public static final long POS_HASH_DIV = 8000; // the smaller -> more tiles
+ public static final long POS_HASH_MUL = 10000; // multiplicator for latitude to create hash
+
+ 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);
+
+ points.add(p);
+
+ long posHash = getPosHashVal(p.getLocation().getLatitude(), p.getLocation().getLongitude());
+
+ //System.out.println("PosHash " + posHash);
+
+ 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(points.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;
+ }
+
+ 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/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/mkgmap) (revision 271)
@@ -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 271)
+++ src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java (.../work_poiaddr/mkgmap) (revision 271)
@@ -271,6 +271,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 271)
+++ src/uk/me/parabola/mkgmap/reader/polish/PolishMapDataSource.java (.../work_poiaddr/mkgmap) (revision 271)
@@ -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 271)
+++ src/uk/me/parabola/mkgmap/main/MapMaker.java (.../work_poiaddr/mkgmap) (revision 271)
@@ -277,26 +277,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/MapElement.java
===================================================================
--- src/uk/me/parabola/mkgmap/general/MapElement.java (.../upstream/mkgmap) (revision 271)
+++ src/uk/me/parabola/mkgmap/general/MapElement.java (.../work_poiaddr/mkgmap) (revision 271)
@@ -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 271)
+++ src/uk/me/parabola/mkgmap/general/MapPoint.java (.../work_poiaddr/mkgmap) (revision 271)
@@ -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: build.xml
===================================================================
--- build.xml (.../upstream/mkgmap) (revision 271)
+++ build.xml (.../work_poiaddr/mkgmap) (revision 271)
@@ -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/LocatorConfig.xml
===================================================================
--- resources/LocatorConfig.xml (.../upstream/mkgmap) (revision 0)
+++ resources/LocatorConfig.xml (.../work_poiaddr/mkgmap) (revision 271)
@@ -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 271)
+++ resources/help/en/options (.../work_poiaddr/mkgmap) (revision 271)
@@ -134,6 +134,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.