diff --git a/build.xml b/build.xml
index aea1ad1..1095e1d 100644
--- a/build.xml
+++ b/build.xml
@@ -80,6 +80,7 @@
 			<fileset dir="${resources}">
 				<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/**"/>
diff --git a/resources/LocatorConfig.xml b/resources/LocatorConfig.xml
new file mode 100644
index 0000000..df45b33
--- /dev/null
+++ b/resources/LocatorConfig.xml
@@ -0,0 +1,39 @@
+<?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>
\ No newline at end of file
diff --git a/resources/help/en/options b/resources/help/en/options
index 9d12f08..7bfbacc 100644
--- a/resources/help/en/options
+++ b/resources/help/en/options
@@ -131,6 +131,32 @@ Misc options:
 	same area, you can see through this map and see the lower map too.
 	Useful for contour line maps among other things.
 
+--pois-addresses 
+	Add address / phone information to POIs. Address info is read
+	according to the "Karlsruhe" tagging schema. If no address
+	info is given name of next city is used. Automatic filling
+	could be extended using the "location-autofill" option.
+
+--location-autofill=''number''
+	Controls how country region information is gathered for
+	cities, streets and pois
+
+	0       (Default) Info of nearest city is taken. If no country
+		region info is present for next city the default
+		passed default country region is used. The country
+		region info is gathered by analysis of the cities
+		is_in tags.
+
+	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.
 
diff --git a/src/uk/me/parabola/imgfmt/app/lbl/City.java b/src/uk/me/parabola/imgfmt/app/lbl/City.java
index 7eb2840..1a858cb 100644
--- a/src/uk/me/parabola/imgfmt/app/lbl/City.java
+++ b/src/uk/me/parabola/imgfmt/app/lbl/City.java
@@ -30,7 +30,7 @@ public class City {
 	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 @@ public class City {
 	// 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 class City {
 	}
 
 	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;
diff --git a/src/uk/me/parabola/imgfmt/app/lbl/LBLFile.java b/src/uk/me/parabola/imgfmt/app/lbl/LBLFile.java
index a6e5a57..40a5529 100644
--- a/src/uk/me/parabola/imgfmt/app/lbl/LBLFile.java
+++ b/src/uk/me/parabola/imgfmt/app/lbl/LBLFile.java
@@ -147,12 +147,16 @@ public class LBLFile extends ImgFile {
 	    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() {
diff --git a/src/uk/me/parabola/imgfmt/app/lbl/POIRecord.java b/src/uk/me/parabola/imgfmt/app/lbl/POIRecord.java
index 9a07a68..05f6216 100644
--- a/src/uk/me/parabola/imgfmt/app/lbl/POIRecord.java
+++ b/src/uk/me/parabola/imgfmt/app/lbl/POIRecord.java
@@ -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 @@ import uk.me.parabola.imgfmt.app.Label;
  * @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,12 +132,15 @@ public class POIRecord {
 	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 char cityIndex ;
-	private char zipIndex  ;
+	private Label complexPhoneNumber; // Used for numbers such as 221b
+	
+	private City city = null;
+	private char zipIndex  = 0;
 
 	private String phoneNumber;
 
@@ -60,14 +152,35 @@ public class POIRecord {
 		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 @@ public class POIRecord {
 		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,59 @@ public class POIRecord {
 			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 */
-
-		for (byte i = 0; i < 6; i++) {
-			mask = 1 << i;
-
-			if ((mask & POIGlobalFlags) == mask) {
-				if ((mask & usedFields) == mask)
-					flag |= (1 << j);
+		/* 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;
+			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,31 +288,33 @@ public class POIRecord {
 		int size = 3;
 		if (POIGlobalFlags != getPOIFlags())
 			size += 1;
-		if (streetName != null)
+		if (simpleStreetNumber.isUsed())		
+			size += simpleStreetNumber.getSize();
+		if (streetNumberName != null)
 			size += 3;
-		if (cityIndex > 0) 
+		if (simplePhoneNumber.isUsed())		
+			size += simplePhoneNumber.getSize();			
+		if (complexPhoneNumber != null)
+			size += 3;			
+		if (streetName != null)
+			size += 3;	
+		if (city != null) 
 		{
 			/*
-			  depending on how many cities are in the LBL block we have
-			  to write one or two bytes 
+			  depending on how many cities are in the LBL
+			  block we have to write one or two bytes
 			*/
 		
-			if(numCities > 255)
-	  		  size += 2;
-			else
-			  size += 1;
+			size += (numCities > 255)? 2 : 1;
 		}
 		if (zipIndex > 0)
 		{
 			/*
-			  depending on how many zips are in the LBL block we have
-			  to write one or two bytes 
+			  depending on how many zips are in the LBL
+			  block we have to write one or two bytes
 			*/
 		
-			if(numZips > 255)
-			   size += 2;						
-			else
-			   size += 1;
+			size += (numZips > 255)? 2 : 1;
 		}
 		return size;
 	}
