Index: me/parabola/mkgmap/reader/osm/boundary/BoundaryDiff.java
===================================================================
--- me/parabola/mkgmap/reader/osm/boundary/BoundaryDiff.java	(revision 2239)
+++ me/parabola/mkgmap/reader/osm/boundary/BoundaryDiff.java	(working copy)
@@ -34,6 +34,13 @@
 import uk.me.parabola.util.GpxCreator;
 import uk.me.parabola.util.Java2DConverter;
 
+/**
+ * Compare two boundary files or two directories with boundary files.
+ * Write differences as gpx files.
+ *  
+ * @author WanMil (initial version), rewritten for BoundaryQuadTree by GerdP
+ *
+ */
 public class BoundaryDiff {
 	private final String inputName1;
 	private final String inputName2;
@@ -41,9 +48,14 @@
 	public BoundaryDiff(String boundaryDirName1, String boundaryDirName2) {
 		this.inputName1 = boundaryDirName1;
 		this.inputName2 = boundaryDirName2;
-		
 	}
 
+	/** 
+	 * Return list of file names
+	 * @param dirName a directory or a zip file containing *.bnd files,
+	 * or a single *.bnd file
+	 * @return
+	 */
 	private List<String> getBoundsFiles(String dirName) {
 		File dir = new File(dirName);
 		System.out.println(dirName);
@@ -56,6 +68,13 @@
 		}
 	}
 
+	/**
+	 * Compare all files in one list with the files in another list.
+	 * Optionally restrict the comparison to boundaries with the
+	 * given tag/value combination.
+	 * @param tag should be admin_level
+	 * @param value any value appropriate for the tag
+	 */
 	public void compare(String tag, String value) {
 		List<String> b1 = getBoundsFiles(inputName1);
 		List<String> b2 = getBoundsFiles(inputName2);
@@ -165,6 +184,13 @@
 		return a;
 	}
 
+	/**
+	 * Create gpx file(s) for a single area. 
+	 * @param a the Area
+	 * @param subDirName target sub-directory
+	 * @param tagKey used to build the gpx file name
+	 * @param tagValue used to build the gpx file name
+	 */
 	private void saveArea(Area a, String subDirName, String tagKey, String tagValue) {
 
 		String gpxBasename = "gpx/diff/" + subDirName + "/"
@@ -181,7 +207,9 @@
 		}
 	}
 
-	
+	/**
+	 * print usage info
+	 */
 	private static void printUsage(){
 		System.err.println("Usage:");
 		System.err
Index: me/parabola/mkgmap/reader/osm/boundary/BoundaryFile2Gpx.java
===================================================================
--- me/parabola/mkgmap/reader/osm/boundary/BoundaryFile2Gpx.java	(revision 2239)
+++ me/parabola/mkgmap/reader/osm/boundary/BoundaryFile2Gpx.java	(working copy)
@@ -36,6 +36,11 @@
 		bqt = BoundaryUtil.loadQuadTree(boundaryDirName, boundaryFileName);
 	}
 
+	/**
+	 * Create gpx files for areas which contain no information for a given
+	 * admin level.  
+	 * @param admLevel reasonable values are 2..11
+	 */
 	public void saveEmptyAreas(int admLevel) {
 		Area tileArea = Java2DConverter.createBoundsArea(BoundaryUtil
 				.getBbox(boundaryFileName));
@@ -68,6 +73,9 @@
 		}
 	}
 
+	/**
+	 * create gpx files for all boundaries contained in one *.bnd file
+	 */
 	public void saveAsGpx() {
 		System.out.println("Start converting " + boundaryFileName);
 
Index: me/parabola/mkgmap/reader/osm/boundary/BoundaryGrid.java
===================================================================
--- me/parabola/mkgmap/reader/osm/boundary/BoundaryGrid.java	(revision 2239)
+++ me/parabola/mkgmap/reader/osm/boundary/BoundaryGrid.java	(working copy)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006, 2011.
+ * 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
Index: me/parabola/mkgmap/reader/osm/boundary/BoundaryLocationPreparer.java
===================================================================
--- me/parabola/mkgmap/reader/osm/boundary/BoundaryLocationPreparer.java	(revision 2239)
+++ me/parabola/mkgmap/reader/osm/boundary/BoundaryLocationPreparer.java	(working copy)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006, 2012.
+ * 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
Index: me/parabola/mkgmap/reader/osm/boundary/BoundaryPreparer.java
===================================================================
--- me/parabola/mkgmap/reader/osm/boundary/BoundaryPreparer.java	(revision 2239)
+++ me/parabola/mkgmap/reader/osm/boundary/BoundaryPreparer.java	(working copy)
@@ -81,7 +81,8 @@
 		return new Osm5BoundaryDataSource();
 	}
 
-	private boolean onePass = true;
+	
+	private boolean workoutOnly = false;
 	private String boundaryFilename;
 	private String inDir;
 	private String outDir;
@@ -90,22 +91,31 @@
 		
 	}
 
