Index: test/uk/me/parabola/mkgmap/general/MapPointKdTreeTest.java
===================================================================
--- test/uk/me/parabola/mkgmap/general/MapPointKdTreeTest.java	(revision 0)
+++ test/uk/me/parabola/mkgmap/general/MapPointKdTreeTest.java	(revision 0)
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2012.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 or
+ * 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.
+ */
+
+package uk.me.parabola.mkgmap.general;
+
+import static org.junit.Assert.assertFalse;
+import org.junit.Test;
+
+import uk.me.parabola.imgfmt.app.Coord;
+
+public class MapPointKdTreeTest {
+
+	@Test
+	public void TestFindNextPoint(){
+        MapPointKdTree t = new MapPointKdTree( );
+        
+        int [][]test = {{70,20}, {50,40}, {90,60}, {20,30}, {40,70}, {80,10}, {-10,20}, {-30,-40} }  ;
+        Coord []testCoords = new Coord[test.length]; 
+        
+        for( int i = 0; i < test.length; i++ )
+        {
+        	MapPoint p = new MapPoint();
+        	testCoords[i] = new Coord(test[i][0],test[i][1]);
+        	p.setLocation(testCoords[i]);
+        	t.add(p);
+        }
+        // compare naive search result with kd--tree result
+        MapPoint toFind = new MapPoint();
+        for (int x = -100; x < 100; x++){
+        	for (int y = -100; y < 100; y++){
+        		Coord co = new Coord(x,y);
+        		double minDist = Double.MAX_VALUE;
+
+        		for (int i = 0; i<testCoords.length; i++){
+        			Double dist =  testCoords[i].distanceInDegreesSquared(co);
+        			if (dist < minDist){
+        				minDist = dist;
+        			}
+        		}
+        		toFind.setLocation(co);
+        		MapPoint next = t.findNextPoint(toFind);
+    			double dist =  next.getLocation().distanceInDegreesSquared(co);
+    			double delta = Math.abs(dist - minDist); 
+    			// if this test fails because 
+        		assertFalse("delta should be 0.0: " + delta, delta != 0.0);
+        	}
+        }
+	}
+}
Index: src/uk/me/parabola/mkgmap/general/MapPointKdTree.java
===================================================================
--- src/uk/me/parabola/mkgmap/general/MapPointKdTree.java	(revision 0)
+++ src/uk/me/parabola/mkgmap/general/MapPointKdTree.java	(revision 0)
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2012.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 or
+ * 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.
+ */
+
+package uk.me.parabola.mkgmap.general;
+
+
+import uk.me.parabola.imgfmt.app.Coord;
+import uk.me.parabola.mkgmap.general.MapPoint;
+
+
+/**
+ * A kd-tree (2D) implementation to solve the nearest neighbor problem.
+ * The tree is not explicitly balanced.
+ * 
+ * @author GerdP
+ *
+ */
+public class MapPointKdTree {
+	private static final boolean ROOT_NODE_USES_LONGITUDE = false;
+	
+	private static class KdNode {
+		MapPoint point;
+		KdNode left;
+		KdNode right;
+
+		KdNode(MapPoint p) {
+			point = p;
+		}
+	}
+	// the tree root
+    private KdNode root;
+    // number of saved MapPoint objects  
+    private int size;
+
+    // helpers 
+    private MapPoint nextPoint ;
+    private double minDist;
+
+    /**
+     *  create an empty tree
+     */
+	public MapPointKdTree() {
+		root = null;
+	}
+
+	public long size()
+	{
+		return size;
+	}
+
+	
+	/**
+	 * Start the add action with the root
+	 * @param toAdd
+	 */
+	public void add(MapPoint toAdd) {
+		size++;
+		root = add(toAdd, root, ROOT_NODE_USES_LONGITUDE);
+	}
+
+	/**
+	 * Compares the given axis of both points. 
+	 * @param longitude <code>true</code>: compare longitude; <code>false</code> compare latitude
+	 * @param c1 a point
+	 * @param c2 another point
+	 * @return <code>true</code> the axis value of c1 is smaller than c2; 
+	 * 		<code>false</code> the axis value of c1 is equal or larger than c2
+	 */
+	private boolean isSmaller(boolean longitude, Coord c1, Coord c2) {
+		if (longitude) {
+			return c1.getLongitude() < c2.getLongitude();
+		} else {
+			return c1.getLatitude() < c2.getLatitude();
+		}
+	}
+	
+	/**
+	 * Recursive routine to find the right place for inserting 
+	 * into the tree.  
+	 * @param toAdd the point
+	 * @param tree the subtree root node where to add (maybe <code>null</code>)
+	 * @param useLongitude <code>true</code> the tree node uses longitude for comparison; 
+	 * 		<code>false</code> the tree node uses latitude for comparison
+	 * @return the subtree root node after insertion
+	 */
+    private KdNode add( MapPoint toAdd, KdNode tree,  boolean useLongitude){
+        if( tree == null ) {
+            tree = new KdNode( toAdd );
+        } else {
+        	if(isSmaller(useLongitude, toAdd.getLocation(), tree.point.getLocation())) {
+        		tree.left = add(toAdd, tree.left, !useLongitude);
+        	} else {
+        		tree.right = add(toAdd, tree.right, !useLongitude);
+        	}
+        }
+        return tree;
+    }
+    
+	/**
+	 * Searches for the point that has smallest distance to the given point.
+	 * @param p the point to search for
+	 * @return the point with shortest distance to <var>p</var>
+	 */
+	public MapPoint findNextPoint(MapPoint p) {
+		// reset 
+		minDist = Double.MAX_VALUE;
+		nextPoint = null;
+		
+		// false => first node is a latitude level
+		return findNextPoint(p, root, ROOT_NODE_USES_LONGITUDE);
+	}
+
+	private MapPoint findNextPoint(MapPoint p, KdNode tree, boolean useLongitude) {
+		boolean continueWithLeft = false;
+		if (tree == null)
+			return nextPoint;
+		
+		if (tree.left == null && tree.right == null){
+			double dist = tree.point.getLocation().distanceInDegreesSquared(p.getLocation());
+			if (dist < minDist){
+				nextPoint = tree.point;
+				minDist = dist;
+			}
+			return nextPoint;
+		}
+		else {
+			if (isSmaller(useLongitude, p.getLocation(), tree.point.getLocation())){
+				continueWithLeft = false;
+				nextPoint = findNextPoint(p, tree.left, !useLongitude);
+			}
+			else {
+				continueWithLeft = true;
+				nextPoint = findNextPoint(p, tree.right, !useLongitude);
+			}
+		}
+		
+		double dist = tree.point.getLocation().distanceInDegreesSquared(p.getLocation());
+		if (dist < minDist){
+			nextPoint = tree.point;
+			minDist = dist;
+		}
+		
+		// do we have to search the other part of the tree?
+		Coord test;
+		if (useLongitude)
+			test = new Coord(p.getLocation().getLatitude(),tree.point.getLocation().getLongitude());
+		else
+			test = new Coord(tree.point.getLocation().getLatitude(),p.getLocation().getLongitude());
+		if (test.distanceInDegreesSquared(p.getLocation()) < minDist){
+			if (continueWithLeft) 
+				nextPoint = findNextPoint(p, tree.left, !useLongitude);
+			else
+				nextPoint = findNextPoint(p, tree.right, !useLongitude);
+		}
+		return nextPoint;
+	}
+} 
Index: src/uk/me/parabola/mkgmap/build/Locator.java
===================================================================
--- src/uk/me/parabola/mkgmap/build/Locator.java	(revision 2239)
+++ src/uk/me/parabola/mkgmap/build/Locator.java	(working copy)
@@ -20,14 +20,18 @@
 
 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.MapPointKdTree;
 import uk.me.parabola.mkgmap.reader.osm.Tags;
 import uk.me.parabola.util.EnhancedProperties;
