Index: resources/styles/default/lines
===================================================================
--- resources/styles/default/lines	(revision 1890)
+++ resources/styles/default/lines	(working copy)
@@ -8,6 +8,28 @@
 # and http://wiki.openstreetmap.org/wiki/Mkgmap/help/Custom_styles
 # for more information.
 
+mkgmap:country!=* & addr:country=* { set mkgmap:country='${addr:country}' }
+mkgmap:country!=* & is_in:country=* { set mkgmap:country='${is_in:country}' }
+mkgmap:country!=* & mkgmap:admin_level2=* { set mkgmap:country='${mkgmap:admin_level2}' }
+ 
+mkgmap:region!=* & is_in:county=* { set mkgmap:region='${is_in:county}' } 
+mkgmap:region!=* & mkgmap:admin_level6=* { set mkgmap:region='${mkgmap:admin_level6}' } 
+mkgmap:region!=* & mkgmap:admin_level5=* { set mkgmap:region='${mkgmap:admin_level5}' } 
+mkgmap:region!=* & mkgmap:admin_level4=* { set mkgmap:region='${mkgmap:admin_level4}' } 
+mkgmap:region!=* & mkgmap:admin_level3=* { set mkgmap:region='${mkgmap:admin_level3}' } 
+
+mkgmap:city!=* & openGeoDB:name=* { set mkgmap:city='${openGeoDB:name}' } 
+mkgmap:city!=* & mkgmap:admin_level8=* { set mkgmap:city='${mkgmap:admin_level8}' } 
+mkgmap:city!=* & mkgmap:admin_level7=* { set mkgmap:city='${mkgmap:admin_level7}' } 
+mkgmap:city!=* & mkgmap:admin_level9=* { set mkgmap:city='${mkgmap:admin_level9}' } 
+mkgmap:city!=* & mkgmap:admin_level10=* { set mkgmap:city='${mkgmap:admin_level10}' } 
+
+mkgmap:postal_code!=* & addr:postcode=* { set mkgmap:postal_code='${addr:postcode}' } 
+mkgmap:postal_code!=* & openGeoDB:postal_codes=* { set mkgmap:postal_code='${openGeoDB:postal_codes}' } 
+mkgmap:postal_code!=* & mkgmap:postcode=* { set mkgmap:postal_code='${mkgmap:postalcode}' } 
+
+
+
 aeroway=runway [0x27 resolution 20]
 aeroway=taxiway [0x27 resolution 24]
 
Index: resources/styles/default/polygons
===================================================================
--- resources/styles/default/polygons	(revision 1890)
+++ resources/styles/default/polygons	(working copy)
@@ -8,6 +8,26 @@
 # and http://wiki.openstreetmap.org/wiki/Mkgmap/help/Custom_styles
 # for more information.
 
+mkgmap:country!=* & addr:country=* { set mkgmap:country='${addr:country}' }
+mkgmap:country!=* & is_in:country=* { set mkgmap:country='${is_in:country}' }
+mkgmap:country!=* & mkgmap:admin_level2=* { set mkgmap:country='${mkgmap:admin_level2}' }
+ 
+mkgmap:region!=* & is_in:county=* { set mkgmap:region='${is_in:county}' } 
+mkgmap:region!=* & mkgmap:admin_level6=* { set mkgmap:region='${mkgmap:admin_level6}' } 
+mkgmap:region!=* & mkgmap:admin_level5=* { set mkgmap:region='${mkgmap:admin_level5}' } 
+mkgmap:region!=* & mkgmap:admin_level4=* { set mkgmap:region='${mkgmap:admin_level4}' } 
+mkgmap:region!=* & mkgmap:admin_level3=* { set mkgmap:region='${mkgmap:admin_level3}' } 
+
+mkgmap:city!=* & openGeoDB:name=* { set mkgmap:city='${openGeoDB:name}' } 
+mkgmap:city!=* & mkgmap:admin_level8=* { set mkgmap:city='${mkgmap:admin_level8}' } 
+mkgmap:city!=* & mkgmap:admin_level7=* { set mkgmap:city='${mkgmap:admin_level7}' } 
+mkgmap:city!=* & mkgmap:admin_level9=* { set mkgmap:city='${mkgmap:admin_level9}' } 
+mkgmap:city!=* & mkgmap:admin_level10=* { set mkgmap:city='${mkgmap:admin_level10}' } 
+
+mkgmap:postal_code!=* & addr:postcode=* { set mkgmap:postal_code='${addr:postcode}' } 
+mkgmap:postal_code!=* & openGeoDB:postal_codes=* { set mkgmap:postal_code='${openGeoDB:postal_codes}' } 
+mkgmap:postal_code!=* & mkgmap:postcode=* { set mkgmap:postal_code='${mkgmap:postalcode}' } 
+
 aeroway=airport [0x07 resolution 18]
 aeroway=aerodrome [0x07 resolution 18]
 aeroway=helipad [0x0e resolution 20]
Index: resources/styles/default/points
===================================================================
--- resources/styles/default/points	(revision 1890)
+++ resources/styles/default/points	(working copy)
@@ -8,6 +8,27 @@
 # and http://wiki.openstreetmap.org/wiki/Mkgmap/help/Custom_styles
 # for more information.
 
+mkgmap:country!=* & addr:country=* { set mkgmap:country='${addr:country}' }
+mkgmap:country!=* & is_in:country=* { set mkgmap:country='${is_in:country}' }
+mkgmap:country!=* & mkgmap:admin_level2=* { set mkgmap:country='${mkgmap:admin_level2}' }
+ 
+mkgmap:region!=* & is_in:county=* { set mkgmap:region='${is_in:county}' } 
+mkgmap:region!=* & mkgmap:admin_level6=* { set mkgmap:region='${mkgmap:admin_level6}' } 
+mkgmap:region!=* & mkgmap:admin_level5=* { set mkgmap:region='${mkgmap:admin_level5}' } 
+mkgmap:region!=* & mkgmap:admin_level4=* { set mkgmap:region='${mkgmap:admin_level4}' } 
+mkgmap:region!=* & mkgmap:admin_level3=* { set mkgmap:region='${mkgmap:admin_level3}' } 
+
+mkgmap:city!=* & openGeoDB:name=* { set mkgmap:city='${openGeoDB:name}' } 
+mkgmap:city!=* & mkgmap:admin_level8=* { set mkgmap:city='${mkgmap:admin_level8}' } 
+mkgmap:city!=* & mkgmap:admin_level7=* { set mkgmap:city='${mkgmap:admin_level7}' } 
+mkgmap:city!=* & mkgmap:admin_level9=* { set mkgmap:city='${mkgmap:admin_level9}' } 
+mkgmap:city!=* & mkgmap:admin_level10=* { set mkgmap:city='${mkgmap:admin_level10}' } 
+
+mkgmap:postal_code!=* & addr:postcode=* { set mkgmap:postal_code='${addr:postcode}' } 
+mkgmap:postal_code!=* & openGeoDB:postal_codes=* { set mkgmap:postal_code='${openGeoDB:postal_codes}' } 
+mkgmap:postal_code!=* & mkgmap:postcode=* { set mkgmap:postal_code='${mkgmap:postalcode}' } 
+
+
 internet_access=yes { name 'Internet ${name}' | 'Internet' } [0x2f12 resolution 24 continue]
 internet_access=* & internet_access!=no & internet_access!=yes { name 'Internet(${internet_access}) ${name|def:}' } [0x2f12 resolution 24 continue]
 
