Index: build.xml
===================================================================
--- build.xml	(revision 4819)
+++ build.xml	(working copy)
@@ -234,6 +234,7 @@
 		<javac srcdir="${src}" destdir="${build.classes}" encoding="utf-8" debug="true" includeantruntime="false">
 			<include name="**/*.java" />
 			<classpath refid="main"/>
+			<compilerarg value="-Xdiags:verbose"/>
 			<compilerarg value="-Xlint:all"/>
 			<compilerarg value="-Xlint:-serial"/>
 			<compilerarg value="-Xlint:-path"/>
Index: doc/options.txt
===================================================================
--- doc/options.txt	(revision 4819)
+++ doc/options.txt	(working copy)
@@ -145,11 +145,10 @@
 === Label options ===
 
 ;--code-page=number
-:     Specify which international character set is to be used. Only 8 bit
-character sets are supported so you have to specify which code page you
-want to use.
-It is entirely dependent on the device firmware which code pages are
-supported.
+:     Specify which character set is to be used.
+The default is --code-page=0 which uses a 6-bit encoding of ASCII characters.
+Mkgmap supports many 8-bit code-pages and Unicode. Other, multi-byte, code-pages might work.
+It is entirely dependent on the device firmware which code pages they support.
 
 ;--latin1
 : 	This is equivalent to --code-page=1252.
@@ -159,8 +158,11 @@
 Unicode maps produced by mkgmap.
 
 ;--lower-case
-: 	Allow labels to contain lower case letters.  Note that many
-Garmin devices are not able to display lower case letters at an angle.
+: 	Allow labels to contain lower case letters.
+Note that some older Garmin devices are not able to display lower case letters at an angle.
+Also note that newer devices InitCap text, so, even without this option, text appears as mixed case.
+Also note that, using the default code-page=0, lower-case letters require 12 bits and the resultant
+map could be larger than if --latin1 had been used.
 
 === Address search options ===
 
Index: resources/help/en/logging
===================================================================
--- resources/help/en/logging	(revision 4819)
+++ resources/help/en/logging	(working copy)
@@ -25,6 +25,8 @@
 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
+# Default logger
+global.level=INFO
 # For ConsoleHandler
 java.util.logging.ConsoleHandler.level=WARNING
 java.util.logging.ConsoleHandler.formatter=uk.me.parabola.log.UsefulFormatter
Index: resources/help/en/options
===================================================================
--- resources/help/en/options	(revision 4819)
+++ resources/help/en/options	(working copy)
@@ -145,10 +145,10 @@
 === Label options ===
 
 --code-page=number
-    Specify which international character set is to be used. Only 8 bit
-    character sets are supported so you have to specify which code page you
-    want to use. It is entirely dependent on the device firmware which code
-    pages are supported.
+    Specify which character set is to be used. The default is --code-page=0
+    which uses a 6-bit encoding of ASCII characters. Mkgmap supports many 8-bit
+    code-pages and Unicode. Other, multi-byte, code-pages might work. It is
+    entirely dependent on the device firmware which code pages they support.
 
 --latin1
     This is equivalent to --code-page=1252.
@@ -158,8 +158,12 @@
     support Unicode maps produced by mkgmap.
 
 --lower-case
-    Allow labels to contain lower case letters. Note that many Garmin devices
-    are not able to display lower case letters at an angle.
+    Allow labels to contain lower case letters. Note that some older Garmin
+    devices are not able to display lower case letters at an angle. Also note
+    that newer devices InitCap text, so, even without this option, text appears
+    as mixed case. Also note that, using the default code-page=0, lower-case
+    letters require 12 bits and the resultant map could be larger than if
+    --latin1 had been used.
 
 === Address search options ===
 