+import uk.me.parabola.util.MultiHashMap;
 
 public class Locator {
 	private static final Logger log = Logger.getLogger(Locator.class);
 
-	private final MapPointFastFindMap cityMap  					= new MapPointFastFindMap();
+    /** hash map to collect equally named MapPoints*/ 
+	private final MultiHashMap<String, MapPoint> cityMap = new MultiHashMap<String, MapPoint>();
+	
+	private final MapPointKdTree cityFinder = new MapPointKdTree();
 	private final List<MapPoint> placesMap  =  new ArrayList<MapPoint>();
 
 	/** Contains the tags defined by the option name-tag-list */
@@ -56,7 +60,8 @@
 			return;
 		}
 		
-		log.debug("S City 0x"+Integer.toHexString(p.getType()), p.getName(), "|", p.getCity(), "|", p.getRegion(), "|", p.getCountry());
+		if (log.isDebugEnabled())
+			log.debug("S City 0x"+Integer.toHexString(p.getType()), p.getName(), "|", p.getCity(), "|", p.getRegion(), "|", p.getCountry());
 
 		// correct the country name
 		// usually this is the translation from 3letter ISO code to country name
@@ -70,14 +75,16 @@
 			if (log.isDebugEnabled())
 				log.debug(p.getCity(),p.getRegion(),p.getCountry());
 			// Must use p.getName() here because p.getCity() contains the city name of the preprocessed cities