diff --git a/src/uk/me/parabola/imgfmt/app/lbl/PlacesFile.java b/src/uk/me/parabola/imgfmt/app/lbl/PlacesFile.java
index 7375784..d734e58 100644
--- a/src/uk/me/parabola/imgfmt/app/lbl/PlacesFile.java
+++ b/src/uk/me/parabola/imgfmt/app/lbl/PlacesFile.java
@@ -20,6 +20,8 @@ import java.util.ArrayList;
 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 @@ import uk.me.parabola.imgfmt.app.Label;
 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 @@ public class PlacesFile {
 			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,126 @@ public class PlacesFile {
 	}
 
 	Country createCountry(String name, String abbr) {
-		Country c = new Country(countries.size()+1);
-
+	
 		String s = abbr != null ? name + (char)0x1d + abbr : name;
-
-		Label l = lblFile.newLabel(s);
-		c.setLabel(l);
-
-		countries.put(name, c);
+			
+		Country c = countries.get(s);
+	
+		if(c == null)
+		{
+			c = new Country(countries.size()+1);
+
+			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);
+
+			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);
+		}
 
-		cities.add(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);
+
+			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);
+		}
 
-		cities.add(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 +223,9 @@ public class PlacesFile {
 	}
 
 	void allPOIsDone() {
+
+		sortCities();
+
 		poisClosed = true;
 
 		byte poiFlags = 0;
diff --git a/src/uk/me/parabola/imgfmt/app/map/Map.java b/src/uk/me/parabola/imgfmt/app/map/Map.java
index b976860..33a24b6 100644
--- a/src/uk/me/parabola/imgfmt/app/map/Map.java
+++ b/src/uk/me/parabola/imgfmt/app/map/Map.java
@@ -225,6 +225,14 @@ public class Map implements InternalFiles, Configurable {
 		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);
 	}
diff --git a/src/uk/me/parabola/imgfmt/app/trergn/TREFile.java b/src/uk/me/parabola/imgfmt/app/trergn/TREFile.java
index 7e6655e..6456cb9 100644
--- a/src/uk/me/parabola/imgfmt/app/trergn/TREFile.java
+++ b/src/uk/me/parabola/imgfmt/app/trergn/TREFile.java
@@ -315,6 +315,10 @@ public class TREFile extends ImgFile implements Configurable {
 		header.setBounds(area);
 	}
 
+	public void setPoiDisplayFlags(byte b) {
+		header.setPoiDisplayFlags(b);
+	}	
+
 	public String[] getCopyrights() {
 		if (!isReadable())
 			throw new IllegalStateException("not open for reading");
diff --git a/src/uk/me/parabola/imgfmt/app/trergn/TREHeader.java b/src/uk/me/parabola/imgfmt/app/trergn/TREHeader.java
index f279d17..7751495 100644
--- a/src/uk/me/parabola/imgfmt/app/trergn/TREHeader.java
+++ b/src/uk/me/parabola/imgfmt/app/trergn/TREHeader.java
@@ -236,6 +236,10 @@ public class TREHeader extends CommonHeader {
 	public void setMapId(int id) {
 		mapId = id;
 	}
+	
+	public void setPoiDisplayFlags(byte poiDisplayFlags) {
+		this.poiDisplayFlags = poiDisplayFlags;
+	}	
 
 	public int getMapInfoSize() {
 		return mapInfoSize;
diff --git a/src/uk/me/parabola/mkgmap/build/Locator.java b/src/uk/me/parabola/mkgmap/build/Locator.java
new file mode 100644
index 0000000..fcc2ab2
--- /dev/null
+++ b/src/uk/me/parabola/mkgmap/build/Locator.java
@@ -0,0 +1,433 @@
+/*
+ * Copyright (C) 2009 Bernhard Heibler
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ * 
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ * 
+ *  The Locator tries to fill missing country, region, postal coude information
+ *
+ *	The algorithm works like this:
+ *
+ *	1. Step: Go through all cities an check if they have useful country region info
+ *	The best case is if the tags is_in:country and is_in:county are present thats easy.
+ *	Some cities have is_in information that can be used. We check for three different 
+ *	formats:
+ *
+ *	County, State, Country, Continent
+ *	County, State, Country
+ *	Continent, Country, State, County, ...
+ *
+ *	In "openGeoDb countries" this info is pretty reliable since it was imported from a 
+ *	external db into osm. Other countries have very sparse is_in info.
+ *
+ *	All this cities that have such info will end up in "city" list. All which lack 
+ *	such information in "location" list.
+ *
+ * 	2. Step: Go through the "location" list and check if the is_in info has some relations 
+ *	to the cities we have info about.
+ *
+ *	Especially hamlets often have no full is_in information. They only have one entry in 
+ *	is_in that points to the city they belong to. I will check if I can find the name 
+ *	of this city in the "City" list. If there are more with the same name I use the 
+ *	closest one. If we can't find the exact name I use fuzzy name search. Thats a
+ *	workaround for german umlaute since sometimes there are used in the tags and 
+ *	sometimes there are written as ue ae oe. 
+ *
+ *	3. Step: Do the same like in step 2 once again. This is used to support at least
+ *	one level of recursion in is_in relations.
+ *
+ *  If there is still no info found I use brute force and use the information from the
+ *	next city. Has to be used for countries with poor is_in tagging.
+ *
+ * Author: Bernhard Heibler
+ * Create date: 02-Jan-2009
+ */
+
+package uk.me.parabola.mkgmap.build;
+
+import java.util.Collection;
+import uk.me.parabola.mkgmap.general.MapPoint;
+import uk.me.parabola.imgfmt.app.Coord;
+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 > 0)
+		{
+			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);
+
+				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);		
+			}
+		}
+		else
+		{
+			if(p.getCity() == null)			// Without autofill city name is the name of the tag
+				p.setCity(p.getName());
+
+			cityMap.put(p.getCity(),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;
+	}
+	
+	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());
+			  	}
+
+	
+					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);
+	}
+		
+}
+
diff --git a/src/uk/me/parabola/mkgmap/build/LocatorConfig.java b/src/uk/me/parabola/mkgmap/build/LocatorConfig.java
new file mode 100644
index 0000000..b2a9985
--- /dev/null
+++ b/src/uk/me/parabola/mkgmap/build/LocatorConfig.java
@@ -0,0 +1,305 @@
+/*
+ * 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.net.*;
+
+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;
+	}
+
+
+
+		
+}
+
diff --git a/src/uk/me/parabola/mkgmap/build/MapBuilder.java b/src/uk/me/parabola/mkgmap/build/MapBuilder.java
index 5e73ca9..800865f 100644
--- a/src/uk/me/parabola/mkgmap/build/MapBuilder.java
+++ b/src/uk/me/parabola/mkgmap/build/MapBuilder.java
@@ -27,6 +27,8 @@ 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 +85,54 @@ public class MapBuilder implements Configurable {
 	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 = false;
+	private int	poiDisplayFlags = 0;
+
 	public MapBuilder() {
 		regionName = null;
 	}
 
 	public void config(EnhancedProperties props) {
+
 		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("pois-addresses", null) != null)
+			poiAddresses = true;
+
+		String autoFillPar = props.getProperty("location-autofill", null);
+
+		if(autoFillPar != null)
+		{
+			try
+			{
+				locationAutofillLevel = Integer.parseInt(autoFillPar);
+			}
+			catch (Exception e)
+			{
+				locationAutofillLevel = 1;
+			}
+		}
+
+		locator.setAutoFillLevel(locationAutofillLevel);
+
+
 	}
 
 	/**
@@ -123,6 +153,7 @@ public class MapBuilder implements Configurable {
 		if(regionName != null)
 			region = lblFile.createRegion(country, regionName, regionAbbr);
 
+		processCities(map, src);
 		processPOIs(map, src);
 		//preProcessRoads(map, src);
 		processOverviews(map, src);
@@ -157,52 +188,194 @@ public class MapBuilder implements Configurable {
 	}
 
 	/**
-	 * 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)
+			{
+
+				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 &&
+				 (p.isRoadNamePOI() || poiAddresses))
+			{		
+
+				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("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 +542,9 @@ public class MapBuilder implements Configurable {
 		// 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 +637,8 @@ public class MapBuilder implements Configurable {
 					// 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 {
diff --git a/src/uk/me/parabola/mkgmap/build/MapPointFastFindMap.java b/src/uk/me/parabola/mkgmap/build/MapPointFastFindMap.java
new file mode 100644
index 0000000..2f09c12
--- /dev/null
+++ b/src/uk/me/parabola/mkgmap/build/MapPointFastFindMap.java
@@ -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
diff --git a/src/uk/me/parabola/mkgmap/build/MapPointMultiMap.java b/src/uk/me/parabola/mkgmap/build/MapPointMultiMap.java
new file mode 100644
index 0000000..75bf6db
--- /dev/null
+++ b/src/uk/me/parabola/mkgmap/build/MapPointMultiMap.java
@@ -0,0 +1,67 @@
+/*
+ * 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.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
diff --git a/src/uk/me/parabola/mkgmap/general/MapElement.java b/src/uk/me/parabola/mkgmap/general/MapElement.java
index 400cb8b..c7e4df3 100644
--- a/src/uk/me/parabola/mkgmap/general/MapElement.java
+++ b/src/uk/me/parabola/mkgmap/general/MapElement.java
@@ -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 @@ public abstract class MapElement {
 
 	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 abstract class MapElement {
 	}
 
 	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.
diff --git a/src/uk/me/parabola/mkgmap/general/MapPoint.java b/src/uk/me/parabola/mkgmap/general/MapPoint.java
index 5e23134..51bf480 100644
--- a/src/uk/me/parabola/mkgmap/general/MapPoint.java
+++ b/src/uk/me/parabola/mkgmap/general/MapPoint.java
@@ -27,6 +27,7 @@ import uk.me.parabola.imgfmt.app.Coord;
 public class MapPoint extends MapElement {
 	private Coord location;
 	private MapPoint nearestCityPoint;
+	private boolean isRoadNamePoi = false;
 
 	public MapPoint() {
 	}
@@ -63,11 +64,11 @@ public class MapPoint extends MapElement {
 		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;
 	}
 }
diff --git a/src/uk/me/parabola/mkgmap/main/MapMaker.java b/src/uk/me/parabola/mkgmap/main/MapMaker.java
index d7ca724..8f0809f 100644
--- a/src/uk/me/parabola/mkgmap/main/MapMaker.java
+++ b/src/uk/me/parabola/mkgmap/main/MapMaker.java
@@ -277,26 +277,10 @@ public class MapMaker implements MapProcessor {
 		}
 
 		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;
diff --git a/src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java b/src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java
index c37db96..50034d8 100644
--- a/src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java
+++ b/src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java
@@ -237,10 +237,55 @@ public class StyledConverter implements OsmConverter {
 	}
 
 	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(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) {
diff --git a/src/uk/me/parabola/mkgmap/reader/polish/PolishMapDataSource.java b/src/uk/me/parabola/mkgmap/reader/polish/PolishMapDataSource.java
index ec60401..67eb78f 100644
--- a/src/uk/me/parabola/mkgmap/reader/polish/PolishMapDataSource.java
+++ b/src/uk/me/parabola/mkgmap/reader/polish/PolishMapDataSource.java
@@ -362,6 +362,23 @@ public class PolishMapDataSource extends MapperBasedMapDataSource implements Loa
 			} 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;
 		}
