Index: src/uk/me/parabola/mkgmap/reader/osm/boundary/BoundaryUtil.java
===================================================================
--- src/uk/me/parabola/mkgmap/reader/osm/boundary/BoundaryUtil.java	(revision 2519)
+++ src/uk/me/parabola/mkgmap/reader/osm/boundary/BoundaryUtil.java	(working copy)
@@ -28,8 +28,10 @@
 import java.util.Date;
 import java.util.Enumeration;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.regex.Pattern;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
@@ -41,6 +43,7 @@
 import uk.me.parabola.mkgmap.reader.osm.Way;
 import uk.me.parabola.util.EnhancedProperties;
 import uk.me.parabola.util.Java2DConverter;
+import uk.me.parabola.util.MultiHashMap;
 
 public class BoundaryUtil {
 	private static final Logger log = Logger.getLogger(BoundaryUtil.class);
@@ -593,6 +596,7 @@
 						boundaryList = readStreamLegacyFormat(inpStream, fname,searchBbox);
 					if (boundaryList == null || boundaryList.isEmpty())
 						return null;
+					boundaryList = mergePostalCodes(boundaryList);
 					bqt = new BoundaryQuadTree(qtBbox, boundaryList, props);
 				}
 			} catch (EOFException exp) {
@@ -610,7 +614,143 @@
 		return bqt;
 	}
 	
+	/**
+	 * Merges boundaries with the same postal code.
+	 * @param boundaries a list of boundaries
+	 * @return the boundary list with postal code areas merged
+	 */
+	private static List<Boundary> mergePostalCodes(List<Boundary> boundaries) {
+		List<Boundary> mergedList = new ArrayList<Boundary>(boundaries.size());
+		
+		MultiHashMap<String, Boundary> equalPostalCodes = new MultiHashMap<String, Boundary>();
+		for (Boundary boundary : boundaries) {
+			String postalCode = getPostalCode(boundary.getTags());
+			if (postalCode == null) {
+				// no postal code boundary
+				mergedList.add(boundary);
+			} else {
+				// postal code boundary => merge it later
+				equalPostalCodes.add(postalCode, boundary);
+			}
+		}
+
+		for (Entry<String, List<Boundary>> postCodeBoundary : equalPostalCodes
+				.entrySet()) {
+			if (postCodeBoundary.getValue().size() == 1) {
+				// nothing to merge
+				mergedList.addAll(postCodeBoundary.getValue());
+				continue;
+			}
+			
+			// there are more than 2 boundaries with the same post code
+			// => merge them
+			Area newPostCodeArea = new Area();
+			for (Boundary b : postCodeBoundary.getValue()) {
+				newPostCodeArea.add(b.getArea());
+				
+				// remove the post code tags from the original boundary
+				if (b.getTags().get("postal_code") != null) {
+					b.getTags().remove("postal_code");
+				} else if ("postal_code".equals(b.getTags().get("boundary"))) {
+					b.getTags().remove("boundary");
+					b.getTags().remove("name");
+				}
+				
+				// check if the boundary contains other boundary information
+				if (isAdministrativeBoundary(b)) {
+					mergedList.add(b);
+				} else {
+					log.info("Boundary", b.getId(), b.getTags(), "contains no more boundary tags. Skipping it.");
+				}
+			}
+			
+			Tags postalCodeTags = new Tags();
+			postalCodeTags.put("postal_code", postCodeBoundary.getKey());
+			Boundary postalCodeBoundary = new Boundary(newPostCodeArea, postalCodeTags, "p"+postCodeBoundary.getKey());
+
+			log.info("Merged", postCodeBoundary.getValue().size(), "postal code boundaries for postal code", postCodeBoundary.getKey());
+			mergedList.add(postalCodeBoundary);
+		}
+		
+		return mergedList;
+	}
+	
+	/**
+	 * Checks if the given boundary contains tags for an administrative boundary.
+	 * @param b a boundary
+	 * @return <code>true</code> administrative boundary or postal code; 
+	 * <code>false</code> element cannot be used for precompiled bounds 
+	 */
+	public static boolean isAdministrativeBoundary(Boundary b) {
+		
+		if (b.getId().startsWith("r")) {
+			String type = b.getTags().get("type");
+			
+			if ("boundary".equals(type) || "multipolygon".equals(type)) {
+				String boundaryVal = b.getTags().get("boundary");
+				if ("administrative".equals(boundaryVal)) {
+					// for boundary=administrative the admin_level must be set
+					if (b.getTags().get("admin_level") == null) {
+						return false;
+					}
+					// and a name must be set (check only for a tag containing name
+					Iterator<Entry<String,String>> tagIterator = b.getTags().entryIterator();
+					while (tagIterator.hasNext()) {
+						Entry<String,String> tag  = tagIterator.next();
+						if (tag.getKey().contains("name")) {
+							return true;
+						}
+					}
+					// does not contain a name tag => do not use it
+					return false;					
+				}  else {
+					return false;
+				}
+			} else {
+				return false;
+			}
+		} else if (b.getId().startsWith("w")) {
+			// the boundary tag must be "administrative" or "postal_code"
+			String boundaryVal = b.getTags().get("boundary");
+			if ("administrative".equals(boundaryVal)) {
+				// for boundary=administrative the admin_level must be set
+				if (b.getTags().get("admin_level") == null) {
+					return false;
+				}
+				// and a name must be set (check only for a tag containing name)
+				Iterator<Entry<String,String>> tagIterator = b.getTags().entryIterator();
+				while (tagIterator.hasNext()) {
+					Entry<String,String> tag  = tagIterator.next();
+					if (tag.getKey().contains("name")) {
+						return true;
+					}
+				}
+				// does not contain a name tag => do not use it
+				return false;
+			} else {
+				return false;
+			}
+		} else {
+			return false;
+		}
+	}
+	
 	