Index: src/uk/me/parabola/util/ElementQuadTreeNode.java
===================================================================
--- src/uk/me/parabola/util/ElementQuadTreeNode.java	(revision 0)
+++ src/uk/me/parabola/util/ElementQuadTreeNode.java	(revision 0)
@@ -0,0 +1,370 @@
+package uk.me.parabola.util;
+
+import java.awt.Rectangle;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import uk.me.parabola.imgfmt.app.Area;
+import uk.me.parabola.imgfmt.app.Coord;
+import uk.me.parabola.log.Logger;
+import uk.me.parabola.mkgmap.reader.osm.Element;
+import uk.me.parabola.mkgmap.reader.osm.Node;
+import uk.me.parabola.mkgmap.reader.osm.Relation;
+import uk.me.parabola.mkgmap.reader.osm.Way;
+
+public final class ElementQuadTreeNode {
+
+	private static final Logger log = Logger.getLogger(ElementQuadTreeNode.class);
+
+	private static final int MAX_POINTS = 1000;
+
+	private MultiHashMap<Coord, Element> points;
+	private final Area bounds;
+	private Area coveredBounds;
+
+	public Area getCoveredBounds() {
+		return coveredBounds;
+	}
+
+	private ElementQuadTreeNode[] children;
+
+	public static final class ElementQuadTreePolygon {
+		private final java.awt.geom.Area javaArea;
+		private final Area bbox;
+
+		public ElementQuadTreePolygon(java.awt.geom.Area javaArea) {
+			this.javaArea = javaArea;
+			Rectangle bboxRect = javaArea.getBounds();
+			bbox = new Area(bboxRect.y, bboxRect.x, bboxRect.y
+					+ bboxRect.height, bboxRect.x + bboxRect.width);
+		}
+
+		public ElementQuadTreePolygon(List<Coord> points) {
+			this(new java.awt.geom.Area(Java2DConverter.createPolygon(points)));
+		}
+
+		public ElementQuadTreePolygon(Collection<List<Coord>> polygonList) {
+			this.javaArea = new java.awt.geom.Area();
+			for (List<Coord> polygon : polygonList) {
+				javaArea.add(new java.awt.geom.Area(Java2DConverter
+						.createPolygon(polygon)));
+			}
+			Rectangle bboxRect = javaArea.getBounds();
+			bbox = new Area(bboxRect.y, bboxRect.x, bboxRect.y
+					+ bboxRect.height, bboxRect.x + bboxRect.width);
+		}
+
+		public Area getBbox() {
+			return bbox;
+		}
+
+		public java.awt.geom.Area getArea() {
+			return javaArea;
+		}
+	}
+
+	public ElementQuadTreeNode(Area bounds) {
+		this(bounds, Collections.<Element> emptyList());
+	}
+	
+	
+	public ElementQuadTreeNode(Collection<Element> elements) {
+		this.children = null;
+
+		int minLat = Integer.MAX_VALUE;
+		int maxLat = Integer.MIN_VALUE;
+		int minLong = Integer.MAX_VALUE;
+		int maxLong = Integer.MIN_VALUE;
+
+		this.points = new MultiHashMap<Coord, Element>();
+		for (Element el : elements) {
+			Collection<Coord> coords = null;
+			if (el instanceof Relation) {
+				continue;
+			} else if (el instanceof Way) {
+				Way w = (Way) el;
+				if (w.isClosed()) {
+					coords = w.getPoints().subList(0, w.getPoints().size()-1);	
+				} else {
+					coords = w.getPoints();
+				}
+			} else if (el instanceof Node) {
+				coords = Collections.singleton(((Node) el).getLocation());
+			}
+			
+			for (Coord c : coords) {
+				if (c.getLatitude() < minLat) {
+					minLat = c.getLatitude();
+				}
+				if (c.getLatitude() > maxLat) {
+					maxLat = c.getLatitude();
+				}
+				if (c.getLongitude() < minLong) {
+					minLong = c.getLongitude();
+				}
+				if (c.getLongitude() > maxLong) {
+					maxLong = c.getLongitude();
+				}
+				points.add(c, el);
+			}
+
+		}
+		coveredBounds = new Area(minLat, minLong, maxLat, maxLong);
+		this.bounds = coveredBounds;
+		
+		if (points.size() > MAX_POINTS) {
+			split();
+		} 
+	}
+	
+	public int getDepth() {
+		if (isLeaf()) {
+			return 1;
+		} else {
+			int maxDepth = 0;
+			for (ElementQuadTreeNode node :children) {
+				maxDepth = Math.max(node.getDepth(), maxDepth);
+			}
+			return maxDepth+1;
+		}
+	}
+	
+//	public void outputBounds(String basename, int level) {
+////		if (level > 8 ) {
+////			return;
+////		}
+//		
+//		if (isLeaf()) {
+//			GpxCreator.createAreaGpx(basename+level+"_"+points.keySet().size(), coveredBounds);
+////			GpxCreator.createGpx(basename+level+"p"+points.keySet().size(), new ArrayList<Coord>(), new ArrayList<Coord>(points.keySet()));
+//		} else {
+////			GpxCreator.createAreaGpx(basename+level, coveredBounds);
+//			int i = 0;
+//			for (ElementQuadTreeNode node : children) {
+//				i++;
+//				node.outputBounds(basename+i+"_", level+1);
+//			}
+//		}
+//	}
+
+	public ElementQuadTreeNode(Area bounds, Collection<Element> elements) {
+		this.bounds = bounds;
+		this.children = null;
+
+		int minLat = Integer.MAX_VALUE;
+		int maxLat = Integer.MIN_VALUE;
+		int minLong = Integer.MAX_VALUE;
+		int maxLong = Integer.MIN_VALUE;
+
+		this.points = new MultiHashMap<Coord, Element>();
+		for (Element el : elements) {
+			Collection<Coord> coords = null;
+			if (el instanceof Relation) {
+				continue;
+			} else if (el instanceof Way) {
+				Way w = (Way) el;
+				if (w.isClosed()) {
+					coords = w.getPoints().subList(0, w.getPoints().size()-1);	
+				} else {
+					coords = w.getPoints();
+				}
+			} else if (el instanceof Node) {
+				coords = Collections.singleton(((Node) el).getLocation());
+			}
+			
+			for (Coord c : coords) {
+				if (c.getLatitude() < minLat) {
+					minLat = c.getLatitude();
+				}
+				if (c.getLatitude() > maxLat) {
+					maxLat = c.getLatitude();
+				}
+				if (c.getLongitude() < minLong) {
+					minLong = c.getLongitude();
+				}
+				if (c.getLongitude() > maxLong) {
+					maxLong = c.getLongitude();
+				}
+				points.add(c, el);
+			}
+
+		}
+		if (minLat > maxLat || minLong > maxLong) {
+			coveredBounds = new Area(bounds.getMinLat(), bounds.getMinLong(), bounds.getMinLat()+1, bounds.getMinLong()+1);
+		} else {
+			coveredBounds = new Area(minLat, minLong, maxLat, maxLong);
+		}
+
+		if (points.size() > MAX_POINTS) {
+			split();
+		} 
+	}
+
+	public Area getBounds() {
+		return this.bounds;
+	}
+	
+	public Rectangle getRectBounds() {
+		return new Rectangle(bounds.getMinLong(), bounds.getMinLat(), bounds.getWidth(), bounds.getHeight());
+	}
+
+	private boolean add(Coord c, Element element) {
+		if (coveredBounds == null) {
+			coveredBounds = new Area(c.getLatitude(), c.getLongitude(),
+					c.getLatitude(), c.getLongitude());
+		} else if (coveredBounds.contains(c) == false) {
+			coveredBounds = new Area(Math.min(coveredBounds.getMinLat(),
+					c.getLatitude()), Math.min(coveredBounds.getMinLong(),
+					c.getLongitude()), Math.max(coveredBounds.getMaxLat(),
+					c.getLatitude()), Math.max(coveredBounds.getMaxLong(),
+					c.getLongitude()));
+		}
+		if (isLeaf()) {
+			points.add(c,element);
+			if (points.size() > MAX_POINTS)
+				split();
+			return true;
+		} else {
+			for (ElementQuadTreeNode nodes : children) {
+				if (nodes.getBounds().contains(c)) {
+					return nodes.add(c, element);
+				}
+			}
+			return false;
+		}
+	}
+
+	public boolean add(Element c) {
+		if (c instanceof Relation) {
+			log.error("Relations are not supported by this quadtree implementation. Skipping relation "
+					+ c.toBrowseURL());
+			return false;
+		} else if (c instanceof Way) {
+			// add all points to the tree
+			Way w = (Way) c;
+			List<Coord> points;
+			if (w.isClosed()) {
+				points = w.getPoints().subList(0, w.getPoints().size()-1);	
+			} else {
+				points = w.getPoints();
+			}
+			
+			boolean allOk = true;
+			for (Coord cp : points) {
+				allOk = add(cp, c) && allOk;
+			}
+			return allOk;
+		} else if (c instanceof Node) {
+			return add(((Node) c).getLocation(), c);
+		} else {
+			log.error("Unsupported element type: "+c);
+			return false;
+		}
+	}
+
+	public Set<Element> get(Area bbox, Set<Element> resultList) {
+		if (isLeaf()) {
+			if (bbox.getMinLat() <= coveredBounds.getMinLat()
+					&& bbox.getMaxLat() >= coveredBounds.getMaxLat()
+					&& bbox.getMinLong() <= coveredBounds.getMinLong()
+					&& bbox.getMaxLong() >= coveredBounds.getMaxLong()) {
+
+				// the bounding box is contained completely in the bbox
+				// => add all points without further check
+				for (List<Element> elem : points.values())
+					resultList.addAll(elem);
+			} else {
+				// check each point
+				for (Entry<Coord, List<Element>> e : points.entrySet()) {
+					if (bbox.contains(e.getKey())) {
+						resultList.addAll(e.getValue());
+					}
+				}
+			}
+		} else {
+			for (ElementQuadTreeNode child : children) {
+				if (bbox.intersects(child.getCoveredBounds())) {
+					resultList = child.get(bbox, resultList);
+				}
+			}
+		}
+		return resultList;
+	}
+
+	public Set<Element> get(ElementQuadTreePolygon polygon,
+			Set<Element> resultList) {
+		if (polygon.getBbox().intersects(getBounds())) {
+			if (isLeaf()) {
+				for (Entry<Coord, List<Element>> e : points.entrySet()) {
+					if (polygon.getArea().contains(e.getKey().getLongitude(),
+							e.getKey().getLatitude())) {
+						resultList.addAll(e.getValue());
+					}
+				}
+			} else {
+				for (ElementQuadTreeNode child : children) {
+					if (polygon.getArea().intersects(child. getRectBounds())) {
+						java.awt.geom.Area subArea = (java.awt.geom.Area) polygon
+								.getArea().clone();
+						subArea.intersect(createArea(child.getBounds()));
+						if (subArea.isEmpty() == false)
+							child.get(new ElementQuadTreePolygon(subArea), resultList);
+					}
+				}
+			}
+		}
+		return resultList;
+
+	}
+
+	private java.awt.geom.Area createArea(Area bbox) {
+		return new java.awt.geom.Area(new Rectangle(bbox.getMinLong(),
+				bbox.getMinLat(), bbox.getWidth(), bbox.getHeight()));
+	}
+
+	public boolean isLeaf() {
+		return points != null;
+	}
+
+	private void split() {
+		if (bounds.getHeight() <= 5 || bounds.getWidth() <= 5) {
+			log.error("Do not split more due to too small bounds: "+bounds);
+			return;
+		}
+
+		int halfLat = (bounds.getMinLat() + bounds.getMaxLat()) / 2;
+		int halfLong = (bounds.getMinLong() + bounds.getMaxLong()) / 2;
+		children = new ElementQuadTreeNode[4];
+
+		Area swBounds = new Area(bounds.getMinLat(), bounds.getMinLong(),
+				halfLat, halfLong);
+		Area nwBounds = new Area(halfLat, bounds.getMinLong(),
+				bounds.getMaxLat(), halfLong);
+		Area seBounds = new Area(bounds.getMinLat(), halfLong, halfLat,
+				bounds.getMaxLong());
+		Area neBounds = new Area(halfLat, halfLong, bounds.getMaxLat(),
+				bounds.getMaxLong());
+		
+		children[0] = new ElementQuadTreeNode(swBounds);
+		children[1] = new ElementQuadTreeNode(nwBounds);
+		children[2] = new ElementQuadTreeNode(seBounds);
+		children[3] = new ElementQuadTreeNode(neBounds);
+		
+		MultiHashMap<Coord, Element> copyPoints = points;
+		points = null;
+		for (Entry<Coord, List<Element>> c : copyPoints.entrySet()) {
+			for (Element el : c.getValue())
+				add(c.getKey(), el);
+		}
+	}
+
+	public void clear() {
+		this.children = null;
+		points = new MultiHashMap<Coord, Element>();
+		coveredBounds = new Area(Integer.MAX_VALUE, Integer.MAX_VALUE,
+				Integer.MIN_VALUE, Integer.MIN_VALUE);
+	}
+}
Index: src/uk/me/parabola/util/MultiHashMap.java
===================================================================
--- src/uk/me/parabola/util/MultiHashMap.java	(revision 1890)
+++ src/uk/me/parabola/util/MultiHashMap.java	(working copy)
@@ -21,7 +21,7 @@
 	* @return a list of values for the given keys or the empty list of no such
 	*         value exist.
 	*/