Index: scripts/download/mkdoc
===================================================================
--- scripts/download/mkdoc	(revision 4819)
+++ 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/labelenc/BaseEncoder.java
===================================================================
--- src/uk/me/parabola/imgfmt/app/labelenc/BaseEncoder.java	(revision 4819)
+++ src/uk/me/parabola/imgfmt/app/labelenc/BaseEncoder.java	(working copy)
@@ -28,7 +28,7 @@
  * @author Steve Ratcliffe
  */
 public class BaseEncoder {
-	private static final Logger log = Logger.getLogger(BaseEncoder.class);
+	protected static final Logger log = Logger.getLogger(BaseEncoder.class);
 
 	public static final EncodedText NO_TEXT = new EncodedText(null, 0, null);
 
Index: src/uk/me/parabola/imgfmt/app/labelenc/Format6Decoder.java
===================================================================
--- src/uk/me/parabola/imgfmt/app/labelenc/Format6Decoder.java	(revision 4819)
+++ src/uk/me/parabola/imgfmt/app/labelenc/Format6Decoder.java	(working copy)
@@ -85,22 +85,10 @@
 		if (symbol) {
 			symbol = false;
 			c = Format6Encoder.SYMBOLS.charAt(b);
-		}
-		else if(lowerCaseOrSeparator) {
+		} else if (lowerCaseOrSeparator) {
 			lowerCaseOrSeparator = false;
-			if(b == 0x2b || b == 0x2c) {
-				c = (char)(b - 0x10); // "thin" separator
-			}
-			else if(Character.isLetter(b)) {
-				// lower case letter
-				c = Character.toLowerCase(Format6Encoder.LETTERS.charAt(b));
-			}
-			else {
-				// not a letter so just use as is (could be a digit)
-				c = Format6Encoder.LETTERS.charAt(b);
-			}
-		}
-		else {
+			c = Format6Encoder.LOWERCASE.charAt(b);
+		} else {
 			switch(b) {
 			case 0x1B:
 				// next char is lower case or a separator
@@ -112,13 +100,6 @@
 				symbol = true;
 				return;
 
-			case 0x1D:
-			case 0x1E:
-			case 0x1F:
-				// these are separators - use as is
-				c = (char)b;
-				break;
-
 			default:
 				c = Format6Encoder.LETTERS.charAt(b);
 				break;
Index: src/uk/me/parabola/imgfmt/app/labelenc/Format6Encoder.java
===================================================================
--- src/uk/me/parabola/imgfmt/app/labelenc/Format6Encoder.java	(revision 4819)
+++ src/uk/me/parabola/imgfmt/app/labelenc/Format6Encoder.java	(working copy)
@@ -17,12 +17,14 @@
 package uk.me.parabola.imgfmt.app.labelenc;
 
 import java.text.Normalizer;
-import java.util.Locale;
 
 /**
- * Format according to the '6 bit' .img format.  The text is first upper
- * cased.  Any letter with a diacritic or accent is replaced with its base
- * letter.
+ * Format according to the '6 bit' .img format.
+ * Any letter with a diacritic or accent is replaced with its base letter.
+ * Characters from other alphabets are transliterated if resources/chars/ascii/ data exists.
+ * If set/forceUpperCase (mkgmap default), the text is upper-cased by the transliterator.
+ * NB lower-case is supported but each lower-case letter needs 12 bits, so, with typical OSM data,
+ * almost any other code-page will be more compact
  *
  * For example Körnerstraße would become KORNERSTRASSE,
  * Řípovská would become RIPOVSKA etc.
@@ -35,22 +37,32 @@
  */
 public class Format6Encoder extends BaseEncoder implements CharacterEncoder {
 
-	// This is 0x1b is the source document, but the accompanying code uses
-	// the value 0x1c, which seems to work.
-	private static final int SYMBOL_SHIFT = 0x1c;
+	// Following are swapped in the above John Mechalas document, but this is what works:
+	private static final int LOWERCASE_SHIFT = 0x1b;
+	private static final int SYMBOL_SHIFT    = 0x1c;
 
 	public static final String LETTERS =
 		" ABCDEFGHIJKLMNO" +	// 0x00-0x0F
-		"PQRSTUVWXYZxx   " +	// 0x10-0x1F
-		"0123456789\u0001\u0002\u0003\u0004\u0005\u0006";	// 0x20-0x2F
+		"PQRSTUVWXYZxx\u001d\u001e\u001f" +	// 0x10-0x1F  xx are above SHIFTs. prefix/suffix indicators
+		"0123456789\u0001\u0002\u0003\u0004\u0005\u0006";	// 0x20-0x2F  digits + shields
 
 	public static final String SYMBOLS =
 		"@!\"#$%&'()*+,-./" +	// 0x00-0x0F
-		"xxxxxxxxxx:;<=>?" +	// 0x10-0x1F
-		"xxxxxxxxxxx[\\]^_";	// 0x20-0x2F
+		"          :;<=>?" +	// 0x10-0x1F
+		"°          [\\]^_";	// 0x20-0x2F
+	//   ^ looks like degree (\u00b0) on MapSource/eTrex. Won't happen as transliterated to "deg"
+	//   0123456789abcdef
+	public static final String LOWERCASE =
+		"`abcdefghijklmno" +	// 0x00-0x0F  back-tick
+		"pqrstuvwxyz{|}~ " +	// 0x10-0x1F
+		"           \u001b\u001c   ";     // 0x20-0x2F  more prefix/suffix indicators
 
-	private final Transliterator transliterator = new TableTransliterator("ascii");
+	private final Transliterator transliterator;
 
+	public Format6Encoder() {
+		transliterator = new TableTransliterator("ascii");
+	}
+
 	/**
 	 * Encode the text into the 6 bit format.  See the class level notes.
 	 *
@@ -62,7 +74,7 @@
 		if (text == null || text.isEmpty())
 			return NO_TEXT;
 		String normalisedText = Normalizer.normalize(text, Normalizer.Form.NFC);
-		String s = transliterator.transliterate(normalisedText).toUpperCase(Locale.ENGLISH);
+		String s = transliterator.transliterate(normalisedText);  // it does the upper if forceUpper
 
 		// Allocate more than enough space on average for the label.
 		// if you overdo it then it will waste a lot of space , but
@@ -78,8 +90,8 @@
 				put6(buf, off++, c - 'A' + 1);
 			} else if (c >= '0' && c <= '9') {
 				put6(buf, off++, c - '0' + 0x20);
-			} else if (c == 0x1b || c == 0x1c) {
-				put6(buf, off++, 0x1b);
+			} else if (c == 0x1b || c == 0x1c) {  // shiftedLowerCase() does same thing
+				put6(buf, off++, LOWERCASE_SHIFT);
 				put6(buf, off++, c + 0x10);
 			} else if (c >= 0x1d && c <= 0x1f) {
 				put6(buf, off++, c);
@@ -86,8 +98,14 @@
 			} else if (c >= 1 && c <= 6) {
 				// Highway shields
 				put6(buf, off++, 0x29 + c);
+			} else if (c >= 'a' && c <= 'z') {
+				put6(buf, off++, LOWERCASE_SHIFT);
+				put6(buf, off++, c - 'a' + 1);
 			} else {
+				int rememberOff = off;
 				off = shiftedSymbol(buf, off, c);
+				if (off == rememberOff)
+					off = shiftedLowerCase(buf, off, c);
 			}
 		}
 
@@ -119,6 +137,16 @@
 		return off;
 	}
 
+	private int shiftedLowerCase(byte[] buf, int startOffset, char c) {
+		int off = startOffset;
+		int ind = LOWERCASE.indexOf(c);
+		if (ind >= 0) {
+			put6(buf, off++, LOWERCASE_SHIFT);
+			put6(buf, off++, ind);
+		}
+		return off;
+	}
+
 	/**
 	 * Each character is packed into 6 bits.  This keeps track of everything so
 	 * that the character can be put into the right place in the byte array.
@@ -149,4 +177,10 @@
 
 		return buf;
 	}
+
+	@Override
+	public void setUpperCase(boolean upperCase) {
+		super.setUpperCase(upperCase);
+		transliterator.forceUppercase(upperCase);
+	}
 }
Index: src/uk/me/parabola/imgfmt/app/mdr/MDRFile.java
===================================================================
--- src/uk/me/parabola/imgfmt/app/mdr/MDRFile.java	(revision 4819)
+++ src/uk/me/parabola/imgfmt/app/mdr/MDRFile.java	(working copy)
@@ -67,6 +67,24 @@
 	private final Mdr28 mdr28;
 	private final Mdr29 mdr29;
 
+	/* %%% for investigation, add some others
+%%% need to make a generic...
+	private final Mdr2 mdr2;
+	private final Mdr3 mdr3;
+	private final Mdr16 mdr16;
+	private final Mdr30 mdr30;
+	private final Mdr31 mdr31;
+	private final Mdr32 mdr32;
+	private final Mdr33 mdr33;
+	private final Mdr34 mdr34;
+	private final Mdr35 mdr35;
+	private final Mdr36 mdr36;
+	private final Mdr37 mdr37;
+	private final Mdr38 mdr38;
+	private final Mdr39 mdr39;
+	private final Mdr40 mdr40;
+	*/
+
 	private int currentMap;
 
 	private final boolean forDevice;
Index: src/uk/me/parabola/imgfmt/app/mdr/Mdr25.java
===================================================================
--- src/uk/me/parabola/imgfmt/app/mdr/Mdr25.java	(revision 4819)
+++ src/uk/me/parabola/imgfmt/app/mdr/Mdr25.java	(working copy)
@@ -12,6 +12,7 @@
  */
 package uk.me.parabola.imgfmt.app.mdr;
 
+import java.text.Collator;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -37,6 +38,8 @@
 	 */
 	public void sortCities(List<Mdr5Record> list) {
 		Sort sort = getConfig().getSort();
+		Collator collator = sort.getCollator();
+		collator.setStrength(Collator.TERTIARY);
 
 		List<SortKey<Mdr5Record>> keys = new ArrayList<>();
 		for (Mdr5Record c : list) {
@@ -52,9 +55,10 @@
 		for (SortKey<Mdr5Record> key : keys) {
 			Mdr5Record city = key.getObject();
 
-			if (lastCity == null || !city.getName().equals(lastCity.getName())
-					|| !city.getRegionName().equals(lastCity.getRegionName())
-					|| !city.getCountryName().equals(lastCity.getCountryName())) {
+			if (lastCity == null ||
+				collator.compare(city.getName(), lastCity.getName()) != 0 ||
+				collator.compare(city.getRegionName(), lastCity.getRegionName()) != 0 ||
+				collator.compare(city.getCountryName(), lastCity.getCountryName()) != 0) {
 				record++;
 
 				// Record in the 29 index if there is one for this record
@@ -61,8 +65,8 @@
 				Mdr14Record mdrCountry = city.getMdrCountry();
 				Mdr29Record mdr29 = mdrCountry.getMdr29();
 				String name = mdr29.getName();
-				assert mdrCountry.getName().equals(name);
-				if (!name.equals(lastName)) {
+				assert collator.compare(mdrCountry.getName(), name) == 0;
+				if (lastName == null || collator.compare(name, lastName) != 0) {
 					mdr29.setMdr25(record);
 					lastName = name;
 				}
Index: src/uk/me/parabola/imgfmt/app/mdr/Mdr29.java
===================================================================
--- src/uk/me/parabola/imgfmt/app/mdr/Mdr29.java	(revision 4819)
+++ src/uk/me/parabola/imgfmt/app/mdr/Mdr29.java	(working copy)
@@ -83,6 +83,8 @@
 		PointerSizes sizes = getSizes();
 		int size24 = sizes.getSize(24);
 		int size22 = sizes.getSize(22);
+		assert sizes.getNumberOfItems(5) >= sizes.getNumberOfItems(25)
+			: "5:" + sizes.getNumberOfItems(5) + " 25:" + sizes.getNumberOfItems(25);
 		int size25 = sizes.getSize(5);  // NB appears to be size of 5 (cities), not 25 (cities with country).
 		int size26 = has26? sizes.getSize(26): 0;
 		int size17 = Utils.numberToPointerSize(max17);
Index: src/uk/me/parabola/imgfmt/app/mdr/Mdr5.java
===================================================================
--- src/uk/me/parabola/imgfmt/app/mdr/Mdr5.java	(revision 4819)
+++ src/uk/me/parabola/imgfmt/app/mdr/Mdr5.java	(working copy)
@@ -39,6 +39,8 @@
 	private int maxCityIndex;
 	private int localCitySize;
 	private int mdr20PointerSize = 0; // bytes for mdr20 pointer, or 0 if no mdr20
+	private Sort sort;
+	private Collator collator;
 
 	// We need a common area to save the mdr20 values, since there can be multiple
 	// city records with the same global city index
@@ -46,6 +48,9 @@
 
 	public Mdr5(MdrConfig config) {
 		setConfig(config);
+		sort = config.getSort();
+		collator = sort.getCollator();
+		collator.setStrength(Collator.TERTIARY);
 	}
 
 	public void addCity(Mdr5Record record) {
@@ -62,15 +67,14 @@
 	public void preWriteImpl() {
 		allCities.trimToSize();
 		localCitySize = Utils.numberToPointerSize(maxCityIndex + 1);
-		Sort sort = getConfig().getSort();
-		genCitiesAndMdr20s(sort);
+		genCitiesAndMdr20s();
 		// calculate positions for the different road indexes
-		calcMdr20SortPos(sort);
-		calcMdr21SortPos(sort);
-		calcMdr22SortPos(sort);
+		calcMdr20SortPos();  // TODO: Maybe this can be simplified away - see mdr5-simplify-v2.patch 02-Nov-2021
+		calcMdr21SortPos();
+		calcMdr22SortPos();
 	}
 
-	private void genCitiesAndMdr20s(Sort sort) {
+	private void genCitiesAndMdr20s() {
 		List<SortKey<Mdr5Record>> sortKeys = new ArrayList<>(allCities.size());
 		Map<String, byte[]> regionCache = new HashMap<>();
 		Map<String, byte[]> countryCache = new HashMap<>();
@@ -90,7 +94,6 @@
 		sortKeys.sort(null);
 
 		cities = new ArrayList<>(sortKeys.size());
-		Collator collator = sort.getCollator();
 
 		int count = 0;
 		Mdr5Record lastCity = null;
@@ -122,7 +125,7 @@
 	/**
 	 * Calculate a position when sorting by name, region, and country- This is used for MDR20. 
 	 */
-	private void calcMdr20SortPos(Sort sort) {
+	private void calcMdr20SortPos() {
 		List<SortKey<Mdr5Record>> sortKeys = new ArrayList<>(allCities.size());
 		Map<String, byte[]> regionCache = new HashMap<>();
 		Map<String, byte[]> countryCache = new HashMap<>();
@@ -155,7 +158,7 @@
 	/**
 	 * Calculate a position when sorting by region- This is used for MDR21. 
 	 */
-	private void calcMdr21SortPos(Sort sort) {
+	private void calcMdr21SortPos() {
 		List<SortKey<Mdr5Record>> sortKeys = new ArrayList<>(allCities.size());
 		for (Mdr5Record m : allCities) {
 			if (m.getRegionName() == null) 
@@ -181,7 +184,7 @@
 	 * Calculate a position when sorting by country- This is used for MDR22. 
 	 */
 
-	private void calcMdr22SortPos(Sort sort) {
+	private void calcMdr22SortPos() {
 		List<SortKey<Mdr5Record>> sortKeys = new ArrayList<>(allCities.size());
 		for (Mdr5Record m : allCities) {
 			if (m.getCountryName() == null)
@@ -208,7 +211,6 @@
 		Mdr5Record lastCity = null;
 		boolean hasString = hasFlag(0x8);
 		boolean hasRegion = hasFlag(0x4);
-		Collator collator = getConfig().getSort().getCollator();
 		for (Mdr5Record city : cities) {
 			int gci = city.getGlobalCityIndex();
 			addIndexPointer(city.getMapIndex(), gci);
@@ -284,7 +286,7 @@
 		int val = (localCitySize - 1);
 		// String offset is only included for a mapsource index.
 		if (isForDevice() ) {
-			if (!getConfig().getSort().isMulti())
+			if (!sort.isMulti())
 				val |= 0x40; // mdr17 sub section present (not with unicode)
 		} else {
 			val |= 0x04;  // region
Index: src/uk/me/parabola/imgfmt/app/mdr/Mdr7.java
===================================================================
--- src/uk/me/parabola/imgfmt/app/mdr/Mdr7.java	(revision 4819)
+++ src/uk/me/parabola/imgfmt/app/mdr/Mdr7.java	(working copy)
@@ -52,6 +52,7 @@
 	private int partialInfoSize;
 	private Set<String> exclNames;
 	private final Sort sort;
+	private Collator collator;
 	private int maxPrefixCount;
 	private int maxSuffixCount;
 	private boolean magicIsValid;
@@ -60,6 +61,8 @@
 	public Mdr7(MdrConfig config) {
 		setConfig(config);
 		sort = config.getSort();
+		collator = sort.getCollator();
+		collator.setStrength(Collator.SECONDARY); 
 		splitName = config.isSplitName();
 		exclNames = config.getMdr7Excl();
 		codepage = sort.getCodepage();
@@ -217,8 +220,6 @@
 		// 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();
@@ -264,6 +265,12 @@
 		// per map for the same name.
 		for (int i = 0; i < samePartial.size(); i++) {
 			Mdr7Record r = samePartial.get(i);
+			//if (last != null && r.getMapIndex() == last.getMapIndex() && collator.compare(r.getName(), last.getName()) == 0) {
+			// 28Oct2021 Ticker:
+			// Maybe this should use above "collator.compare(x,y)==0" rather than following "x.equals(y)", but this causes
+			// significant differences to various MDR sections when there is the same street name but with different shields
+			// The eTrex Legend HXc supresses these duplicate entries in the list of streets but gives a result for each.
+			// cf. Mdr20 "Only save a single copy of each street name."
 			if (last != null && r.getMapIndex() == last.getMapIndex() && r.getName().equals(last.getName())) {
 				// This has the same name (and map number) as the previous one.
 				// Save the pointer to that one
@@ -283,8 +290,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 4819)
+++ 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/mkgmap/build/MapBuilder.java
===================================================================
--- src/uk/me/parabola/mkgmap/build/MapBuilder.java	(revision 4819)
+++ src/uk/me/parabola/mkgmap/build/MapBuilder.java	(working copy)
@@ -1239,6 +1239,7 @@
 
 		LayerFilterChain normalFilters = new LayerFilterChain(config);
 		LayerFilterChain keepParallelFilters = new LayerFilterChain(config);
+		DouglasPeuckerFilter dp = null;
 		if (enableLineCleanFilters && (res < 24)) {
 			MapFilter rounder = new RoundCoordsFilter();
 			MapFilter sizeFilter = new SizeFilter(MIN_SIZE_LINE);
@@ -1246,17 +1247,20 @@
 			normalFilters.addFilter(sizeFilter);
 			double errorForRes = dpFilterLineResMap.ceilingEntry(res).getValue();
 			if(errorForRes > 0) {
-				DouglasPeuckerFilter dp = new DouglasPeuckerFilter(errorForRes);
-				normalFilters.addFilter(dp);
+				dp = new DouglasPeuckerFilter(errorForRes);
 				keepParallelFilters.addFilter(dp);
 			}
 			keepParallelFilters.addFilter(rounder);
 			keepParallelFilters.addFilter(sizeFilter);
 		}
+		RemoveObsoletePointsFilter removeObsolete = new RemoveObsoletePointsFilter();
+		normalFilters.addFilter(removeObsolete);
+		keepParallelFilters.addFilter(removeObsolete);
+		if (dp != null)
+			normalFilters.addFilter(dp);
 		for (MapFilter filter : Arrays.asList(
+				new RemoveEmpty(),
 				new LineSplitterFilter(), 
-				new RemoveEmpty(),
-				new RemoveObsoletePointsFilter(), 
 				new LinePreparerFilter(div), 
 				new LineAddFilter(div, map))) {
 			normalFilters.addFilter(filter);
@@ -1304,6 +1308,9 @@
 		filters.addFilter(new PolygonSplitterFilter());
 		if (enableLineCleanFilters && (res < 24)) {
 			filters.addFilter(new RoundCoordsFilter());
+		}
+		filters.addFilter(new RemoveObsoletePointsFilter());
+		if (enableLineCleanFilters && (res < 24)) {
 			int sizefilterVal =  getMinSizePolygonForResolution(res);
 			if (sizefilterVal > 0)
 				filters.addFilter(new SizeFilter(sizefilterVal));
@@ -1313,7 +1320,6 @@
 			if(errorForRes > 0)
 				filters.addFilter(new DouglasPeuckerFilter(errorForRes));
 		}
-		filters.addFilter(new RemoveObsoletePointsFilter());
 		filters.addFilter(new RemoveEmpty());
 		filters.addFilter(new LinePreparerFilter(div));
 		filters.addFilter(new ShapeAddFilter(div, map));
Index: src/uk/me/parabola/mkgmap/filters/ShapeMergeFilter.java
===================================================================
--- src/uk/me/parabola/mkgmap/filters/ShapeMergeFilter.java	(revision 4819)
+++ src/uk/me/parabola/mkgmap/filters/ShapeMergeFilter.java	(working copy)
@@ -293,7 +293,7 @@
 					.collect(Collectors.toList());
 			if (sh0.getPoints().get(0).getHighwayCount() > 1)
 				sharedPoints.remove(0);
-			if (sharedPoints.isEmpty() || (sh0.getPoints().size() - sharedPoints.size() > maxPoints)) {
+			if (sharedPoints.isEmpty() /*%%% || (sh0.getPoints().size() - sharedPoints.size() > maxPoints)*/) {
 				// merge will not work
 				continue;
 			}
@@ -391,7 +391,7 @@
 			}
 		}
 		List<Coord> merged = null;
-		if (points1.size() + points2.size() - 2 * sh1PositionsToCheck.size() < maxPoints) {
+		if (true) { // %%% points1.size() + points2.size() - 2 * sh1PositionsToCheck.size() < maxPoints) {
 			merged = findBestMerge(points1, points2, sh1PositionsToCheck, sh2PositionsToCheck, sameDir);
 			if (merged == null)
 				return sh1;
@@ -400,9 +400,12 @@
 			if (merged.get(0) != merged.get(merged.size() - 1))
 				merged = null;
 			else if (merged.size() > maxPoints) {
-				// don't merge because merged polygon would probably be split again
-				log.info("merge rejected: merged shape has too many points " + merged.size());
-				merged = null;
+				int predictedPoints = PredictFilterPoints.predictedMaxNumPoints(merged, resolution, false);
+				if (predictedPoints > maxPoints) {
+					// don't merge because merged polygon would probably be split again
+					log.info("merge rejected: merged shape has too many points", merged.size(), predictedPoints);
+					merged = null;
+				}
 			}
 		}
 		if (merged == null)
Index: src/uk/me/parabola/mkgmap/main/Main.java
===================================================================
--- src/uk/me/parabola/mkgmap/main/Main.java	(revision 4819)
+++ src/uk/me/parabola/mkgmap/main/Main.java	(working copy)
@@ -582,6 +582,7 @@
 					}
 				} catch (Exception e) {
 					Logger.defaultLogger.error("Unexpected error", e);
+					e.printStackTrace();
 					throw new ExitException("Exiting due to unexpected error");
 				}
 			}
Index: src/uk/me/parabola/util/ShapeSplitter.java
===================================================================
--- src/uk/me/parabola/util/ShapeSplitter.java	(revision 4819)
+++ src/uk/me/parabola/util/ShapeSplitter.java	(working copy)
@@ -387,13 +387,16 @@
 				else if (areaDirection != tmpAreaDirection)
 					logMsg("Direction/Area conflict", fullAreaSign, areaDirection, tmpAreaDirection);
 			}
-			// create endPoint elements
-			LoopEndPoint lep1 = new LoopEndPoint(thisLine.lowPoint,  +1, thisLine);
-			LoopEndPoint lep2 = new LoopEndPoint(thisLine.highPoint, -1, thisLine);
-			lep1.otherEnd = lep2;
-			lep2.otherEnd = lep1;
-			endPoints.add(lep1);
-			endPoints.add(lep2);
+			if (Math.abs(thisLine.areaToLine) >= uk.me.parabola.mkgmap.filters.ShapeMergeFilter.SINGLE_POINT_AREA) {
+				// create endPoint elements
+				LoopEndPoint lep1 = new LoopEndPoint(thisLine.lowPoint,  +1, thisLine);
+				LoopEndPoint lep2 = new LoopEndPoint(thisLine.highPoint, -1, thisLine);
+				lep1.otherEnd = lep2;
+				lep2.otherEnd = lep1;
+				endPoints.add(lep1);
+				endPoints.add(lep2);
+			} else
+				log.debug("Drop very small shape", thisLine.areaToLine, thisLine);
 		}
 		if (someDirectionsNotSet) {
 			if (areaDirection == 0)
@@ -407,6 +410,10 @@
 		Comparator<LoopEndPoint> comparator = new EndPointComparator();
 		multSamePoint = false;
 		endPoints.sort(comparator);
+		/* %%% ???
+maybe scan anyway checking for alternating shape/hole and making thePoint same if close to prev
+then, if problems,  re-sort and processAwkward
+		*/
 		if (multSamePoint)
 			processAwkward(comparator);
 //		if (log.isDebugEnabled()) { // can be useful to have raw loop data before shape/hole processing
@@ -426,8 +433,7 @@
 		doLines(0, endPoints.size(), null);
 		if (detectedProblems && log.isDebugEnabled())
 			for (LoopEndPoint endPoint : endPoints)
-				log.warn("ep", endPoint.ownIndex, endPoint.thePoint, endPoint.lowEnd, System.identityHashCode(endPoint),
-						 System.identityHashCode(endPoint.otherEnd), endPoint.theLoop);
+				log.debug("ep", endPoint.ownIndex, endPoint.otherEnd.ownIndex, endPoint.thePoint, endPoint.lowEnd, endPoint.theLoop);
 	} // processLineList
 
 	private void processAwkward(Comparator<LoopEndPoint> comparator) {
@@ -820,6 +826,7 @@
 			logDiagInfo(coords, lessList, moreList);
 			log.warn(isLongitude ? "Vertical" : "Horizontal", "split", dividingLine, "failed on shape at", coords.get(0).toOSMURL(),
 					  "Possibly a self-intersecting polygon");
+			//assert false; // %%%
 		}
 	} // split
 
Index: test/uk/me/parabola/imgfmt/app/labelenc/CodeFunctionsTest.java
===================================================================
--- test/uk/me/parabola/imgfmt/app/labelenc/CodeFunctionsTest.java	(revision 4819)
+++ test/uk/me/parabola/imgfmt/app/labelenc/CodeFunctionsTest.java	(working copy)
@@ -28,6 +28,7 @@
 		assertEquals("code page", 0, functions.getCodepage());
 		assertEquals("encoding type", 6, functions.getEncodingType());
 		CharacterEncoder enc = functions.getEncoder();
+		((BaseEncoder) enc).setUpperCase(true);
 
 		EncodedText etext = enc.encodeText("hello world");
 		byte[] ctext = etext.getCtext();
@@ -58,8 +59,13 @@
 		CodeFunctions functions = CodeFunctions.createEncoderForLBL(6, 0);
 
 		CharacterEncoder encoder = functions.getEncoder();
+		((BaseEncoder) encoder).setUpperCase(true);
+
 		// Twülpstedt contains u + "COMBINING DIAERESIS" (0x75 + 0x308)
-		EncodedText text = encoder.encodeText("Körnerstraße, Twülpstedt, Velkomezeříčská, Skólavörðustigur");
+		String tstStr = "Körnerstraße, Twülpstedt, Velkomezeříčská, Skólavörðustigur";
+		//              "12345678901234567890123456789012345678901234567890123456789"
+		assertEquals("tstStr length", 60, tstStr.length()); // check COMBINING DIAERESIS not already combined
+		EncodedText text = encoder.encodeText(tstStr);
 
 		CharacterDecoder decoder = functions.getDecoder();
 		byte[] ctext = text.getCtext();
@@ -70,6 +76,7 @@
 		}
 		String result = decoder.getText().getText();
 		assertEquals("transliterated text", "KORNERSTRASSE, TWULPSTEDT, VELKOMEZERICSKA, SKOLAVORDUSTIGUR", result);
+		assertEquals("result length", 60, result.length()); // ß changed to ss, COMBINING DIAERESIS combined by normalisation
 	}
 
 	/**
@@ -81,8 +88,11 @@
 		CodeFunctions functions = CodeFunctions.createEncoderForLBL("latin1");
 
 		CharacterEncoder encoder = functions.getEncoder();
+		((BaseEncoder) encoder).setUpperCase(false);
 		// Twülpstedt contains u + "COMBINING DIAERESIS" (0x75 + 0x308)
-		EncodedText text = encoder.encodeText("Körnerstraße, Twülpstedt, Velkomezeříčská, Skólavörðustigur");
+		String tstStr = "Körnerstraße, Twülpstedt, Velkomezeříčská, Skólavörðustigur";
+		assertEquals("tstStr length", 60, tstStr.length()); // check COMBINING DIAERESIS not already combined
+		EncodedText text = encoder.encodeText(tstStr);
 
 		CharacterDecoder decoder = functions.getDecoder();
 		byte[] ctext = text.getCtext();
@@ -95,6 +105,7 @@
 		String result = decoder.getText().getText();
 		// Twülpstedt now contains LATIN SMALL LETTER U WITH DIAERESIS (u+00fc)
 		assertEquals("transliterated text", "Körnerstraße, Twülpstedt, Velkomezerícská, Skólavörðustigur", result);
+		assertEquals("result length", 59, result.length()); // COMBINING DIAERESIS combined by normalisation
 	}
 
 	/**
@@ -112,6 +123,7 @@
 		}
 
 		CharacterEncoder encoder = functions.getEncoder();
+		((BaseEncoder) encoder).setUpperCase(false);
 		EncodedText text = encoder.encodeText(sb.toString());
 
 		// This encoder appends a null byte.