+	private static String getPostalCode(Tags tags) {
+		String zip = tags.get("postal_code");
+		if (zip == null) {
+			if ("postal_code".equals(tags.get("boundary"))){
+				String name = tags.get("name"); 
+				if (name != null) {
+					String[] nameParts = name.split(Pattern.quote(" "));
+					if (nameParts.length > 0) {
+						zip = nameParts[0].trim();
+					}
+				}
+			}
+		}
+		return zip;
+	}
 	/**
 	 * Helper to ease the reporting of errors. Creates a java code snippet 
 	 * that can be compiled to have the same area.
Index: src/uk/me/parabola/mkgmap/reader/osm/boundary/BoundaryElementSaver.java
===================================================================
--- src/uk/me/parabola/mkgmap/reader/osm/boundary/BoundaryElementSaver.java	(revision 2519)
+++ src/uk/me/parabola/mkgmap/reader/osm/boundary/BoundaryElementSaver.java	(working copy)
@@ -17,11 +17,13 @@
 import java.util.Map.Entry;
 
 import uk.me.parabola.log.Logger;
+import uk.me.parabola.mkgmap.build.Locator;
 import uk.me.parabola.mkgmap.reader.osm.Element;
 import uk.me.parabola.mkgmap.reader.osm.ElementSaver;
 import uk.me.parabola.mkgmap.reader.osm.Node;
 import uk.me.parabola.mkgmap.reader.osm.OsmConverter;
 import uk.me.parabola.mkgmap.reader.osm.Relation;
+import uk.me.parabola.mkgmap.reader.osm.Tags;
 import uk.me.parabola.mkgmap.reader.osm.Way;
 import uk.me.parabola.util.EnhancedProperties;
 
@@ -41,6 +43,8 @@
 		this.saver = saver;
 	}
 
+	private final static Locator locator = new Locator();
+	
 	/**
 	 * Checks if the given element is an administrative boundary or a
 	 * postal code area.
@@ -59,6 +63,17 @@
 					if (element.getTag("admin_level") == null) {
 						return false;
 					}
+					if (element.getTag("admin_level").equals("2")) {
+						Tags copyTags = new Tags();
+						for (Entry<String,String> tag : element.getEntryIteratable()) {
+							copyTags.put(tag.getKey(), tag.getValue());
+						}
+						String iso = locator.getCountryISOCode(copyTags);
+						if (iso == null) {
+							log.warn("Ignore admin_level 2 element:", element.toBrowseURL(), element.toTagString());
+							return false;
+						}
+					}
 					// and a name must be set (check only for a tag containing name
 					for (Entry<String,String> tag : element.getEntryIteratable()) {
 						if (tag.getKey().contains("name")) {
@@ -103,7 +118,17 @@
 				if (element.getTag("admin_level") == null) {
 					return false;
 				}
-				
+				if (element.getTag("admin_level").equals("2")) {
+					Tags copyTags = new Tags();
+					for (Entry<String,String> tag : element.getEntryIteratable()) {
+						copyTags.put(tag.getKey(), tag.getValue());
+					}
+					String iso = locator.getCountryISOCode(copyTags);
+					if (iso == null) {
+						log.warn("Ignore admin_level 2 element:", element.toBrowseURL(), element.toTagString());
+						return false;
+					}
+				}
 				// and a name must be set (check only for a tag containing name)
 				for (Entry<String,String> tag : element.getEntryIteratable()) {
 					if (tag.getKey().contains("name")) {