-			cityMap.put(p.getName(), p);
+			addCity(p.getName(), p);
 		}
 		else
 		{
 			// All other places which do not seam to be a real city has to resolved later
 			placesMap.add(p);		
 		}
-		log.debug("E City 0x"+Integer.toHexString(p.getType()), p.getName(), "|", p.getCity(), "|", p.getRegion(), "|", p.getCountry());
+		
+		if (log.isDebugEnabled())
+			log.debug("E City 0x"+Integer.toHexString(p.getType()), p.getName(), "|", p.getCity(), "|", p.getRegion(), "|", p.getCountry());
 	}
 
 	public void setDefaultCountry(String country, String abbr)
@@ -244,7 +251,7 @@
 	
 	public MapPoint findNextPoint(MapPoint p)
 	{
-		return cityMap.findNextPoint(p);
+		return cityFinder.findNextPoint(p);
 	}
 	
 	public MapPoint findNearbyCityByName(MapPoint p) {
@@ -252,7 +259,7 @@
 		if (p.getCity() == null)
 			return null;
 
-		Collection<MapPoint> nextCityList = cityMap.getList(p.getCity());
+		Collection<MapPoint> nextCityList = cityMap.get(p.getCity());
 		if (nextCityList == null) {
 			return null;
 		}
@@ -296,11 +303,10 @@
 		for (String cityCandidate : cityList) {
 			cityCandidate = cityCandidate.trim();
 
-			Collection<MapPoint> candidateCityList = cityMap
-					.getList(cityCandidate);
+			Collection<MapPoint> candidateCityList = cityMap.get(cityCandidate);
 			if (candidateCityList != null) {
 				if (nextCityList == null) {
-					nextCityList = new ArrayList<MapPoint>();
+					nextCityList = new ArrayList<MapPoint>(candidateCityList.size());
 				}
 				nextCityList.addAll(candidateCityList);
 			}
@@ -340,6 +346,7 @@
 		
 		log.info("Locator City   Map contains", cityMap.size(), "entries");
 		log.info("Locator Places Map contains", placesMap.size(), "entries");
+		log.info("Locator Finder KdTree contains", cityFinder.size(), "entries");
 
 		int runCount = 0;
 		int maxRuns = 2;
@@ -369,7 +376,7 @@
 					} else if (locationAutofill.contains("nearest") && (runCount + 1) == maxRuns) {
 						// In the last resolve run just take info from the next
 						// known city
-						near = cityMap.findNextPoint(place);
+						near = cityFinder.findNextPoint(place);
 						if (near != null && near.getCountry() != null) {
 							if (place.getCity() == null)
 								place.setCity(place.getName());
@@ -395,11 +402,11 @@
 
 				if (place != null) {
 					if (place.getCity() != null) {
-						cityMap.put(place.getName(), place);
+						addCity(place.getName(), place);
 						placesMap.set(i, null);
 					} else if ((runCount + 1) == maxRuns) {
 						place.setCity(place.getName());
-						cityMap.put(place.getName(), place);
+						addCity(place.getName(), place);
 					}
 				}
 			}
@@ -413,5 +420,22 @@
 		} while (unresCount > 0 && runCount < maxRuns);
 
 	}
+	
+	/**
+	 * Add MapPoint to cityMap and cityFinder
+	 *  
+	 * @param name Name that is used to find the city
+	 * @param p the MapPoint
+	 */
+	private void addCity(String name, MapPoint p){
+		if(name != null)
+		{
+			cityMap.add(name, p);
+			
+			// add point to the kd-tree
+			cityFinder.add(p);
+		}
+		
+	}
 }
 
