Index: resources/help/en/logging
===================================================================
--- resources/help/en/logging	(revision 4740)
+++ resources/help/en/logging	(working copy)
@@ -25,7 +25,9 @@
 uk.me.parabola.mkgmap.reader.osm.xml.level=INFO
 uk.me.parabola.mkgmap.reader.osm.RestrictionRelation.level=FINE
 uk.me.parabola.mkgmap.reader.osm.Restriction.level=FINE
-# For ConsoleHandler
+# Default logger
+global.level=INFO
+# For ConsoleHandlerM
 java.util.logging.ConsoleHandler.level=WARNING
 java.util.logging.ConsoleHandler.formatter=uk.me.parabola.log.UsefulFormatter
 # For FileHandler
Index: scripts/download/mkdoc
===================================================================
--- scripts/download/mkdoc	(revision 4740)
+++ scripts/download/mkdoc	(working copy)
@@ -16,7 +16,9 @@
 for f in *.txt
 do
 	mwconv -t text $f > ../dist/doc/$f
+	mwconv -l 1 -t html $f > ../dist/doc/${f/.txt/.html}
 done
+exit
 
 # Use the actual options help file.
 cp ../resources/help/en/options ../dist/doc/options.txt
Index: src/uk/me/parabola/imgfmt/app/mdr/LargeListSorter.java
===================================================================
--- src/uk/me/parabola/imgfmt/app/mdr/LargeListSorter.java	(revision 4740)
+++ src/uk/me/parabola/imgfmt/app/mdr/LargeListSorter.java	(working copy)
@@ -29,9 +29,11 @@
  */
 public abstract class LargeListSorter<T extends NamedRecord> {
 	private final Sort sort;
+	private final boolean useCache;
 	
-	public LargeListSorter(Sort sort) {
+	public LargeListSorter(Sort sort, boolean useCache) {
 		this.sort = sort;
+		this.useCache = useCache;
 	}
 
 	/**
@@ -39,6 +41,8 @@
 	 * @param list list of records.
 	 */
 	public void sort(List<T> list) {
+		if (list.size() <= 1)  // Mdr7: can have no streets or single element in partial list
+			return;
 		mergeSort(0, list, 0, list.size());
 	}
 	
@@ -51,13 +55,15 @@
 	 */
 	private void mergeSort(int depth, List<T> list, int start, int len) {
 		// we split if the number is very high and recursion is not too deep
-		if (len > 1_000_000 && depth < 3) {
+		if (len > 500_000 && depth < 4) {
 			mergeSort(depth+1,list, start, len / 2); // left
 			mergeSort(depth+1,list, start + len / 2, len - len / 2); // right
 			merge(list,start,len);
 		} else {
 			// sort one chunk
-			Map<String, byte[]> cache = new HashMap<>();
+			Map<String, byte[]> cache = null;
+			if (useCache)
+				cache = new HashMap<>();
 			List<SortKey<T>> keys = new ArrayList<>(len);
 
 			for (int i = start; i < start + len; i++) {
@@ -82,7 +88,7 @@
 		int stop2 = start + len;
 		boolean fetch1 = true;
 		boolean fetch2 = true;
-		List<T> merged = new ArrayList<>();
+		List<T> merged = new ArrayList<>(len);
 		SortKey<T> sk1 = null;
 		SortKey<T> sk2 = null;
 		while (pos1 < stop1 &&  pos2 < stop2) {
Index: src/uk/me/parabola/imgfmt/app/mdr/Mdr11.java
===================================================================
--- src/uk/me/parabola/imgfmt/app/mdr/Mdr11.java	(revision 4740)
+++ src/uk/me/parabola/imgfmt/app/mdr/Mdr11.java	(working copy)
@@ -61,8 +61,8 @@
 		pois.trimToSize();
 		Sort sort = getConfig().getSort();
 
-		LargeListSorter<Mdr11Record> sorter = new LargeListSorter<Mdr11Record>(sort) {
-			
+		LargeListSorter<Mdr11Record> sorter = new LargeListSorter<Mdr11Record>(sort, false) {
+			// typical 15% cache hit-rate so cache probably not worth-while
 			@Override
 			protected SortKey<Mdr11Record> makeKey(Mdr11Record r, Sort sort, Map<String, byte[]> cache) {
 				return sort.createSortKey(r, r.getName(), r.getMapIndex(), cache);
Index: src/uk/me/parabola/imgfmt/app/mdr/Mdr7.java
===================================================================
--- src/uk/me/parabola/imgfmt/app/mdr/Mdr7.java	(revision 4740)
+++ src/uk/me/parabola/imgfmt/app/mdr/Mdr7.java	(working copy)
@@ -51,11 +51,12 @@
 
 	private int partialInfoSize;
 	private Set<String> exclNames;
-	private final Sort sort;
+	private Sort sort;
 	private int maxPrefixCount;
 	private int maxSuffixCount;
 	private boolean magicIsValid;
 	private int magic;
+	private Collator collator;
 
 	public Mdr7(MdrConfig config) {
 		setConfig(config);
@@ -64,6 +65,8 @@
 		exclNames = config.getMdr7Excl();
 		codepage = sort.getCodepage();
 		isMulti = sort.isMulti();
+		collator = sort.getCollator();
+		collator.setStrength(Collator.SECONDARY);
 	}
 
 	public void addStreet(int mapId, String name, int lblOffset, int strOff, Mdr5Record mdrCity) {
@@ -204,7 +207,8 @@
 	@Override
 	protected void preWriteImpl() {
 		
-		LargeListSorter<Mdr7Record> partialSorter = new LargeListSorter<Mdr7Record>(sort) {
+		LargeListSorter<Mdr7Record> partialSorter = new LargeListSorter<Mdr7Record>(sort, true) {
+			// typical 50% cache hit-rate so cache probably worth-while
 			@Override
 			protected SortKey<Mdr7Record> makeKey(Mdr7Record r, Sort sort, Map<String, byte[]> cache) {
 				return sort.createSortKey(r, r.getPartialName(), 0, cache); // first sort by partial name only
@@ -211,14 +215,12 @@
 			}
 		};
 		
-		ArrayList<Mdr7Record> sorted = new ArrayList<>(allStreets);
-		allStreets.clear();
+		ArrayList<Mdr7Record> sorted = allStreets;
+		allStreets = new ArrayList<>(sorted.size());
 		partialSorter.sort(sorted);
 		// list is now sorted by partial name only, we have to group by name and map index now
 		String lastPartial = null;
 		List<Mdr7Record> samePartial = new ArrayList<>();
-		Collator collator = sort.getCollator();
-		collator.setStrength(Collator.SECONDARY);
 		for (int i = 0; i < sorted.size(); i++) {
 			Mdr7Record r = sorted.get(i);
 			String partial = r.getPartialName();
@@ -246,16 +248,20 @@
 		// Basecamp needs the records grouped by partial name, full name, and map index.
 		// This sometimes presents search results in the wrong order. The partial sort fields allow to
 		// tell the right order.
-		
-		LargeListSorter<Mdr7Record> fullNameSorter = new LargeListSorter<Mdr7Record>(sort) {
-			@Override
-			protected SortKey<Mdr7Record> makeKey(Mdr7Record r, Sort sort, Map<String, byte[]> cache) {
-				return sort.createSortKey(r, r.getName(), r.getMapIndex(), cache);
-			}
-		};
-		
-		
-		fullNameSorter.sort(samePartial);
+		if (samePartial.size() > 1) {
+			LargeListSorter<Mdr7Record> fullNameSorter = new LargeListSorter<Mdr7Record>(sort, true) {
+				// Very high hit-rate so use cache.
+				// NB: Mostly there are only a few records to sort here (the common case is 1),
+				// but must use SortKeys rather than a Comparator with Sort.collator.compare(names)
+				// because the special char handling is different.
+				@Override
+				protected SortKey<Mdr7Record> makeKey(Mdr7Record r, Sort sort, Map<String, byte[]> cache) {
+					return sort.createSortKey(r, r.getName(), r.getMapIndex(), cache);
+				}
+			};
+			fullNameSorter.sort(samePartial);
+		}
+
 		Mdr7Record last = null;
 		int recordNumber = streets.size();
 		
@@ -283,8 +289,6 @@
 	public void writeSectData(ImgFileWriter writer) {
 		boolean hasStrings = hasFlag(MDR7_HAS_STRING);
 		boolean hasNameOffset = hasFlag(MDR7_HAS_NAME_OFFSET);
-		Collator collator = sort.getCollator();
-		collator.setStrength(Collator.SECONDARY); 
 		Mdr7Record last = null;
 		for (Mdr7Record s : streets) {
 			addIndexPointer(s.getMapIndex(), s.getIndex());
Index: src/uk/me/parabola/imgfmt/app/net/RouteArc.java
===================================================================
--- src/uk/me/parabola/imgfmt/app/net/RouteArc.java	(revision 4740)
+++ src/uk/me/parabola/imgfmt/app/net/RouteArc.java	(working copy)
@@ -418,7 +418,9 @@
 			int compactedRatio = lengthRatio / 2 - 8;
 			assert compactedRatio > 0 && compactedRatio < 8;
 			curveData = new int[1];
-			curveData[0] = (compactedRatio << 5) | ((dh >> 3) & 0x1f);
+			//curveData[0] = (compactedRatio << 5) | ((dh >> 3) & 0x1f);
+			//try rounding this as well
+			curveData[0] = (compactedRatio << 5) | (((dh+4) >> 3) & 0x1f);
 			
 			/* check math:
 			int dhx = curveData[0] & 0x1f;
Index: src/uk/me/parabola/imgfmt/app/trergn/Polyline.java
===================================================================
--- src/uk/me/parabola/imgfmt/app/trergn/Polyline.java	(revision 4740)
+++ src/uk/me/parabola/imgfmt/app/trergn/Polyline.java	(working copy)
@@ -179,6 +179,8 @@
 
 		if(labelOff != 0)
 			type |= 0x20;		// has label
+		if (direction)
+			type |= FLAG_DIR;   // %%% not sure if this bit means same thing for extended types
 		if(extraBytes != null)
 			type |= 0x80;		// has extra bytes
 		stream.write(type >> 8);
Index: src/uk/me/parabola/mkgmap/filters/ShapeMergeFilter.java
===================================================================
--- src/uk/me/parabola/mkgmap/filters/ShapeMergeFilter.java	(revision 4740)
+++ src/uk/me/parabola/mkgmap/filters/ShapeMergeFilter.java	(working copy)
@@ -54,7 +54,7 @@
 	 * @return list of merged shapes (might be identical to original list)
 	 */
 	public List<MapShape> merge(List<MapShape> shapes) {
-		if (shapes.size() <= 1)
+		if (shapes.size() <= 1 || true)
 			return shapes;
 		List<MapShape> mergedShapes = new ArrayList<>();
 		List<MapShape> usableShapes = new ArrayList<>();
Index: src/uk/me/parabola/mkgmap/main/Main.java
===================================================================
--- src/uk/me/parabola/mkgmap/main/Main.java	(revision 4740)
+++ src/uk/me/parabola/mkgmap/main/Main.java	(working copy)
@@ -605,6 +605,7 @@
 			log.info("nothing to do for combiners.");
 			return;
 		}
+		Logger.defaultLogger.write("Combiners started: " + new Date());
 		log.info("Combining maps");
 
 		args.setSort(getSort(args));
Index: src/uk/me/parabola/util/ShapeSplitter.java
===================================================================
--- src/uk/me/parabola/util/ShapeSplitter.java	(revision 4740)
+++ src/uk/me/parabola/util/ShapeSplitter.java	(working copy)
@@ -471,7 +471,9 @@
 				return cmp;
 			// case where when have same start & end
 			// return the shape as lower than the hole, to handle first
-			cmp = other.areaOrHole - this.areaOrHole;
+//			cmp = other.areaOrHole - this.areaOrHole;
+			// Above is not reliable, there might be an earlier shape. try doing larger area first
+			cmp = Long.compare(this.areaToLine * this.areaOrHole, other.areaToLine * other.areaOrHole);
 			if (cmp != 0)
 				return cmp;
 			log.warn("Lines hit divider at same points and have same area sign", "this:", this, "other:", other);
@@ -644,6 +646,9 @@
 			trailAlong = leadAlong;
 			trailRel = leadRel;
 		} // for leadCoord
+		if (log.isDebugEnabled()) {
+			log.debug("initSplit #points", coords.size(), "on", dividingLine, isLongitude, "Area", runningArea, "#newLess", newLess.size(), "#newMore", newMore.size());
+		}
 		processLineList(newLess, lessList, runningArea);
 		processLineList(newMore, moreList, runningArea);
 	} // splitShape