-	public List<V> get(K key) {
+	public List<V> get(Object key) {
 		List<V> result = super.get(key);
 		return result == null ? emptyList : result;
 	}
Index: src/uk/me/parabola/util/ElementQuadTree.java
===================================================================
--- src/uk/me/parabola/util/ElementQuadTree.java	(revision 0)
+++ src/uk/me/parabola/util/ElementQuadTree.java	(revision 0)
@@ -0,0 +1,151 @@
+package uk.me.parabola.util;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import uk.me.parabola.imgfmt.app.Area;
+import uk.me.parabola.imgfmt.app.Coord;
+import uk.me.parabola.mkgmap.reader.osm.Element;
+import uk.me.parabola.util.ElementQuadTreeNode.ElementQuadTreePolygon;
+
+public class ElementQuadTree {
+
+	private final ElementQuadTreeNode root;
+	private long itemCount;
+
+	public ElementQuadTree(Area bbox) {
+		this.root = new ElementQuadTreeNode(bbox);
+		this.itemCount = 0;
+	}
+
+	public ElementQuadTree(Collection<Element> elements) {
+		this.root = new ElementQuadTreeNode(elements);
+		this.itemCount = 0;
+	}
+	
+//	public void outputBounds(String basename) {
+//		root.outputBounds(basename, 0);
+//	}
+	
+	public boolean addAll(Collection<Element> elements) {
+		boolean oneAdded = false;
+		for (Element element : elements) {
+			oneAdded = add(element) | oneAdded;
+		}
+		return oneAdded;
+	}
+
+	public boolean add(Element element) {
+
+		boolean added = root.add(element);
+		if (added) {
+			itemCount++;
+		}
+		return added;
+	}
+
+	public Set<Element> get(Area bbox) {
+		return root.get(bbox, new HashSet<Element>());
+	}
+
+	public Set<Element> get(java.awt.geom.Area polygon) {
+		return root.get(new ElementQuadTreePolygon(polygon), new HashSet<Element>());
+	}
+	
+	public Set<Element> get(Collection<List<Coord>> polygons) {
+		return root.get(new ElementQuadTreePolygon(polygons),
+				new HashSet<Element>());
+	}
+
+	public int getDepth() {
+		return root.getDepth();
+	}
+	
+	public Set<Element> get(List<Coord> polygon) {
+		if (polygon.size() < 3) {
+			return new HashSet<Element>();
+		}
+		if (polygon.get(0).equals(polygon.get(polygon.size() - 1)) == false) {
+			return new HashSet<Element>();
+		}
+		return root.get(new ElementQuadTreePolygon(polygon),
+				new HashSet<Element>());
+	}
+
+//	public Set<Element> get(List<Coord> polygon, int offset) {
+//		if (polygon.size() < 3) {
+//			return new HashSet<Element>();
+//		}
+//		if (polygon.get(0).equals(polygon.get(polygon.size() - 1)) == false) {
+//			return null;
+//		}
+//		Set<Element> points = root.get(new QuadTreePolygon(polygon),
+//				new HashSet<Element>());
+//		if (offset > 0) {
+//			for (Coord c : new ArrayList<Coord>(points.keySet())) {
+//				if (isCloseToPolygon(c, polygon, offset)) {
+//					points.remove(c);
+//				}
+//			}
+//		}
+//		return points;
+//	}
+
+	public void clear() {
+		itemCount = 0;
+		root.clear();
+	}
+
+	// TODO itemCount is not a good counter (coords or elements?)
+	private long getSize() {
+		return itemCount;
+	}
+
+	private boolean isCloseToPolygon(Coord point, List<Coord> polygon,
+			int gap) {
+		Iterator<Coord> polyIter = polygon.iterator();
+		Coord c2 = polyIter.next();
+		while (polyIter.hasNext()) {
+			Coord c1 = c2;
+			c2 = polyIter.next();
+			double dist = distanceToSegment(c1, c2, point);
+			if (dist <= gap) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * Calculates the distance to the given segment in meter.
+	 * @param spoint1 segment point 1
+	 * @param spoint2 segment point 2
+	 * @param point point
+	 * @return the distance in meter
+	 */
+	private double distanceToSegment(Coord spoint1, Coord spoint2, Coord point) {
+
+		double dx = spoint2.getLongitude() - spoint1.getLongitude();
+		double dy = spoint2.getLatitude() - spoint1.getLatitude();
+
+		if ((dx == 0) && (dy == 0)) {
+			return spoint1.distance(point);
+		}
+
+		double frac = ((point.getLongitude() - spoint1.getLongitude()) * dx + (point
+				.getLatitude() - spoint1.getLatitude()) * dy)
+				/ (dx * dx + dy * dy);
+
+		if (frac < 0) {
+			return spoint1.distance(point);
+		} else if (frac > 1) {
+			return spoint2.distance(point);
+		} else {
+			return spoint1.makeBetweenPoint(spoint2, frac).distance(point);
+		}
+
+	}
+}
Index: src/uk/me/parabola/mkgmap/reader/osm/OsmMapDataSource.java
===================================================================
--- src/uk/me/parabola/mkgmap/reader/osm/OsmMapDataSource.java	(revision 1890)
+++ src/uk/me/parabola/mkgmap/reader/osm/OsmMapDataSource.java	(working copy)
@@ -52,7 +52,9 @@
 	private Style style;
 	private final OsmReadingHooks[] POSSIBLE_HOOKS = {
 			new SeaGenerator(),
+			new MultiPolygonFinishHook(),
 			new HighwayHooks(),
+			new LocationHook(),
 	};
 	private OsmConverter converter;
 	private final Set<String> usedTags = new HashSet<String>();
Index: src/uk/me/parabola/mkgmap/reader/osm/Way.java
===================================================================
--- src/uk/me/parabola/mkgmap/reader/osm/Way.java	(revision 1890)
+++ src/uk/me/parabola/mkgmap/reader/osm/Way.java	(working copy)
@@ -18,6 +18,7 @@
 
 import java.awt.*;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 import uk.me.parabola.imgfmt.Utils;
@@ -92,12 +93,7 @@
 	}
 
 	public void reverse() {
-		int numPoints = points.size();
-		for(int i = 0; i < numPoints/2; ++i) {
-			Coord t = points.get(i);
-			points.set(i, points.get(numPoints - 1 - i));
-			points.set(numPoints - 1 - i, t);
-		}
+		Collections.reverse(points);
 	}
 
 	public boolean isClosed() {
Index: src/uk/me/parabola/mkgmap/reader/osm/Element.java
===================================================================
--- src/uk/me/parabola/mkgmap/reader/osm/Element.java	(revision 1890)
+++ src/uk/me/parabola/mkgmap/reader/osm/Element.java	(working copy)
@@ -27,6 +27,10 @@
 	private String name;
 	private long id;
 
+	public int getTagCount() {
+		return (tags == null ? 0 : tags.size());
+	}
+	
 	/**
 	 * Add a tag to the way.  Some tags are recognised separately and saved in
 	 * separate fields.
Index: src/uk/me/parabola/mkgmap/reader/osm/MultiPolygonFinishHook.java
===================================================================
--- src/uk/me/parabola/mkgmap/reader/osm/MultiPolygonFinishHook.java	(revision 0)
+++ src/uk/me/parabola/mkgmap/reader/osm/MultiPolygonFinishHook.java	(revision 0)
@@ -0,0 +1,49 @@
+package uk.me.parabola.mkgmap.reader.osm;
+
+import java.util.Arrays;
+
+import uk.me.parabola.log.Logger;
+import uk.me.parabola.util.EnhancedProperties;
+
+public class MultiPolygonFinishHook extends OsmReadingHooksAdaptor {
+	private static final Logger log = Logger.getLogger(MultiPolygonFinishHook.class);
+	
+	private ElementSaver saver;
+	
+	public MultiPolygonFinishHook() {
+	}
+
+	@Override
+	public boolean init(ElementSaver saver, EnhancedProperties props) {
+		this.saver = saver;
+		return true;
+	}
+
+	@Override
+	public void end() {
+		long t1 = System.currentTimeMillis();
+		log.info("Finishing multipolygons");
+		for (Way way : saver.getWays().values()) {
+			String removeTag = way.getTag(ElementSaver.MKGMAP_REMOVE_TAG);
+			if (removeTag == null) {
+				continue;
+			}
+			if (ElementSaver.MKGMAP_REMOVE_TAG_ALL_KEY.equals(removeTag)) {
+				if (log.isDebugEnabled())
+					log.debug("Remove all tags from way",way.getId(),way.toTagString());
+				way.removeAllTags();
+			} else {
+				String[] tagsToRemove = removeTag.split(";");
+				if (log.isDebugEnabled())
+					log.debug("Remove tags",Arrays.toString(tagsToRemove),"from way",way.getId(),way.toTagString());
+				for (String rTag : tagsToRemove) {
+					way.deleteTag(rTag);
+				}
+				way.deleteTag(ElementSaver.MKGMAP_REMOVE_TAG);
+			}
+		}
+		log.info("Multipolygon hook finished in "+(System.currentTimeMillis()-t1)+" ms");
+
+	}
+
+}
Index: src/uk/me/parabola/mkgmap/reader/osm/LocationHook.java
===================================================================
--- src/uk/me/parabola/mkgmap/reader/osm/LocationHook.java	(revision 0)
+++ src/uk/me/parabola/mkgmap/reader/osm/LocationHook.java	(revision 0)
@@ -0,0 +1,357 @@
+package uk.me.parabola.mkgmap.reader.osm;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import uk.me.parabola.log.Logger;
+import uk.me.parabola.mkgmap.build.Locator;
+import uk.me.parabola.util.ElementQuadTree;
+import uk.me.parabola.util.EnhancedProperties;
+import uk.me.parabola.util.Java2DConverter;
+import uk.me.parabola.util.MultiHashMap;
+
+public class LocationHook extends OsmReadingHooksAdaptor {
+	private static final Logger log = Logger.getLogger(LocationHook.class);
+
+	private ElementSaver saver;
+	private final List<String> nameTags = new ArrayList<String>();
+	private final Locator locator = new Locator();
+
+	private static final Pattern COMMA_OR_SPACE_PATTERN = Pattern
+			.compile("[,\\s]+");
+	private static final Pattern COMMA_OR_SEMICOLON_PATTERN = Pattern
+			.compile("[,;]+");
+
+	private static class LocationInfo {
+		private final String key;
+		private final String mkgmapTag;
+		private final Collection<String> osmTags;
+
+		public LocationInfo(String key, String mkgmapTag, String... osmTags) {
+			super();
+			this.key = key;
+			this.mkgmapTag = mkgmapTag;
+			this.osmTags = new ArrayList<String>(Arrays.asList(osmTags));
+		}
+
+		public String getMkgmapTag() {
+			return mkgmapTag;
+		}
+
+		public Collection<String> getOsmTags() {
+			return osmTags;
+		}
+
+		public String getKey() {
+			return key;
+		}
+
+	}
+
+
+	private final List<LocationInfo> locationInfos = new ArrayList<LocationInfo>() {
+		{
+			add(new LocationInfo("admin_level=1", "mkgmap:admin_level1",
+					"addr:country", "is_in:country"));
+			add(new LocationInfo("admin_level=2", "mkgmap:admin_level2",
+					"addr:country", "is_in:country"));
+			add(new LocationInfo("admin_level=3", "mkgmap:admin_level3",
+					"addr:province"));
+			add(new LocationInfo("admin_level=4", "mkgmap:admin_level4",
+					"addr:province"));
+			add(new LocationInfo("admin_level=5", "mkgmap:admin_level5",
+					"addr:district"));
+			add(new LocationInfo("admin_level=6", "mkgmap:admin_level6",
+					"addr:subdistrict"));
+			add(new LocationInfo("admin_level=7", "mkgmap:admin_level7",
+					"addr:subdistrict"));
+			add(new LocationInfo("admin_level=8", "mkgmap:admin_level8", "addr:city",
+					"is_in:city"));
+			add(new LocationInfo("admin_level=9", "mkgmap:admin_level9", "addr:city",
+					"is_in:city"));
+			add(new LocationInfo("admin_level=10", "mkgmap:admin_level10", "addr:city",
+					"is_in:city"));
+			add(new LocationInfo("admin_level=11", "mkgmap:admin_level11", "addr:city",
+			"is_in:city"));
+			add(new LocationInfo("postal_code", "mkgmap:postcode", "addr:postcode",
+					"postal_code"));
+		}
+	};
+
+	public boolean init(ElementSaver saver, EnhancedProperties props) {
+		this.saver = saver;
+
+		String nameTagProp = props.getProperty("name-tag-list", "name");
+		nameTags.addAll(Arrays.asList(COMMA_OR_SPACE_PATTERN.split(nameTagProp)));
+
+		return true;
+	}
+
+	public void end() {
+		long t1 = System.currentTimeMillis();
+		log.info("Starting with location hook");
+		
+		List<Element> allElements = new ArrayList<Element>(saver.getWays().size()
+				+ saver.getNodes().size());
+
+		MultiHashMap<String, Way> locationElements = new MultiHashMap<String, Way>();
+
+		// add all nodes that might be converted to a garmin node (tagcount > 0)
+		for (Node node : saver.getNodes().values()) {
+			if (node.getTagCount() > 0) {
+				allElements.add(node);
+			}
+		}
+
+		// add all ways that might be converted to a garmin way (tagcount > 0)
+		// and save all polygons that contains location information
+		for (Way way : saver.getWays().values()) {
+			if (way.getTagCount() == 0) {
+				continue;
+			}
+
+			// in any case add it to the element list
+			allElements.add(way);
+			
+			if (way.isClosed()
+					&& "polyline".equals(way.getTag("mkgmap:stylefilter")) == false) {
+				// it is a polygon - check if it contains location relevant information
+
+				if ("administrative".equals(way.getTag("boundary"))
+						&& way.getTag("admin_level") != null) {
+					try {
+						int adminlevel = Integer.valueOf(way
+								.getTag("admin_level"));
+						if (adminlevel >= 1 && adminlevel <= 11) {
+							locationElements.add("admin_level=" + adminlevel, way);
+						}
+					} catch (NumberFormatException nfe) {
+						log.warn("Wrong admin_level format in way "+way.getId()+" "+way.toTagString());
+					}
+				} else if ("postal_code".equals(way.getTag("boundary"))) {
+					locationElements.add("postal_code", way);
+				}
+			}
+		}
+
+		log.error("Element lists created after "
+				+ (System.currentTimeMillis() - t1) + " ms");
+
+		
+//		java.awt.geom.Area tileArea = Java2DConverter
+//		.createBoundsArea(saver.getBoundingBox());
+		
+		log.info("Creating quadtree for", allElements.size(), "elements");
+		ElementQuadTree quadTree = new ElementQuadTree(allElements);
+		log.error("Quadtree created after "
+				+ (System.currentTimeMillis() - t1) + " ms");
+
+		
+		
+//		// first add the tags to the boundaries itself
+//		for (LocationInfo boundaryInfo : locationInfos) {
+//			String tag = boundaryInfo.getMkgmapTag();
+//			log.info("Preparing", boundaryInfo.getKey(), "with tag", tag);
+//			List<Way> levelBoundaries = locationElements
+//					.get(boundaryInfo.getKey());
+//			log.info(levelBoundaries.size(), "boundaries");
+//			for (Way boundary : levelBoundaries) {
+//				if (boundaryInfo.getKey().startsWith("admin_level")) {
+//					String name = getName(boundary);
+//					if (name == null) {
+//						continue;
+//					}
+//					boundary.addTag(tag, name);
+//				} else if (boundaryInfo.getKey().equals("postal_code")) {
+//					String zip = getZip(boundary);
+//					if (zip != null) {
+//						boundary.addTag(tag, zip);
+//					} else {
+//						log.error("No zip information in " + boundary.getId()
+//								+ " " + boundary.toTagString());
+//					}
+//				}
+//			}
+//		}
+
+		for (LocationInfo locationInfo : locationInfos) {
+			String mkgmapTag = locationInfo.getMkgmapTag();
+			log.info("Processing", locationInfo.getKey(), "with tag", mkgmapTag);
+
+			List<Way> locationPolys = locationElements
+					.get(locationInfo.getKey());
+			log.info(locationPolys.size(), "location relevant polygons");
+			for (Way locationPoly : locationPolys) {
+				String name = (locationInfo.getKey().equals(
+						"postal_code") ? getZip(locationPoly) : getName(locationPoly));
+				if (name == null) {
+					log.error("Cannot process location element because it contains no name tag: " + locationPoly.getId()
+							+ " " + locationPoly.toTagString());
+					continue;
+				}
+				
+				if (locationInfo.getKey().equals("admin_level=2")) {
+					log.info("Input country: "+name);
+					name = locator.fixCountryString(name);
+					log.info("Fixed country: "+name);
+					String cCode = locator.getCountryCode(name);
+					if (cCode != null) {
+						name = cCode;
+					}
+					log.info("Coded: "+name);
+				}
+
+				java.awt.geom.Area boundArea = Java2DConverter
+						.createArea(locationPoly.getPoints());
+
+				log.info("Checking", locationPoly.toTagString());
+				Set<Element> elemsInLocation = quadTree.get(boundArea);
+
+				for (Element elem : elemsInLocation) {
+
+					if (elem.getTag(mkgmapTag) == null) {
+						elem.addTag(mkgmapTag, name);
+						if (log.isDebugEnabled())
+							log.debug("Added tag", mkgmapTag, "=", name, "to",
+									elem.toTagString());
+					}
+				}
+			}
+			
+			log.error("Location "+locationInfo.getKey()+" processed after "
+					+ (System.currentTimeMillis() - t1) + " ms");
+
+			// perform a special handling for country borders
+			if (locationInfo.getKey().equals("admin_level=2")) {
+
+				// count all elements within the tile bounds and use the most assigned one
+				// for all unassigned elements
+				Collection<String> osmTags = locationInfo.getOsmTags();
+				Map<String, Integer> tagValueCounter = new HashMap<String, Integer>();
+				for (Element elem : allElements) {
+					String refTagValue = null;
+					for (String osmTag : osmTags) {
+						refTagValue = elem.getTag(osmTag);
+						if (refTagValue != null) {
+							break;
+						}
+					}
+					if (refTagValue != null) {
+						Integer i = tagValueCounter.get(refTagValue);
+						if (i == null) {
+							tagValueCounter.put(refTagValue, 1);
+						} else {
+							tagValueCounter.put(refTagValue, i + 1);
+						}
+					}
+				}
+
+				log.info("Distribution of country information: " + tagValueCounter);
+
+				String maxTag = null;
+				int maxCount = 0;
+				for (Entry<String, Integer> tagEntry : tagValueCounter.entrySet()) {
+					if (maxCount < tagEntry.getValue()) {
+						maxCount = tagEntry.getValue();
+						maxTag = tagEntry.getKey();
+					}
+				}
+				
+				log.error("Start assigning  "+locationInfo.getKey()+" after "
+						+ (System.currentTimeMillis() - t1) + " ms");
+				if (maxCount > 0) {
+					maxTag = locator.fixCountryString(maxTag);
+					String cCode = locator.getCountryCode(maxTag);
+					if (cCode != null) {
+						maxTag = cCode;
+					}
+					log.info("admin_level2 fixed: " + maxTag);
+
+					// assign the most used country to all unassigned elements
+					for (Element elem : allElements) {
+						if (elem.getTag(mkgmapTag) == null)
+							elem.addTag(mkgmapTag, maxTag);
+					}
+				}
+				
+				// fix the addr:country and is_in:country tag
+				for (Element elem : allElements) {
+					String addrCountry = elem.getTag("addr:country");
+					if (addrCountry != null) {
+						addrCountry = locator.fixCountryString(addrCountry);
+						String cCode = locator.getCountryCode(addrCountry);
+						if (cCode != null) {
+							elem.addTag("addr:country", cCode);
+						}
+//						log.error("addr:country fixed: "+addrCountry+"/"+cCode);
+					}
+
+					String isInCountry = elem.getTag("is_in:country");
+					if (isInCountry != null) {
+						isInCountry = locator.fixCountryString(isInCountry);
+						String cCode = locator.getCountryCode(isInCountry);
+						if (cCode != null) {
+							elem.addTag("is_in:country", cCode);
+						}
+//						log.error("is_in:country fixed: "+isInCountry+"/"+cCode);
+					}
+				}
+				
+				log.error("Special "+locationInfo.getKey()+" handling processed after "
+						+ (System.currentTimeMillis() - t1) + " ms");
+			}
+		}
+		
+		log.error("Location hook finished in "
+				+ (System.currentTimeMillis() - t1) + " ms");
+	}
+
+	private String getName(Element element) {
+		for (String nameTag : nameTags) {
+			String nameTagValue = element.getTag(nameTag);
+			if (nameTagValue == null) {
+				continue;
+			}
+
+			String[] nameParts = COMMA_OR_SEMICOLON_PATTERN.split(nameTagValue);
+			if (nameParts.length == 0) {
+				continue;
+			}
+			return nameParts[0].trim().intern();
+		}
+		return null;
+	}
+
+	private String getZip(Element element) {
+		String zip = element.getTag("postal_code");
+		if (zip == null) {
+			String name = getName(element);
+			if (name != null) {
+				String[] nameParts = name.split(Pattern.quote(" "));
+				if (nameParts.length > 0) {
+					zip = nameParts[0].trim();
+				}
+			}
+		}
+		return zip;
+	}
+
+	@Override
+	public Set<String> getUsedTags() {
+		Set<String> tags = new HashSet<String>();
+		tags.add("boundary");
+		tags.add("admin_level");
+		tags.add("postal_code");
+		tags.addAll(nameTags);
+		return tags;
+	}
+
+}
Index: src/uk/me/parabola/mkgmap/reader/osm/ElementSaver.java
===================================================================
--- src/uk/me/parabola/mkgmap/reader/osm/ElementSaver.java	(revision 1890)
+++ src/uk/me/parabola/mkgmap/reader/osm/ElementSaver.java	(working copy)
@@ -14,7 +14,6 @@
 
 import java.util.AbstractMap;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.HashMap;
 import java.util.IdentityHashMap;
 import java.util.LinkedHashMap;
@@ -206,7 +205,6 @@
 	
 	public void finishLoading() {
 		coordMap = null;
-		finishMultiPolygons();
 	}
 
 	/**
@@ -244,29 +242,6 @@
 		relationMap = null;
 	}
 
-
-	private void finishMultiPolygons() {
-		for (Way way : wayMap.values()) {
-			String removeTag = way.getTag(MKGMAP_REMOVE_TAG);
-			if (removeTag == null) {
-				continue;
-			}
-			if (MKGMAP_REMOVE_TAG_ALL_KEY.equals(removeTag)) {
-				if (log.isDebugEnabled())
-					log.debug("Remove all tags from way",way.getId(),way.toTagString());
-				way.removeAllTags();
-			} else {
-				String[] tagsToRemove = removeTag.split(";");
-				if (log.isDebugEnabled())
-					log.debug("Remove tags",Arrays.toString(tagsToRemove),"from way",way.getId(),way.toTagString());
-				for (String rTag : tagsToRemove) {
-					way.deleteTag(rTag);
-				}
-				way.deleteTag(MKGMAP_REMOVE_TAG);
-			}
-		}
-	}
-
 	/**
 	 *
 	 * "soft clip" each way that crosses a boundary by adding a point
@@ -564,6 +539,10 @@
 		map.put(p, inc);
 	}
 
+	public Map<Long, Node> getNodes() {
+		return nodeMap;
+	}
+	
 	public Map<Long, Way> getWays() {
 		return wayMap;
 	}
Index: src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java
===================================================================
--- src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java	(revision 1890)
+++ src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java	(working copy)
@@ -663,23 +663,20 @@
 		
 		// Now try to get some address info for POIs
 		
-		String city         = element.getTag("addr:city");
-		String zip          = element.getTag("addr:postcode");
+		String country      = element.getTag("mkgmap:country");
+		String region       = element.getTag("mkgmap:region");
+		String city         = element.getTag("mkgmap:city");
+		String zip          = element.getTag("mkgmap:postal_code");
 		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:name");
+		if(country != null)
+			ms.setCountry(country);
+
+		if(region != null)
+			ms.setRegion(region);
 		
 		if(city != null)
 			ms.setCity(city);
@@ -699,12 +696,8 @@
 		if(phone != null)
 			ms.setPhone(phone);
 
-		if(country != null)
-			ms.setCountry(country);
-
-		if(region != null)
-			ms.setRegion(region);
 
+		
 		if(MapObject.hasExtendedType(gt.getType())) {
 			// pass attributes with mkgmap:xt- prefix (strip prefix)
 			Map<String,String> xta = element.getTagsWithPrefix("mkgmap:xt-", true);
Index: src/uk/me/parabola/mkgmap/osmstyle/actions/EchoAction.java
===================================================================
--- src/uk/me/parabola/mkgmap/osmstyle/actions/EchoAction.java	(revision 1890)
+++ src/uk/me/parabola/mkgmap/osmstyle/actions/EchoAction.java	(working copy)
@@ -23,10 +23,10 @@
  * @author Richard Fairhurst
  */
 public class EchoAction implements Action {
-	private final ValueBuilder value;
+	private final AllValueBuilder value;
 
 	public EchoAction(String str) {
-		this.value = new ValueBuilder(str);
+		this.value = new AllValueBuilder(str);
 	}
 
 	public void perform(Element el) {
Index: src/uk/me/parabola/mkgmap/osmstyle/actions/AllValueBuilder.java
===================================================================
--- src/uk/me/parabola/mkgmap/osmstyle/actions/AllValueBuilder.java	(revision 0)
+++ src/uk/me/parabola/mkgmap/osmstyle/actions/AllValueBuilder.java	(revision 0)
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2008 Steve Ratcliffe
+ *
+ *  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.
+ *
+ *
+ * Author: Steve Ratcliffe
+ * Create date: 02-Dec-2008
+ */
+package uk.me.parabola.mkgmap.osmstyle.actions;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import uk.me.parabola.mkgmap.reader.osm.Element;
+
+/**
+ * Build a value that can have tag values substituted in it.
+ *
+ * @author Steve Ratcliffe
+ * @author Toby Speight
+ */
+public class AllValueBuilder {
+
+	private final List<ValueItem> items = new ArrayList<ValueItem>();
+
+	public AllValueBuilder(String pattern) {
+		compile(pattern);
+	}
+
+	/**
+	 * Build this string if all the tags that are required are available.
+	 *
+	 * If a tag does not exist then the whole string is rejected.  This allows
+	 * you to make conditional replacements.
+	 *
+	 * @param el Used as a source of tags.
+	 * @param lel Used as a source of local tags.
+	 * @return The built string if all required tags are available.  If any
+	 * are missing then it returns null.
+	 */
+	public String build(Element el, Element lel) {
+
+		// If we get here we can build the final string.  A common case
+		// is that there is just one, so return it directly.
+		if (items.size() == 1)
+			return items.get(0).getValue(el, lel);
+
+		// OK we have to construct the result.
+		StringBuilder sb = new StringBuilder();
+		for (ValueItem item : items)
+			sb.append(item.getValue(el, lel));
+
+		return sb.toString();
+	}
+
+	/**
+	 * A tag value can contain variables that are the values of other tags.
+	 * This is especially useful for 'name', as you might want to set it to
+	 * some combination of other tags.
+	 *
+	 * If there are no replacement values, the same string as was passed
+	 * in.  If all the replacement values exist, then the string with the
+	 * values all replaced.  If any replacement tagname does not exist
+	 * then returns null.
+
+	 * @param in An input string that may contain tag replacement introduced
+	 * by ${tagname}.
+	 */
+	private void compile(String in) {
+		if (!in.contains("$")) {
+			items.add(new ValueItem(in));
+			return;
+		}
+
+		char state = '\0';
+		StringBuilder text = new StringBuilder();
+		StringBuilder tagname = null;
+		for (char c : in.toCharArray()) {
+			switch (state) {
+			case '\0':
+				if (c == '$') {
+					state = '$';
+				} else
+					text.append(c);
+				break;
+			case '$':
+				switch (c) {
+				case '{':
+				case '(':
+					if (text.length() > 0) {
+						items.add(new ValueItem(text.toString()));
+						text.setLength(0);
+					}
+					tagname = new StringBuilder();
+					state = (c == '{') ? '}' : ')';
+					break;
+				default:
+					state = '\0';
+					text.append('$');
+					text.append(c);
+				}
+				break;
+			case '}':
+			case ')':
+				if (c == state) {
+					//noinspection ConstantConditions
+					assert tagname != null;
+					addTagValue(tagname.toString(), c == ')');
+					state = '\0';
+					tagname = null;
+				} else {
+					tagname.append(c);
+				}
+				break;
+			default:
+				assert false;
+			}
+		}
+
+		if (text.length() > 0)
+			items.add(new ValueItem(text.toString()));
+	}
+
+	private void addTagValue(String tagname, boolean is_local) {
+		ValueItem item = new ValueItem();
+		if (tagname.contains("|")) {
+			String[] parts = tagname.split("\\|");
+			assert parts.length > 1;
+			item.setTagname(parts[0], is_local);
+			for (int i = 1; i < parts.length; i++)
+				addFilter(item, parts[i]);
+		} else {
+			item.setTagname(tagname, is_local);
+		}
+		items.add(item);
+	}
+
+	private void addFilter(ValueItem item, String expr) {
+		Pattern pattern = Pattern.compile("([^:]+):(.*)");
+		//pattern.
+		Matcher matcher = pattern.matcher(expr);
+		matcher.find();
+		String cmd = matcher.group(1);
+		String arg = matcher.group(2);
+		if (cmd.equals("def")) {
+			item.addFilter(new DefaultFilter(arg));
+		} else if (cmd.equals("conv")) {
+			item.addFilter(new ConvertFilter(arg));
+		} else if (cmd.equals("subst")) {
+			item.addFilter(new SubstitutionFilter(arg));
+		} else if (cmd.equals("prefix")) {
+			item.addFilter(new PrependFilter(arg));
+		} else if (cmd.equals("highway-symbol")) {
+			item.addFilter(new HighwaySymbolFilter(arg));
+		} else if (cmd.equals("height")) {
+			item.addFilter(new HeightFilter(arg));
+		} else if (cmd.equals("not-equal")) {
+			item.addFilter(new NotEqualFilter(arg));
+		} else if (cmd.equals("substring")) {
+			item.addFilter(new SubstringFilter(arg));
+		}
+	}
+
+	public String toString() {
+		StringBuilder sb = new StringBuilder("'");
+		for (ValueItem v : items) {
+			sb.append(v);
+		}
+		sb.append("'");
+		return sb.toString();
+	}
+
+	public Set<String> getUsedTags() {
+		Set<String> set = new HashSet<String>();
+		for (ValueItem v : items) {
+			String tagname = v.getTagname();
+			if (tagname != null)
+				set.add(tagname);
+		}
+		return set;
+	}
+}
Index: src/uk/me/parabola/mkgmap/build/MapBuilder.java
===================================================================
--- src/uk/me/parabola/mkgmap/build/MapBuilder.java	(revision 1890)
+++ src/uk/me/parabola/mkgmap/build/MapBuilder.java	(working copy)
@@ -258,18 +258,22 @@
 			{
 				Country thisCountry;
 
-				String CountryStr = p.getCountry();
-				String RegionStr  = p.getRegion();
+				String countryStr = p.getCountry();
+				if (countryStr != null) {
+					countryStr = locator.fixCountryString(countryStr);
+					p.setCountry(countryStr);
+				}
 
-				if(CountryStr != null)
-					thisCountry = lbl.createCountry(CountryStr, locator.getCountryCode(CountryStr));
-				else
+				if(countryStr != null) {
+					thisCountry = lbl.createCountry(countryStr, locator.getCountryCode(countryStr));
+				} else
 					thisCountry = country;
 
+				String regionStr  = p.getRegion();
 				Region thisRegion;
-				if(RegionStr != null)
+				if(regionStr != null)
 				{
-					thisRegion = lbl.createRegion(thisCountry,RegionStr, null);
+					thisRegion = lbl.createRegion(thisCountry,regionStr, null);
 				}
 				else
 					thisRegion = region;
@@ -290,25 +294,29 @@
 			if(line.isRoad()) {
 				String cityName = line.getCity();
 				String cityCountryName = line.getCountry();
+				if (cityCountryName != null) {
+					cityCountryName = locator.fixCountryString(cityCountryName);
+					line.setCountry(cityCountryName);
+				}
 				String cityRegionName  = line.getRegion();
 				String zipStr = line.getZip();
 
-				if(cityName == null) {
-					// Get name of next city if untagged
-
-					tempPoint.setLocation(line.getLocation());
-					MapPoint nextCity = locator.findNextPoint(tempPoint);
-
-					if(nextCity != null) {
-						cityName = nextCity.getCity();
-						if(cityCountryName == null)
-							cityCountryName = nextCity.getCountry();
-						if(cityRegionName == null)
-							cityRegionName = nextCity.getRegion();
-						if(zipStr == null)
-							zipStr = nextCity.getZip();
-					}
-				}
+//				if(cityName == null) {
+//					// Get name of next city if untagged
+//
+//					tempPoint.setLocation(line.getLocation());
+//					MapPoint nextCity = locator.findNextPoint(tempPoint);
+//
+//					if(nextCity != null) {
+//						cityName = nextCity.getCity();
+//						if(cityCountryName == null)
+//							cityCountryName = nextCity.getCountry();
+//						if(cityRegionName == null)
+//							cityRegionName = nextCity.getRegion();
+//						if(zipStr == null)
+//							zipStr = nextCity.getZip();
+//					}
+//				}
 
 				if(cityName != null) {
 
@@ -341,12 +349,15 @@
 			if(p.isExit()) {
 				processExit(map, (MapExitPoint)p);
 			}
-			else if(!p.isCity() && !p.hasExtendedType() && (p.isRoadNamePOI() || poiAddresses)) {
+			else if (!p.isCity())
+//				if(!p.isCity() && !p.hasExtendedType() && (p.isRoadNamePOI() || poiAddresses)) 
+				{
+				
 				boolean doAutofill;
-				if(locationAutofillLevel > 0 || p.isRoadNamePOI())
+//				if(locationAutofillLevel > 0 || p.isRoadNamePOI())
 					doAutofill = true;
-				else
-					doAutofill = false;
+//				else
+//					doAutofill = false;
 				
 				
 				String countryStr = p.getCountry();
Index: src/uk/me/parabola/mkgmap/build/Locator.java
===================================================================
--- src/uk/me/parabola/mkgmap/build/Locator.java	(revision 1890)
+++ src/uk/me/parabola/mkgmap/build/Locator.java	(working copy)
@@ -55,14 +55,15 @@
 import java.util.Collection;
 import java.util.List;
 
+import uk.me.parabola.log.Logger;
 import uk.me.parabola.mkgmap.general.MapPoint;
 import uk.me.parabola.mkgmap.general.MapPointFastFindMap;
-import uk.me.parabola.mkgmap.general.MapPointMultiMap;
 
 public class Locator {
+	private static final Logger log = Logger.getLogger(Locator.class);
 
 	private final MapPointFastFindMap cityMap  					= new MapPointFastFindMap();
-	private final MapPointMultiMap  	fuzzyCityMap			= new MapPointMultiMap();
+//	private final MapPointMultiMap  	fuzzyCityMap			= new MapPointMultiMap();
 	private final List<MapPoint> placesMap  =  new ArrayList<MapPoint>();
 
 	private final LocatorConfig locConfig = new LocatorConfig();
@@ -74,21 +75,24 @@
 	public void addLocation(MapPoint p) 
 	{
 		resolveIsInInfo(p); // Pre-process the is_in field
-		
-		if(autoFillLevel < 1 &&  p.getCity() == null)
-		{			
-			// Without auto-fill city name is the name of the tag
-			p.setCity(p.getName());
-		}
+
+
+//		if(autoFillLevel < 1 &&  p.getCity() == null)
+//		{			
+//			// Without auto-fill city name is the name of the tag
+//			p.setCity(p.getName());
+//		}
 		
 		if(p.getCity() != null)
 		{
+			if (log.isDebugEnabled())
+			log.debug(p.getCity()+" "+p.getRegion()+" "+p.getCountry());
 			cityMap.put(p.getCity(), p);
 			
-			fuzzyCityMap.put(fuzzyDecode(p.getCity()),p);
-
-			if(p.getName() != null && !p.getCity().equals(p.getName())) // Name variants ?
-				fuzzyCityMap.put(fuzzyDecode(p.getName()),p);
+//			fuzzyCityMap.put(fuzzyDecode(p.getCity()),p);
+//
+//			if(p.getName() != null && !p.getCity().equals(p.getName())) // Name variants ?
+//				fuzzyCityMap.put(fuzzyDecode(p.getName()),p);
 		}
 		else
 		{
@@ -170,13 +174,15 @@
 			if(cityList.length > 1 &&
 				isContinent(cityList[cityList.length-1]))	// Is last a continent ?
 			{
-				// The one before continent should be the country
-				p.setCountry(fixCountryString(cityList[cityList.length-2].trim()));
-
+				if (p.getCountry() == null) {
+					// The one before continent 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)
+				if(cityList.length > offset && p.getRegion() == null)
 					p.setRegion(cityList[cityList.length-(offset+1)].trim());
 
 			}
@@ -185,12 +191,14 @@
 
 			if(cityList.length > 1 && isContinent(cityList[0]))	// Is first a continent ?
 			{
-				// The one before continent should be the country
-				p.setCountry(fixCountryString(cityList[1].trim()));
+				if (p.getCountry() == null) {
+					// The one before continent should be the country
+					p.setCountry(fixCountryString(cityList[1].trim()));
+				}
 				
 				int offset = locConfig.getRegionOffset(p.getCountry()) + 1;
 
-				if(cityList.length > offset)
+				if(cityList.length > offset && p.getRegion() == null)
 					p.setRegion(cityList[offset].trim());
 			}	
 
@@ -208,8 +216,8 @@
 
 					int offset = locConfig.getRegionOffset(countryStr) + 1;
 
-					if(cityList.length > offset)
-		     		p.setRegion(cityList[cityList.length-(offset+1)].trim());	
+					if(cityList.length > offset && p.getRegion() == null)
+						p.setRegion(cityList[cityList.length-(offset+1)].trim());	
 				}
 			}
 		}
@@ -259,21 +267,21 @@
 			}
 	 	}
 		
-		nextCityList = fuzzyCityMap.getList(fuzzyDecode(p.getCity()));
-		
-		if(nextCityList != null)
-		{
-			for (MapPoint nextCity: nextCityList)		
-			{
-				Double dist = p.getLocation().distance(nextCity.getLocation());
-
-				if(dist < minDist)
-				{
-					minDist = dist;
-					near = nextCity;
-				}
-			}
-	 	}
+//		nextCityList = fuzzyCityMap.getList(fuzzyDecode(p.getCity()));
+//		
+//		if(nextCityList != null)
+//		{
+//			for (MapPoint nextCity: nextCityList)		
+//			{
+//				Double dist = p.getLocation().distance(nextCity.getLocation());
+//
+//				if(dist < minDist)
+//				{
+//					minDist = dist;
+//					near = nextCity;
+//				}
+//			}
+//	 	}
 		
 		if(near != null && minDist < 30000) // Wrong hit more the 30 km away ?
 			return near;
@@ -300,9 +308,9 @@
 				String biggerCityName = aCityList.trim();
 
 
-				if (fuzzy)
-					nextCityList = fuzzyCityMap.getList(fuzzyDecode(biggerCityName));
-				else
+//				if (fuzzy)
+//					nextCityList = fuzzyCityMap.getList(fuzzyDecode(biggerCityName));
+//				else
 					nextCityList = cityMap.getList(biggerCityName);
 
 				if (nextCityList != null) {
@@ -380,8 +388,10 @@
 
 
 					if (near != null) {
-						place.setCity(near.getCity());
-						place.setZip(near.getZip());
+						if (place.getCity()==null)
+							place.setCity(near.getCity());
+						if (place.getZip() == null)
+							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);
@@ -390,7 +400,7 @@
 							// 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(near.getCountry()))
+							if(!isOpenGeoDBCountry(near.getCountry()) && place.getCity() == null)
 								place.setCity(place.getName());
 						}
 					}
@@ -420,7 +430,7 @@
 					if( place.getCity() != null)
 					{
 						cityMap.put(place.getName(),place);
-						fuzzyCityMap.put(fuzzyDecode(place.getName()),place);
+//						fuzzyCityMap.put(fuzzyDecode(place.getName()),place);
 						placesMap.set(i, null);
 					}
 					else if(autoFillLevel < 2 && (runCount + 1) == maxRuns)
@@ -442,21 +452,21 @@
 		
 	}
 
-	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);
-	}
+//	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);
+//	}
 }
 
