
Hi, here comes the latest version of my patch. Now updated to latest SVN version 861. Patch v3 is a full patch and can be applied without the previous ones. Changes to previous version are: - Reactivated map.setPoiDisplayFlags method to control format of address for POIs. Flag is read from xml config file per country - Road Name POI: Replaced linear search for next city by faster tile based search I developed for Country/Region Autofill I have tested this stuff so far for the following locations: Paris, Switzerland, Souther Bavaria - Western Austria, North Italy and Cambridge Area UK. Sometimes some cities might end up in the wrong region / country because of missing or wrong is_in tagging. I think I might be able to improve this by geometrically analyzing the borderlines in the maps but I have to take a closer look into this. Resources/LocatorConfig.xml contains only information for a bunch of countries. You might want to add a entry to specify variants of country names used in the osm file. Berni.
- Country Region Autofill for Marks recently added find-by-name function - POI simple and complex house number and phone number support - POI address info is taken from "Karlsruhe address" info if available - Phone support is pretty cool if you have a garmin that support cell phone control
If no country / region / address info is given information is take from nearest city. I have added a class that uses tiles for fast nearest point search. In countries that have OpenGeoDB data imported into OSM this works pretty good. In Bavaria and Austria it works pretty good.
For other countries this doesn't work so well. I have also tested it near Cambridge UK and I it was also not so bad. Unfortunately the is_in tag I have to us is very fuzzy. I needed some lookup tables right now. This are hardcoded in Locator.java right now this should be in a config file. It might help to pass your country with the --country-name option for better County detection.
If you use the .osm file as direct input for mkgmap you should be fine. If you use polish map you have to patch osm2mp because I needed some more fields. Let me know If this works for you guys or if you have further questions ...
Index: src/uk/me/parabola/imgfmt/app/trergn/TREHeader.java =================================================================== --- src/uk/me/parabola/imgfmt/app/trergn/TREHeader.java (revision 180) +++ src/uk/me/parabola/imgfmt/app/trergn/TREHeader.java (working copy) @@ -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 (revision 180) +++ src/uk/me/parabola/imgfmt/app/trergn/TREFile.java (working copy) @@ -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 (revision 180) +++ src/uk/me/parabola/imgfmt/app/lbl/POIRecord.java (working copy) @@ -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 (revision 180) +++ src/uk/me/parabola/imgfmt/app/lbl/PlacesFile.java (working copy) @@ -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,110 @@ } 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); + + 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); + + 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 +207,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 (revision 180) +++ src/uk/me/parabola/imgfmt/app/lbl/City.java (working copy) @@ -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 (revision 180) +++ src/uk/me/parabola/imgfmt/app/lbl/LBLFile.java (working copy) @@ -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 (revision 180) +++ src/uk/me/parabola/imgfmt/app/map/Map.java (working copy) @@ -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/osmstyle/StyledConverter.java =================================================================== --- src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java (revision 180) +++ src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java (working copy) @@ -225,9 +225,51 @@ } private void elementSetup(MapElement ms, GType gt, Element element) { + ms.setName(element.getName()); 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(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); } } Index: src/uk/me/parabola/mkgmap/main/MapMaker.java =================================================================== --- src/uk/me/parabola/mkgmap/main/MapMaker.java (revision 180) +++ src/uk/me/parabola/mkgmap/main/MapMaker.java (working copy) @@ -278,26 +278,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/build/Locator.java =================================================================== --- src/uk/me/parabola/mkgmap/build/Locator.java (revision 0) +++ src/uk/me/parabola/mkgmap/build/Locator.java (revision 0) @@ -0,0 +1,362 @@ +/* + * 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 java.util.Collection; +import uk.me.parabola.mkgmap.general.MapPoint; +import uk.me.parabola.imgfmt.app.Coord; +import java.util.Vector; + +public class Locator { + + private final MapPointFastFindMap cityMap = new MapPointFastFindMap(); + private final MapPointFastFindMap fuzzyCityMap = new MapPointFastFindMap(); + private final java.util.Vector<MapPoint> placesMap = new Vector<MapPoint>(); + + private LocatorConfig locConfig = new LocatorConfig(); + + static double totalTime = 0; + static long totalFinds = 0; + + public void addLocation(MapPoint p) + { + resolveIsInInfo(p); // Preprocess the is_in field + + if(p.getCity() != null) + { + // All cities I have more detailed info about. + // In Germany, Austria and Swizerland this are real independet communites Gemeinden + + cityMap.put(p.getCity(),p); + fuzzyCityMap.put(fuzzyDecode(p.getCity()),p); + } + else + { + // All other places which do not seam to be a real city has to resolved later + placesMap.add(p); + } + } + + 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; + } + + private MapPoint findCity(MapPoint place, boolean fuzzy) + { + MapPoint near = null; + Double minDist = Double.MAX_VALUE; + Collection <MapPoint> nextCityList = null; + boolean checker = false; + + 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 (checker) // Some debug output to find suspicios relations + { + + if(near != null && minDist > 30000) + { + System.out.println(place.getName() + " -> " + near.getName() + + " " + (minDist/1000.0) + " km " + place.getIsIn()); + if(nextCityList != null) + System.out.println("Number of cities with this name: " + nextCityList.size()); + } + + if(near == null) + { + System.out.println("CAN't locate " + place.getName() + " " + place.getIsIn()); + } + + if(near != null && fuzzy) + { + System.out.println(place.getName() + " may belong to " + near.getName()); + } + } + } + + return near; + } + + public void resolve() { + + //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(near != null) + { + place.setCity(near.getCity()); + place.setZip(near.getZip()); + } + else if ((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((runCount + 1) == maxRuns) + place.setCity(place.getName()); + } + + if(near == null) + unresCount++; + + } + } + + for (int i = 0; i < placesMap.size(); i++) + { + MapPoint place = placesMap.get(i); + + if (place != null && place.getCity() != null) + { + cityMap.put(place.getName(),place); + fuzzyCityMap.put(fuzzyDecode(place.getName()),place); + placesMap.set(i, null); + } + } + + runCount++; + + //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"); + + return (decodeString); + } + +} + Index: src/uk/me/parabola/mkgmap/build/MapBuilder.java =================================================================== --- src/uk/me/parabola/mkgmap/build/MapBuilder.java (revision 180) +++ src/uk/me/parabola/mkgmap/build/MapBuilder.java (working copy) @@ -27,6 +27,8 @@ 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,9 +85,11 @@ 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; @@ -93,6 +97,7 @@ private String countryAbbr = "GBR"; private String regionName; private String regionAbbr; + private int poiDisplayFlags = 0; public MapBuilder() { regionName = null; @@ -123,6 +128,7 @@ if(regionName != null) region = lblFile.createRegion(country, regionName, regionAbbr); + processCities(map, src); processPOIs(map, src); //preProcessRoads(map, src); processOverviews(map, src); @@ -157,52 +163,192 @@ } /** - * 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); + locator.resolve(); // Try to fill missing information that include search of next city + + for (MapPoint p : src.getPoints()) + { + if(p.isCity() && p.getName() != null) + { + + String cityName = p.getName(); + + //System.out.println(s); + + 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; + 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) + { + + 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.findNextPoint(p); + + if(nextCity != null) + { + guessed = true; + + if (CountryStr == null) CountryStr = nextCity.getCountry(); + if (RegionStr == null) RegionStr = nextCity.getRegion(); + + if(ZipStr == null) ZipStr = nextCity.getZip(); + if(CityStr == null) CityStr = nextCity.getCity(); + + } + } + + if(CountryStr != null && checkedForPoiDispFlag == false) + { + // Different countries require different address notation + + poiDisplayFlags = locator.getPOIDispFlag(CountryStr); + checkedForPoiDispFlag = true; + } + + + if(CityStr != null && p.isRoadNamePOI()) + { + // 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) + { + Label streetName = lbl.newLabel("FIXME 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 +515,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 +610,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 (revision 0) +++ src/uk/me/parabola/mkgmap/build/MapPointFastFindMap.java (revision 0) @@ -0,0 +1,198 @@ +/* + * 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 + * It 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.List; +import java.util.Vector; +import uk.me.parabola.mkgmap.general.MapPoint; +import uk.me.parabola.imgfmt.app.Coord; + +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 (revision 0) +++ src/uk/me/parabola/mkgmap/build/LocatorConfig.java (revision 0) @@ -0,0 +1,290 @@ +/* + * 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 org.xml.sax.*; +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("resources/LocatorConfig.xml"); + } + + private void loadConfig(String fileName) + { + try + { + DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + + Document document = builder.parse(fileName); + + 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/reader/polish/PolishMapDataSource.java =================================================================== --- src/uk/me/parabola/mkgmap/reader/polish/PolishMapDataSource.java (revision 180) +++ src/uk/me/parabola/mkgmap/reader/polish/PolishMapDataSource.java (working copy) @@ -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/general/MapElement.java =================================================================== --- src/uk/me/parabola/mkgmap/general/MapElement.java (revision 180) +++ src/uk/me/parabola/mkgmap/general/MapElement.java (working copy) @@ -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 (revision 180) +++ src/uk/me/parabola/mkgmap/general/MapPoint.java (working copy) @@ -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: resources/LocatorConfig.xml =================================================================== --- resources/LocatorConfig.xml (revision 0) +++ resources/LocatorConfig.xml (revision 0) @@ -0,0 +1,35 @@ +<?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> + </country> + <country name="Österreich" abr="AUT" geodb="1" poiDispFlag="0xc"> + <variant>Austria</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> + </country> + <country name="Italia" abr="ITA" regionOffset="2"> + <variant>Italy</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> \ No newline at end of file Index: osm2mp.pl =================================================================== --- osm2mp.pl (revision 160) +++ osm2mp.pl (working copy) @@ -228,8 +228,7 @@ my $id; my $latlon; -my ($poi, $poiname, $ZipCode, $CityName, $StreetDesc, $HouseNumber, $isIn, $phone); -my ($CountryName, $RegionName); +my ($poi, $poiname); my $nameprio = 99; while (<IN>) { @@ -243,41 +242,18 @@ $poi = ""; $poiname = ""; - $ZipCode = ""; - $CityName = ""; - $StreetDesc = ""; - $HouseNumber = ""; - $isIn = ""; - $phone = ""; $nameprio = 99; - $CountryName = ""; - $RegionName = ""; next; } if ( /\<tag/ ) { - /^.*k=["'](.*)["'].*v=["'](.*)["'].*$/; + /^.*k=["'](.*)["'].*v=["'](.*)["'].*$/; $poi = "$1=$2" if ($poitype{"$1=$2"}); my $tagprio = indexof(\@nametagarray, $1); if ($tagprio>=0 && $tagprio<$nameprio) { $poiname = convert_string ($2); $nameprio = $tagprio; } - - #printf STDERR "$1 $2\n" if ( "$1" ne "created_by"); - - $CountryName = convert_string($2) if ( "$1" eq "is_in:country"); - $RegionName = convert_string($2) if ( "$1" eq "is_in:county"); - $isIn = convert_string($2) if ( "$1" eq "is_in"); - $ZipCode = convert_string($2) if ( "$1" eq "openGeoDB:postal_codes"); - $ZipCode = convert_string($2) if ( "$1" eq "addr:postcode" ); - $CityName = convert_string($2) if ( "$1" eq "addr:city" ); - #$CityName = convert_string($2) if ( "$1" eq "openGeoDB:name" ); - $CityName = convert_string($2) if ( "$1" eq "openGeoDB:sort_name" ); - $StreetDesc = convert_string($2) if ( "$1" eq "addr:street" ); - $HouseNumber = convert_string($2) if ( "$1" eq "addr:housenumber" ); - $phone = convert_string($2) if ( "$1" eq "phone" ); - next; } @@ -294,14 +270,6 @@ printf "EndLevel=%d\n", $type[2] if ($type[2] > $type[1]); printf "City=Y\n", if ($type[3]); print "Label=$poiname\n" if ($poiname); - printf "ZipCode=$ZipCode\n", if ($ZipCode); - printf "CityName=$CityName\n", if ($CityName); - printf "StreetDesc=$StreetDesc\n", if ($StreetDesc); - printf "HouseNumber=$HouseNumber\n", if ($HouseNumber); - printf "is_in=$isIn\n", if ($isIn); - printf "Phone=$phone\n", if ($phone); - printf "CountryName=$CountryName\n", if ($CountryName); - printf "RegionName=$RegionName\n", if ($RegionName); print "[END]\n\n"; } }