+	/**
+	 * constructor for stand-alone usage (workout only)
+	 * @param in source directory or zip file 
+	 * @param out target directory
+	 */
+	private BoundaryPreparer(String in, String out){
+		this.inDir = in;
+		this.outDir = out;
+		this.workoutOnly = true;
+	}
+	
 	public boolean init(EnhancedProperties props,
 			ExecutorService additionalThreadPool) {
 		super.init(props, additionalThreadPool);
 		
+		if (workoutOnly)
+			return true;
 		this.boundaryFilename = props
 				.getProperty("createboundsfile", null);
 		this.inDir = props.getProperty("bounds", "bounds");
-		this.outDir = props.getProperty("preparer-out-dir", null);
-		if (this.outDir == null)
-			this.outDir = this.inDir;
-		this.onePass = props.getProperty("preparer-one-pass", false);
 		return boundaryFilename != null;
 	}
 
 	public void run() {
-		if (onePass == false && boundaryFilename != null) {
+		if (workoutOnly == false && boundaryFilename != null) {
 			long t1 = System.currentTimeMillis();
 			boolean prepOK = createRawData();
 			long t2 = System.currentTimeMillis();
@@ -118,6 +128,11 @@
 		workoutBoundaryRelations(inDir, outDir);
 	}
 
+	/**
+	 * Parse OSM data and create boundaries. Distribute the boundaries on a grid
+	 * with a fixed raster. 
+	 * @return true if data was successfully written, else false
+	 */
 	private boolean createRawData(){
 		File boundsDirectory = new File(outDir);
 		BoundarySaver saver = new BoundarySaver(boundsDirectory, BoundarySaver.RAW_DATA_FORMAT);
@@ -167,12 +182,8 @@
 		ExecutorService threadPool = Executors.newFixedThreadPool(maxJobs);
 
 		EnhancedProperties props = new EnhancedProperties();
-		props.setProperty("bounds", in);
-		props.setProperty("preparer-out-dir", out);
-		props.setProperty("preparer-one-pass", "true");
-		// is the separate out parameter required?
 		
-		BoundaryPreparer p = new BoundaryPreparer();
+		BoundaryPreparer p = new BoundaryPreparer(in, out);
 		p.init(props, threadPool);
 		try {
 			p.runPreparer();
@@ -185,23 +196,17 @@
 		
 		threadPool.shutdown();
 		
-		System.out.println("Bnd files converted in " + (System.currentTimeMillis()-t1) + " ms");
+		log.info("Bnd files converted in " + (System.currentTimeMillis()-t1) + " ms");
 	}
 
 	/**
 	 * Reworks all bounds files of the given directory so that all boundaries
 	 * are applied with the information with which boundary they intersect.<br/>
-	 * This information is added as tag "mkgmap:intersects_with" and contains a semicolon 
-	 * separated list of boundary ids.<br/>
-	 * Example:<br/>
-	 * <code>
-	 *   mkgmap:intersects_with=2:r51477;4:r87782
-	 * </code><br/>
-	 * The boundary tagged in such a way intersects with relation 51477 (admin level 2) 
-	 * and with relation 87782 (admin level 4).
+	 * The files are rewritten in the QUADTREE_DATA_FORMAT which is used in the 
+	 * LocationHook.
 	 * 
-	 * @param boundsDirectory
-	 *            the directory of the bounds files
+	 * @param inputDirName the directory or zip file name that identifies the input
+	 * @param outputDirName a directory name for the rewritten bnd files
 	 */
 	public void workoutBoundaryRelations(String inputDirName, String outputDirName) {
 		List<String> boundsFileNames = BoundaryUtil.getBoundaryDirContent(inputDirName);
@@ -226,20 +231,20 @@
 		
 		@Override
 		public String call() throws Exception {
-					log.info("Workout boundary relations in ", inputDirName + " " + boundsFileName);
-					long t1 = System.currentTimeMillis();
-					BoundaryQuadTree bqt = BoundaryUtil.loadQuadTree(inputDirName, boundsFileName);
-					long t2 = System.currentTimeMillis() - t1;
-					log.info("splitting " + boundsFileName + " took " + t2 + " ms");
-					if (bqt != null){
-						File outDir = new File(outputDirName);
-						BoundarySaver saver = new BoundarySaver(outDir, BoundarySaver.QUADTREE_DATA_FORMAT);
-						saver.setCreateEmptyFiles(false);
+			log.info("Workout boundary relations in ", inputDirName + " " + boundsFileName);
+			long t1 = System.currentTimeMillis();
+			BoundaryQuadTree bqt = BoundaryUtil.loadQuadTree(inputDirName, boundsFileName);
+			long t2 = System.currentTimeMillis() - t1;
+			log.info("splitting " + boundsFileName + " took " + t2 + " ms");
+			if (bqt != null){
+				File outDir = new File(outputDirName);
+				BoundarySaver saver = new BoundarySaver(outDir, BoundarySaver.QUADTREE_DATA_FORMAT);
+				saver.setCreateEmptyFiles(false);
 
-						saver.saveQuadTree(bqt, boundsFileName); 		
-						saver.end();
-					}
-					return boundsFileName;
+				saver.saveQuadTree(bqt, boundsFileName); 		
+				saver.end();
+			}
+			return boundsFileName;
 		}
 
 	}
Index: me/parabola/mkgmap/reader/osm/boundary/BoundaryQuadTree.java
===================================================================
--- me/parabola/mkgmap/reader/osm/boundary/BoundaryQuadTree.java	(revision 2239)
+++ me/parabola/mkgmap/reader/osm/boundary/BoundaryQuadTree.java	(working copy)
@@ -1,3 +1,15 @@
+/*
+ * 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.reader.osm.boundary;
 
 import java.awt.Rectangle;
@@ -31,25 +43,30 @@
 
 /**
  * A quadtree implementation to handle areas formed by boundaries.
- * @author GerdP
+ * Each node of the Quadtree stores a list of NodeElems. A NodeElem 
+ * stores one area and the related location relevant tags. 
+ * @author GerdP 
  *
  */
 public class BoundaryQuadTree {
 	private static final Logger log = Logger.getLogger(BoundaryQuadTree.class);
 	private static final boolean DEBUG = false;
+	// debugging  aid 
 	private static final String DEBUG_TREEPATH = "?";
 	private static final boolean DO_ALL_TESTS = false;
 
-	// the "normal" tags of the boundaries that are saved in this tree
+	// maps the "normal" tags of the boundaries that are saved in this tree to
+	// the boundaryId
 	private final HashMap<String, Tags> boundaryTags = new LinkedHashMap<String,Tags>();
-	// the location relevant info 
+	// maps the location relevant info to the boundaryId 
 	private final HashMap<String, BoundaryLocationInfo> preparedLocationInfo;
 	// property controlled preparer
 	private final BoundaryLocationPreparer preparer;
 	
 	private final Node root;
-	// the bounding box of the tree
+	// the bounding box of the quadtree
 	private final Rectangle bbox;
+	private final String bbox_key; 
 
 	// tags that can be returned in the get method
 	public final static String[] mkgmapTagsArray =  {
@@ -66,7 +83,8 @@
 		"mkgmap:admin_level11",
 		"mkgmap:postcode"
 	};
-	public final static short POSTCODE_ONLY = 1 << 11;  // 11: the position of "mkgmap:postcode" in the above aray 
+	// 11: the position of "mkgmap:postcode" in the above array
+	public final static short POSTCODE_ONLY = 1 << 11;   
 	
 	/**
 	 * Create a quadtree with the data in an open stream. 
@@ -81,35 +99,39 @@
 			throws IOException {
 		preparedLocationInfo = new LinkedHashMap<String, BoundaryLocationInfo> ();
 		preparer = new BoundaryLocationPreparer(props);
+		assert fileBbox != null: "parameter fileBbox must not be null";
 		this.bbox = new Rectangle(fileBbox.getMinLong(), fileBbox.getMinLat(),
 				fileBbox.getMaxLong() - fileBbox.getMinLong(), fileBbox.getMaxLat()
 				- fileBbox.getMinLat());
-
+		this.bbox_key = BoundaryUtil.getKey(this.bbox.y, this.bbox.x);
 		root = new Node(this.bbox);
 		
-		assert fileBbox != null: "parameter fileBbox must not be null";
 		readStreamQuadTreeFormat(inpStream,searchBbox);
 	}
 	
 	
 	/**
 	 * Create a quadtree for a given bounding box and a list of boundaries.
+	 * Involves costly calculations to split the areas.
 	 * @param givenBbox	The bounding box for the quadTree, only data within this box is used 
 	 * @param boundaries A list of boundaries. For better performance, the list should be sorted so that small areas come first.
 	 * @param props if not null, use it to set location names
 	 */
-	public BoundaryQuadTree (uk.me.parabola.imgfmt.app.Area givenBbox, List<Boundary> boundaries, EnhancedProperties props){
+	public BoundaryQuadTree(uk.me.parabola.imgfmt.app.Area givenBbox,
+			List<Boundary> boundaries, EnhancedProperties props) {
 		preparer = new BoundaryLocationPreparer(props);
+		assert givenBbox != null: "parameter givenBbox must not be null";
 		this.bbox = new Rectangle(givenBbox.getMinLong(), givenBbox.getMinLat(),
 				givenBbox.getMaxLong() - givenBbox.getMinLong(), 
 				givenBbox.getMaxLat() - givenBbox.getMinLat());
+		this.bbox_key = BoundaryUtil.getKey(this.bbox.y, this.bbox.x);
+		
 		root = new Node(this.bbox);
 		// extract the location relevant tags
 		preparedLocationInfo = preparer.getPreparedLocationInfo(boundaries);
 		if (boundaries == null || boundaries.size() == 0)
 			return;
 		
-		assert givenBbox != null: "parameter givenBbox must not be null";
 
 		HashMap<String,Boundary> bMap = new HashMap<String,Boundary>();
 		for (Boundary b: boundaries){
@@ -132,7 +154,7 @@
 	 * The returned Tags must not be modified by the caller.   
 	 */
 	public Tags get(Coord co){
-		Tags res = root.get(co, "_");
+		Tags res = root.get(co/*, "_"*/);
 		if (res == null && bbox.contains(co.getLongitude(),co.getLatitude())){
 			// we did not find the point, probably it lies on a boundary and
 			// the clauses regarding insideness of areas make it "invisible"
@@ -141,13 +163,13 @@
 			Coord neighbour2 = new Coord(co.getLatitude()  , co.getLongitude()-1);
 			Coord neighbour3 = new Coord(co.getLatitude()+1, co.getLongitude());
 			Coord neighbour4 = new Coord(co.getLatitude()  , co.getLongitude()+1);
-			res = root.get(neighbour1, "_");
+			res = root.get(neighbour1/*, "_"*/);
 			if (res == null)
-				res = root.get(neighbour2, "_");
+				res = root.get(neighbour2/*, "_"*/);
 			if (res == null)
-				res = root.get(neighbour3, "_");
+				res = root.get(neighbour3/*, "_"*/);
 			if (res == null)
-				res = root.get(neighbour4, "_");
+				res = root.get(neighbour4/*, "_"*/);
 		}
 		return res;
 	}
@@ -162,11 +184,12 @@
 	}
 	
 	/**
-	 * Create a map with boundary Ids and the area parts that are in the tree.
-	 * When the parts are added, they normally give the original boundary. 
+	 * Create a map with boundary Ids and the related area parts that 
+	 * are stored in the tree. When the parts are added, they normally 
+	 * give the original boundary (clipped by the bounding box of the quadtree). 
 	 * Exception: For overlapping boundaries with equal admin_levels only one
 	 * boundary will contain the complete area information.   
-	 * @return
+	 * @return A HashMap mapping BoundaryIds to a List with all area parts  
 	 */
 	public Map<String, List<Area>> getAreas(){
 		Map<String, List<Area>> areas = new HashMap<String, List<Area>>();
@@ -200,7 +223,7 @@
 	
 	/**
 	 * Return the area that is covered by a given admin level
-	 * @param admLevel
+	 * @param admLevel reasonable values are 2 .. 11 (inclusive)
 	 * @return
 	 */
 	public Area getCoveredArea (Integer admLevel){
@@ -211,7 +234,7 @@
 	 * Return boundary names relevant for the point defined by Coord 
 	 * @param co the point
 	 * @return A string with a boundary Id, optionally followed by pairs of admlevel:boundary Id.
-	 * Sample  
+	 * Sample: r1184826;6:r62579;4:r62372;2:r51477  
 	 */
 	public String getBoundaryNames(Coord co){
 		return root.getBoundaryNames(co);
@@ -250,10 +273,13 @@
 
 
 	/**
-	 * Write the TAGS sections. 
-	 * @param stream
-	 * @param tags
-	 * @param id
+	 * Write the TAGS sections of the QUADTREE_DATA_FORMAT. Each
+	 * section starts with the "TAGS" eye catcher followed by the
+	 * boundary id, the number of tags and the tags as 
+	 * key/value pairs.
+	 * @param stream an already opened OutputStream
+	 * @param tags the boundary tags
+	 * @param id the boundaryId
 	 * @throws IOException
 	 */
 	private void writeBoundaryTags(OutputStream stream, Tags tags, String id) throws IOException{
@@ -281,8 +307,9 @@
 
 	/**
 	 * Read a stream in QUADTREE_DATA_FORMAT 
-	 * @param inpStream
-	 * @param bbox
+	 * @param inpStream the already opened DataInputStream
+	 * @param bbox a bounding box. Areas not intersecting the bbox are 
+	 * ignored. 
 	 * @throws IOException
 	 */
 	private void readStreamQuadTreeFormat(DataInputStream inpStream,
@@ -344,7 +371,7 @@
 	}
 
 	/**
-	 * Fill the map preparedLocationInfo with data from the boundary tags
+	 * Fill the map preparedLocationInfo with data from the boundary tags.
 	 */
 	private void prepareLocationInfo() {
 		for (Entry<String, Tags> entry : boundaryTags.entrySet()) {
@@ -422,7 +449,7 @@
 		 * Return boundary names relevant for the point defined by Coord 
 		 * @param co the point
 		 * @return A string with a boundary Id, optionally followed by pairs of admlevel:boundary Id.
-		 * Sample  
+		 * Sample: r1184826;6:r62579;4:r62372;2:r51477  
 		 */
 		private String getBoundaryNames(Coord co) {
 			if (this.bounds.contains(co) == false)
@@ -459,7 +486,7 @@
 		 * @return a reference to the internal Tags or null if the point was not found. 
 		 * The returned Tags must not be modified by the caller.   
 		 */
-		private Tags get(Coord co, String treePath){
+		private Tags get(Coord co/*, String treePath*/){
 			if (this.bounds.contains(co) == false)
 				return null;
 			if (isLeaf){
@@ -477,7 +504,7 @@
 			}
 			else {
 				for (int i = 0; i < 4; i++){
-					Tags res = childs[i].get(co, treePath+i);
+					Tags res = childs[i].get(co/*, treePath+i*/);
 					if (res != null) 
 						return res; 
 				}
@@ -510,9 +537,9 @@
 
 		/**
 		 * Test if all areas in one node are distinct areas. This is wanted, but not 
-		 * absolutely needed.
+		 * absolutely needed. 
 		 * @param treePath Position in the quadtree. Used for GPX.
-		 * @return false if any area intersects with another and the 
+		 * @return false if any area intersects with another area and the 
 		 * intersection has a dimension.
 		 */
 		private boolean testIfDistinct(String treePath){
@@ -654,7 +681,7 @@
 		 * Calculate the area that is covered by boundaries of a given adminlevel
 		 * @param admLevel the admin_level, a value from 2 to 11 (including)
 		 * @param treePath A string that helps to identify the position in the quadtree 
-		 * @return
+		 * @return a new Area instance (might be empty)
 		 */
 		private Area getCoveredArea(Integer admLevel, String treePath){
 			HashMap<String,List<Area>> areas = new HashMap<String, List<Area>>();
@@ -670,7 +697,7 @@
 		}
 		
 		/**
-		 * See BoundaryQuadTree.getAreas()
+		 * See BoundaryQuadTree {@link #BoundaryQuadTree.getAreas()}
 		 * @param areas
 		 * @param treePath
 		 * @param admLevel
@@ -730,6 +757,7 @@
 		 * For intersections, create new areas with the merged 
 		 * location tags, and subtract the parts from the source 
 		 * areas. The result should be a reduced number of distinct areas.
+		 * @param treePath Identifies the position in the tree
 		 */
 		private void makeDistinct(String treePath){
 			if (isLeaf == false || nodes == null || nodes.size() <= 1)
@@ -856,7 +884,7 @@
 
 			long t2 = System.currentTimeMillis()-t1;
 			if (t2  > 1000)
-				System.out.println("merge required long time: " + t2 + " ms");
+				log.info(bbox_key + ": merge required long time: " + t2 + " ms");
 			if (DEBUG)
 				printNodes("end", treePath);
 
@@ -881,8 +909,10 @@
 			}
 		}
 		/**
+		 * Optimization:
 		 * The last nodes are likely to fully cover the quadtree bbox. 
-		 * Merge the tag information for them. 
+		 * Merge the tag information for them to avoid some splitting
+		 * and later merging.
 		 */
 		private void mergeLastRectangles(){
 			boolean done;
@@ -908,7 +938,8 @@
 
 		/**
 		 * The mergeBoundaries() algorithm can create almost empty
-		 * areas. These are removed here.
+		 * areas (points, lines, or extremely small intersections). 
+		 * These are removed here.
 		 * @param treePath
 		 */
 		private void removeEmptyAreas(String treePath){
@@ -929,6 +960,10 @@
 			}			
 		}
 
+		/**
+		 * allocate 4 childs with bounding boxes that have 1/4 of the 
+		 * size of the parent.  
+		 */
  		private void allocChilds(){
 			childs = new Node[4];
 			Coord center = bounds.getCenter();
@@ -948,7 +983,8 @@
 		}
 
 		/**
-		 * Split the tree into 4 equally sized parts.
+		 * Split the tree into 4 equally sized parts and
+		 * distribute the data.
 		 */
 		private void split(String treePath){
 			if (isLeaf == true){
@@ -992,7 +1028,13 @@
 		private String locationDataSrc;
 		private int srcPos;
 
-		
+		/**
+		 * Create a node elem. 
+		 * @param boundaryId The boundary Id
+		 * @param area the (part of the) boundary area stored in this node
+		 * @param refs A string containing boundaryIds and admin-level infos
+		 * of all boundaries with lower admin levels that share the same area. 
+		 */
 		NodeElem (String boundaryId, Area area, String refs){
 			srcPos = -1;
 			this.boundaryId = boundaryId;
@@ -1001,6 +1043,14 @@
 			calcLocTags();
 		}
 
+		/**
+		 * Create a new node elem as a partly copy of an existing 
+		 * NodeElem and a new area. 
+		 * @param other the existing NodeElem instance
+		 * @param area the new area 
+		 * @param srcPos identifies the position of other in the 
+		 * nodes list of the Node.
+		 */
 		NodeElem (NodeElem other, Area area, int srcPos){
 			this.area = area;
 			this.srcPos = srcPos;
@@ -1010,6 +1060,11 @@
 			this.locTags = other.locTags.copy();
 		}
 
+		/**
+		 * check if a NodeElem contains usable info.
+		 * @return false if either the area is not usable or 
+		 * the tags should be ignored.
+		 */
 		private boolean isValid(){
 			if (tagMask == 0 || area == null || area.isEmpty())
 				return false;
@@ -1034,7 +1089,12 @@
 		 */
 		private void calcLocTags(){
 			locTags = new Tags();
+			tagMask = 0;
 			BoundaryLocationInfo bInfo  = preparedLocationInfo.get(boundaryId);
+			if (bInfo == null){
+				log.error("unknown boundaryId " + boundaryId);
+				return;
+			}
 			if (bInfo.getZip() != null){
 				locTags.put("mkgmap:postcode",bInfo.getZip());
 			}
@@ -1078,6 +1138,11 @@
 			tagMask = calcLocationTagsMask();
 		}
 		
+		/**
+		 * Merge the locationDataSrc of two NodeElems.
+		 * The caller has to make sure that the merge makes sense.
+		 * @param toAdd The other NodeElem
+		 */
 		private void addLocationDataString (NodeElem toAdd){
 			BoundaryLocationInfo info = preparedLocationInfo.get(toAdd.boundaryId);
 			assert info.getAdmLevel() > 0 : "cannot use admLevel";
@@ -1093,9 +1158,8 @@
 		}
 		/**
 		 * Write a nodeElem an AREA segment of the QUADTREE_DATA_FORMAT. 
-		 * @param stream
-		 * @param nodeElem
-		 * @param treePath
+		 * @param stream the already opened OutputStream
+		 * @param treePath identifies the position within the tree
 		 * @throws IOException
 		 */
 		private void save(OutputStream stream, String treePath) throws IOException{
@@ -1136,7 +1200,6 @@
 		
 		/**
 		 * calculate a handy short value that represents the available location tags
-		 * @param tags 
 		 * @return a bit mask, a bit with value 1 means the corresponding entry in {@link locationTagNames } 
 		 * is available
 		 */
@@ -1151,7 +1214,7 @@
 		
 		/**
 		 * For debugging: Save the area in gpx format
-		 * @param desc
+		 * @param desc used as directory name  
 		 * @param treePath
 		 */
 		private void saveGPX(String desc, String treePath){
@@ -1176,7 +1239,9 @@
 		/**
 		 * Handle errors in OSM data. Two boundaries with equal levels should not intersect.
 		 * Special case: zip-code-only boundaries with same zip code 
-		 * @param other 
+		 * @param other the other NodeElem
+		 * @param bounds a bounding box for the intersection of the two areas. Used 
+		 * to create the error message.
 		 * @return null if no error, else a String with an error message
 		 */
 		private String checkAddTags(NodeElem other, uk.me.parabola.imgfmt.app.Area bounds){
@@ -1215,8 +1280,7 @@
 
 		/**
 		 * Add tags from src to locTags if they are missing 
-		 * @param dest
-		 * @param src
+		 * @param src the Tags to be added 
 		 */
 		private void addMissingTags(Tags src){
 			Iterator<Entry<String,String>> tagIter = src.entryIterator();
@@ -1229,6 +1293,11 @@
 		}
 	}
 
+	/***
+	 * Used to sort BoundaryLocationInfo. Input are boundaryIds.
+	 * @author gerd
+	 *
+	 */
 	public class AdminLevelCollator implements Comparator<String> {
 
 		public int compare(String o1, String o2) {
@@ -1269,7 +1338,7 @@
 				return 1;
 			if (!post1set && post2set)
 				return -1;
-			
+			// if all is equal, prefer the lower boundaryId
 			return o1.compareTo(o2);
 		}
 	}
Index: me/parabola/mkgmap/reader/osm/boundary/BoundaryUtil.java
===================================================================
--- me/parabola/mkgmap/reader/osm/boundary/BoundaryUtil.java	(revision 2239)
+++ me/parabola/mkgmap/reader/osm/boundary/BoundaryUtil.java	(working copy)
@@ -19,7 +19,6 @@
 import java.io.DataInputStream;
 import java.io.EOFException;
 import java.io.File;
-import java.io.FileFilter;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -38,19 +37,18 @@
 import uk.me.parabola.mkgmap.reader.osm.Tags;
 import uk.me.parabola.mkgmap.reader.osm.Way;
 import uk.me.parabola.util.EnhancedProperties;
-import uk.me.parabola.util.GpxCreator;
 import uk.me.parabola.util.Java2DConverter;
 
 public class BoundaryUtil {
 	private static final Logger log = Logger.getLogger(BoundaryUtil.class);
 
 	
-	public static class BoundaryFileFilter implements FileFilter {
-		public boolean accept(File pathname) {
-			return pathname.isFile() && pathname.getName().endsWith(".bnd");
-		}
-	}
-	
+	/**
+	 * Calculate the polygons that describe the area.
+	 * @param area the Area instance
+	 * @param id an id that is used to create meaningful messages, typically a boundary Id
+	 * @return A list of BoundaryElements (can be empty)
+	 */
 	public static List<BoundaryElement> splitToElements(Area area, String id) {
 		if (area.isEmpty()) {
 			return Collections.emptyList();
@@ -98,10 +96,10 @@
 			if (tryAgain == false){
 				// cannot convert this area
 				log.error(" first element is not outer. "+ bElements.get(0));
-				// TODO: remove debug code
-				createJavaCodeSnippet(area);
-				String fname = "bnd_gpx/first_not_outer" + id ;
-				GpxCreator.createGpx(fname, bElements.get(0).getPoints());
+				
+				//createJavaCodeSnippet(area);
+				//String fname = "bnd_gpx/first_not_outer" + id ;
+				//GpxCreator.createGpx(fname, bElements.get(0).getPoints());
 				return Collections.emptyList();
 			}
 			// try converting the area with rounded float values
@@ -111,26 +109,28 @@
 		}
 	}
 
-	/*
-	public static Area convertToArea(List<BoundaryElement> list) {
-		Area area = new Area();
-
-		for (BoundaryElement elem : list) {
-			if (elem.isOuter()) {
-				area.add(elem.getArea());
-			} else {
-				area.subtract(elem.getArea());
-			}
-		}
-		return area;
-	}
+	/**
+	 * Wrapper for {@link #loadQuadTree(String, String, uk.me.parabola.imgfmt.app.Area, EnhancedProperties)}
+	 * @param boundaryDirName a directory name or zip file containing the *.bnd file
+	 * @param boundaryFileName the *.bnd file name
+	 * @return
 	 */
-	
 	public static BoundaryQuadTree loadQuadTree (String boundaryDirName, 
 			String boundaryFileName){ 
 		return (loadQuadTree (boundaryDirName, boundaryFileName, null, null));
 	}
 	
+	/**
+	 * create a BoundaryQuadTree with the data in the given file. 
+	 * The routine opens the file and reads only the header 
+	 * to decide what format was used to write it
+	 * and calls the appropriate method to read the rest.
+	 * @param boundaryDirName a directory name or zip file containing the *.bnd file
+	 * @param boundaryFileName the *.bnd file name
+	 * @param searchBbox null or a bounding box. Data outside of this box is ignored.
+	 * @param props null or the properties to be used for the locator
+	 * @return
+	 */
 	public static BoundaryQuadTree loadQuadTree (String boundaryDirName, 
 			String boundaryFileName, 
 			uk.me.parabola.imgfmt.app.Area searchBbox, EnhancedProperties props){
@@ -175,7 +175,7 @@
 	 * read path iterator info from stream and create Area. 
 	 * Data is stored with float precision.  
 	 * @param inpStream the already opened DataInputStream 
-	 * @return a new Area object
+	 * @return a new Area object or null if not successful 
 	 * @throws IOException
 	 */
 	public static Area readArea(DataInputStream inpStream) throws IOException{
@@ -221,58 +221,28 @@
 	}
 	
 	/**
-	 * Common code to read the area info from a stream
-	 * @param inpStream
-	 * @param tags
+	 * Read boundary info saved in RAW_DATA_FORMAT. 
+	 * @param inpStream the already opened DataInputStream
+	 * @param fname the related file name of the *.bnd file
+	 * @param bbox a bounding box. Data outside of this box is ignored.
 	 * @return
 	 * @throws IOException
 	 */
-	public static Area readArea(DataInputStream inpStream, String id) throws IOException{
-		int noBElems = inpStream.readInt();
-	
-		// the first area is always an outer area and will be assigned to the variable
-		Area area = null;
-	
-		for (int i = 0; i < noBElems; i++) {
-			boolean outer = inpStream.readBoolean();
-			int noCoords = inpStream.readInt();
-			log.debug("No of coords",noCoords);
-			List<Coord> points = new ArrayList<Coord>(noCoords);
-			for (int c = 0; c < noCoords; c++) {
-				int lat = inpStream.readInt();
-				int lon = inpStream.readInt();
-				points.add(new Coord(lat, lon));
-			}
-	
-			Area elemArea = Java2DConverter.createArea(points);
-			if (outer) {
-				if (area == null) {
-					area = elemArea;
-				} else {
-					area.add(elemArea);
-				}
-			} else {
-				if (area == null) {
-					log.warn("Boundary: " + id);
-					log.warn("Outer way is tagged incosistently as inner way. Ignoring it.");
-					log.warn("Points: "+points);
-				} else {
-					area.subtract(elemArea);
-				}
-			}
-		}
-		if (area == null)
-			area = new Area();
-		return area;
-	}
-
 	private static List<Boundary> readStreamRawFormat(
 			DataInputStream inpStream, String fname,
-			uk.me.parabola.imgfmt.app.Area bbox, String rel) throws IOException			{
+			uk.me.parabola.imgfmt.app.Area bbox) throws IOException			{
 		List<Boundary> boundaryList = new ArrayList<Boundary>();
 
 
 		try {
+			// 1st read the mkgmap release the boundary file is created by
+			String mkgmapRel = inpStream.readUTF();
+			long createTime = inpStream.readLong();
+			
+			if (log.isDebugEnabled()) {
+				log.debug("File created by mkgmap release",mkgmapRel,"at",new Date(createTime));
+			}
+			
 			while (true) {
 				int minLat = inpStream.readInt();
 				int minLong = inpStream.readInt();
@@ -284,27 +254,58 @@
 				int bSize = inpStream.readInt();
 				log.debug("Size:",bSize);
 
-				if ( bbox == null || bbox.intersects(rBbox)) {
+				if (bbox == null || bbox.intersects(rBbox)) {
 					log.debug("Bbox intersects. Load the boundary");
 					Tags tags = new Tags();
 					int noOfTags = inpStream.readInt();
-					String id = "?";
+					String id = "?"; 
 					for (int i = 0; i < noOfTags; i++) {
 						String name = inpStream.readUTF();
 						String value = inpStream.readUTF();
 						// boundary.id was always saved together with the other tags
 						if (name.equals("mkgmap:boundaryid")){  
 						    id = value;								
-						    continue;
-            }
+						    continue; 
+						}
 						if (name.equals("mkgmap:lies_in") == false) // ignore info from older preparer version 
-							tags.put(name, value.intern());
+							tags.put(name, value.intern()); 					
 					}
+
+					int noBElems = inpStream.readInt();
+					assert noBElems > 0;
+					
+					// the first area is always an outer area and will be assigned to the variable
 					Area area = null;
-					if (rel.endsWith(BoundarySaver.RAW_DATA_FORMAT))
-						area = readArea(inpStream);
-					else // area info was saved in legacy format 
-						area = readArea(inpStream, id);
+					
+					for (int i = 0; i < noBElems; i++) {
+						boolean outer = inpStream.readBoolean();
+						int noCoords = inpStream.readInt();
+						log.debug("No of coords",noCoords);
+						List<Coord> points = new ArrayList<Coord>(noCoords);
+						for (int c = 0; c < noCoords; c++) {
+							int lat = inpStream.readInt();
+							int lon = inpStream.readInt();
+							points.add(new Coord(lat, lon));
+						}
+
+						Area elemArea = Java2DConverter.createArea(points);
+						if (outer) {
+							if (area == null) {
+								area = elemArea;
+							} else {
+								area.add(elemArea);
+							}
+						} else {
+							if (area == null) {
+								log.warn("Boundary: "+tags);
+								log.warn("Outer way is tagged incosistently as inner way. Ignoring it.");
+								log.warn("Points: "+points);
+							} else {
+								area.subtract(elemArea);
+							}
+						}
+					}
+
 					if (area != null) {
 						Boundary boundary = new Boundary(area, tags,id);
 						boundaryList.add(boundary);
@@ -319,8 +320,8 @@
 			}
 		} catch (EOFException exp) {
 			// it's always thrown at the end of the file
-			//				log.error("Got EOF at the end of the file");
-		} 
+			// log.error("Got EOF at the end of the file");
+		}  		
 		return boundaryList;
 	}
 
@@ -380,61 +381,6 @@
 		return names;
 	}
 	
-	/*
-	private static List<Boundary> mergeBoundaries(List<Boundary> boundaryList) {
-		int noIdBoundaries = 0;
-		Map<String, Boundary> mergeMap = new HashMap<String, Boundary>();
-		for (Boundary toMerge : boundaryList) {
-			String bId = toMerge.getId();
-			if (bId == null) {
-				noIdBoundaries++;
-				mergeMap.put("n" + noIdBoundaries, toMerge);
-			} else {
-				Boundary existingBoundary = mergeMap.get(bId);
-				if (existingBoundary == null) {
-					mergeMap.put(bId, toMerge);
-				} else {
-					if (log.isInfoEnabled())
-						log.info("Merge boundaries", existingBoundary.getTags(), "with", toMerge.getTags());
-					existingBoundary.getArea().add(toMerge.getArea());
-					
-					// Merge the mkgmap:lies_in tag
-					// They should be the same but better to check that...
-					String liesInTagExist = existingBoundary.getTags().get("mkgmap:lies_in");
-					String liesInTagMerge = toMerge.getTags().get("mkgmap:lies_in");
-					if (liesInTagExist != null && liesInTagExist.equals(liesInTagMerge)==false) {
-						if (liesInTagMerge == null) {
-							existingBoundary.getTags().remove("mkgmap:lies_in");
-						} else {
-							// there is a difference in the lies_in tag => keep the equal ids
-							Set<String> existIds = new HashSet<String>(Arrays.asList(liesInTagExist.split(";")));
-							Set<String> mergeIds = new HashSet<String>(Arrays.asList(liesInTagMerge.split(";")));
-							existIds.retainAll(mergeIds);
-							if (existIds.isEmpty()) {
-								existingBoundary.getTags().remove("mkgmap:lies_in");
-							} else {
-								StringBuilder newLiesIn = new StringBuilder();
-								for (String liesInEntry : existIds) {
-									if (newLiesIn.length() > 0) {
-										newLiesIn.append(";");
-									}
-									newLiesIn.append(liesInEntry);
-								}
-								existingBoundary.getTags().put("mkgmap:lies_in", newLiesIn.toString());
-							}
-						}
-					}
-				}
-			}
-		}
-		if (noIdBoundaries > 0) {
-			log.error(noIdBoundaries
-					+ " without boundary id. Could not merge them.");
-		}
-		return new ArrayList<Boundary>(mergeMap.values());
-	}
-	 */
-	
 	public static final int RASTER = 50000;
 
 	public static int getSplitBegin(int value) {
@@ -480,7 +426,17 @@
 		return new uk.me.parabola.imgfmt.app.Area(lat, lon, lat+RASTER, lon+RASTER);
 	}
 
-	
+	/**
+	 * Create and fill a BoundaryQuadTree. Two formats are supported: RAW_DATA_FORMAT and
+	 * QUADTREE_DATA_FORMAT. 
+	 * @param stream an already opened InputStream
+	 * @param fname the file name of the corresponding *.bnd file
+	 * @param searchBbox a bounding box or null. If not null, area info outside of this
+	 * bounding box is ignored. 
+	 * @param props properties to be used or null 
+	 * @return on success it returns a new BoundaryQuadTree, else null 
+	 * @throws IOException
+	 */
 	private static BoundaryQuadTree loadQuadTreeFromStream(InputStream stream, 
 			String fname,
 			uk.me.parabola.imgfmt.app.Area searchBbox, 
@@ -504,7 +460,7 @@
 					
 				}
 				else{
-					List<Boundary> boundaryList = readStreamRawFormat(inpStream, fname,searchBbox, mkgmapRel);
+					List<Boundary> boundaryList = readStreamRawFormat(inpStream, fname, searchBbox);
 					if (boundaryList == null || boundaryList.isEmpty())
 						return null;
 					bqt = new BoundaryQuadTree(qtBbox, boundaryList, props);
@@ -533,7 +489,9 @@
 		}
 	}
 	/**
-	 * Helper to ease the reporting of errors.
+	 * Helper to ease the reporting of errors. Creates a java code snippet 
+	 * that can be compiled to have the same area.
+	 * @param area the area for which the code should be produced
 	 */
 	public static void createJavaCodeSnippet(Area area) {
 		double[] res = new double[6];
Index: me/parabola/mkgmap/reader/osm/LocationHook.java
===================================================================
--- me/parabola/mkgmap/reader/osm/LocationHook.java	(revision 2239)
+++ me/parabola/mkgmap/reader/osm/LocationHook.java	(working copy)
@@ -28,6 +28,7 @@
 public class LocationHook extends OsmReadingHooksAdaptor {
 	private static final boolean PRINT_RESULT = false;
 	private static final Logger log = Logger.getLogger(LocationHook.class);
+	// counters for stats
 	long cntQTSearch = 0;
 	long cntNotFnd = 0;
 	long cntwayNotFnd = 0;
@@ -67,7 +68,7 @@
 			boundaryDirName = props.getProperty("bounds", "bounds");
 			long t1 = System.currentTimeMillis();
 
-			synchronized (BOUNDS_OPTION) {
+			synchronized (boundaryDirName) {
 				// checking of the boundary dir is expensive
 				// check once only and reuse the result
 				if (boundaryDirName.equals(checkedBoundaryDirName)) {
@@ -107,19 +108,17 @@
 		}
 
 		long dt = (System.currentTimeMillis() - t1);
+		log.info("======= LocationHook Stats =====");             
+		log.info("QuadTree searches    : "  + cntQTSearch);             
+		log.info("unsuccesfull         : "  + cntNotFnd);             
+		log.info("unsuccesfull for ways: "  + cntwayNotFnd);             
 		log.info("Location hook finished in "+
 				dt+ " ms");
-		System.out.println("======= Stats =====");             
-		System.out.println("QuadTree searches    : "  + cntQTSearch);             
-		System.out.println("unsuccesfull         : "  + cntNotFnd);             
-		System.out.println("unsuccesfull for ways: "  + cntwayNotFnd);             
-		System.out.println("Location hook finished in "+
-				dt+ " ms");
 
 	}
 
 	/**
-	 * Retrieve all elements for which the boundary assignment should be performed.
+	 * Iterate over all elements for which the boundary assignment should be performed.
 	 */
 	private void processLocationRelevantElements() {
 		// process all nodes that might be converted to a garmin node (tagcount > 0)
@@ -128,8 +127,7 @@
 				if (saver.getBoundingBox().contains(node.getLocation())){
 					processElem(node);
 					if (PRINT_RESULT)
-						System.out.println("N " + node.getId() + " " + calcLocationTagsMask(node) 
-								+ " " + tagsToString(node));
+						System.out.println("N " + node.getId() + " " + locationTagsToString(node));
 				}
 			}
 		}
@@ -139,17 +137,15 @@
 			if (way.getTagCount() > 0) {
 				processElem(way);
 				if (PRINT_RESULT)
-					System.out.println("W " + way.getId() + " " + calcLocationTagsMask(way)
-							+ " " + tagsToString(way));
+					System.out.println("W " + way.getId() + " " + locationTagsToString(way));
 			}
 		}
 	}
 
 	/**
-	 * For each element in elemList, performs a test against the quadtree. 
-	 * If found, assign the tags and check if the element is done  
-	 * @param elemList the list of elements
-	 * @param currBoundaries a list of boundaries handled in the current level
+	 * Extract the location info and perform a test 
+	 * against the BoundaryGrid. If found, assign the tags.  
+	 * @param elem A way or Node
 	 */
 	private void processElem(Element elem){
 		Tags tags = null;
@@ -215,26 +211,13 @@
 	}
 			
 	/**
-	 * Calculate a short value that represents the location relevant tags that are set 
-	 * @param elem
-	 * @return the short value. Each bit in the value that is 1 means that the corresponding entry 
-	 * in mkgmapTagsArray is available.
-	 */
-	private short calcLocationTagsMask(Element elem){
-		short res = 0;
-		for (int i = 0; i < BoundaryQuadTree.mkgmapTagsArray.length; i++){
-			if (elem.getTag(BoundaryQuadTree.mkgmapTagsArray[i] ) != null)
-				res |= (1 << i);
-		}
-		return res;
-	}
-
-	/**
-	 * Create a string with location relevant tags ordered by admin_level
-	 * @param elem
+	 * Debugging:
+	 * Create a string with location relevant tags ordered by admin_level.
+	 * Can be used to compare results with tools like diff.
+	 * @param elem the element 
 	 * @return A new String object
 	 */
-	private String tagsToString(Element elem){
+	private String locationTagsToString(Element elem){
 		StringBuilder res = new StringBuilder();
 		for (int i = BoundaryQuadTree.mkgmapTagsArray.length-1; i >= 0; --i){
 			String tagVal = elem.getTag(BoundaryQuadTree.mkgmapTagsArray[i] );
