v6 - don't trash first ref if it is the same as the name (sans shield)
and more refs follow

---------

v5 - now understands the 0x1b prefix code that introduces a lower case
letter (and also is used to prefix a couple of separators (0x1b and
0x1c).

I thought great, now I can prefix my road names with ^\ (aka 0x1c) and
they won't show up so readily when zoomed out. That worked as expected
but, unfortunately, it broke the address search stuff so the bottom
line is that you can't use the separators as a prefix (sob).

Also, for those of you wondering why the display names and other refs
are not showing up in the mapsource address search - it's because the
MDR building code only reads the first label for a road and ignores any
others. Shame that. I don't think there's a good reason why it couldn't
read the other labels, it's just doesn't do that at the moment. BTW -
the basic address search on the etrex and Nuvi still sees those
alternative road labels.

So, those people who are tracking this patch series, please test and if
it doesn't bite your arse, I will commit it soonish

--------

v4 - found the motorways (and a load of other roads too!)

--------

v3 - now works harder to clean up road names for use in MDR file - not
sure if this will have a beneficial effect but it could possibly fix
the issue recently reported by Felix.

Motorways are still not showing up.

-------

v2 - remove more duplicate labels that only differ in letter case -
remove leading spaces from labels even if they start with a Garmin code.

Still something wrong with motorway names because on the UK map, only
the M74 appears in the mapsource road names - all other motorways are
missing - very odd.

-------

This patch codes around the problems introduced by highway
shields with regard to the sorted roads:

1 - the sort order should now be much improved

2 - no duplicate symbols (shield version + non-shield version)

It also includes a fix to the label reading code so that labels with a
highway shield prefix are read in correctly when generating the MDR
file.

For me, in mapsource, road search for roads with highway shields now
works apart from motorways which don't seem to searchable - perhaps
that's deliberate on Garmin's part?


All feedback appreciated.

Mark
diff --git a/src/uk/me/parabola/imgfmt/app/Label.java b/src/uk/me/parabola/imgfmt/app/Label.java
index c7ca3a3..149205b 100644
--- a/src/uk/me/parabola/imgfmt/app/Label.java
+++ b/src/uk/me/parabola/imgfmt/app/Label.java
@@ -56,6 +56,19 @@ public class Label implements Comparable<Label> {
 		return text;
 	}
 
+	public String getTextSansGarminCodes() {
+		return stripGarminCodes(text);
+	}
+
+	public static String stripGarminCodes(String s) {
+		if(s == null)
+			return null;
+		s = s.replaceAll("[\u0001-\u0006\u001b-\u001c]", ""); // remove highway shields and "thin" separators
+		s = s.replaceAll("[\u001d-\u001f]", " "); // replace "fat" separators with spaces
+		// a leading separator would have turned into a space so trim it
+		return s.trim();
+	}
+
 	/**
 	 * The offset of this label in the LBL file.  The first byte of this file
 	 * is zero and an offset of zero means that the label has a zero length/is
diff --git a/src/uk/me/parabola/imgfmt/app/labelenc/BaseEncoder.java b/src/uk/me/parabola/imgfmt/app/labelenc/BaseEncoder.java
index 0dc8390..fd06368 100644
--- a/src/uk/me/parabola/imgfmt/app/labelenc/BaseEncoder.java
+++ b/src/uk/me/parabola/imgfmt/app/labelenc/BaseEncoder.java
@@ -65,7 +65,7 @@ public class BaseEncoder {
 		return new EncodedText(out, out.length);
 	}
 
-	protected boolean isUpperCase() {
+	public boolean isUpperCase() {
 		return upperCase;
 	}
 
diff --git a/src/uk/me/parabola/imgfmt/app/labelenc/Format6Decoder.java b/src/uk/me/parabola/imgfmt/app/labelenc/Format6Decoder.java
index 079ef4c..f4547cd 100644
--- a/src/uk/me/parabola/imgfmt/app/labelenc/Format6Decoder.java
+++ b/src/uk/me/parabola/imgfmt/app/labelenc/Format6Decoder.java
@@ -11,6 +11,7 @@ public class Format6Decoder implements CharacterDecoder {
 	private boolean needReset;
 
 	private boolean symbol;
+	private boolean lowerCaseOrSeparator;
 
 	private int store;
 	private int nbits;
@@ -77,20 +78,40 @@ public class Format6Decoder implements CharacterDecoder {
 		if (symbol) {
 			symbol = false;
 			c = Format6Encoder.SYMBOLS.charAt(b);
-		} else {
+		}
+		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 {
 			switch(b) {
 			case 0x1B:
-				// perhaps this is "next-char lower case"?
+				// next char is lower case or a separator
+				lowerCaseOrSeparator = true;
 				return;
+
 			case 0x1C:
 				// next char is symbol
 				symbol = true;
 				return;
+
 			case 0x1D:
 			case 0x1E:
 			case 0x1F:
-				// these define abbreviations; fall through to
-				// lookup which returns a space
+				// these are separators - use as is
+				c = (char)b;
+				break;
+
 			default:
 				c = Format6Encoder.LETTERS.charAt(b);
 				break;
diff --git a/src/uk/me/parabola/imgfmt/app/labelenc/Format6Encoder.java b/src/uk/me/parabola/imgfmt/app/labelenc/Format6Encoder.java
index ec226a1..da4068f 100644
--- a/src/uk/me/parabola/imgfmt/app/labelenc/Format6Encoder.java
+++ b/src/uk/me/parabola/imgfmt/app/labelenc/Format6Encoder.java
@@ -41,7 +41,7 @@ public class Format6Encoder extends BaseEncoder implements CharacterEncoder {
 	public static final String LETTERS =
 		" ABCDEFGHIJKLMNO" +	// 0x00-0x0F
 		"PQRSTUVWXYZxx   " +	// 0x10-0x1F
-		"0123456789xxxxxx";	// 0x20-0x2F
+		"0123456789\u0001\u0002\u0003\u0004\u0005\u0006";	// 0x20-0x2F
 
 	public static final String SYMBOLS =
 		"@!\"#$%&'()*+,-./" +	// 0x00-0x0F
@@ -69,11 +69,14 @@ public class Format6Encoder extends BaseEncoder implements CharacterEncoder {
 		for (char c : transliterator.transliterate(s).toCharArray()) {
 
 			if (c == ' ') {
-				buf = put6(buf, off++, 0);
+				put6(buf, off++, 0);
 			} else if (c >= 'A' && c <= 'Z') {
-				buf = put6(buf, off++, c - 'A' + 1);
+				put6(buf, off++, c - 'A' + 1);
 			} else if (c >= '0' && c <= '9') {
-				buf = put6(buf, off++, c - '0' + 0x20);
+				put6(buf, off++, c - '0' + 0x20);
+			} else if (c == 0x1b || c == 0x1c) {
+				put6(buf, off++, 0x1b);
+				put6(buf, off++, c + 0x10);
 			} else if (c >= 0x1d && c <= 0x1f) {
 				put6(buf, off++, c);
 			} else if (c >= 1 && c <= 6) {
diff --git a/src/uk/me/parabola/imgfmt/app/lbl/City.java b/src/uk/me/parabola/imgfmt/app/lbl/City.java
index 4fc7939..2ae6389 100644
--- a/src/uk/me/parabola/imgfmt/app/lbl/City.java
+++ b/src/uk/me/parabola/imgfmt/app/lbl/City.java
@@ -118,7 +118,7 @@ public class City implements Comparable<City> {
 		if(label != null)
 			result += label.getText();
 		if (subdivision != null)
-			result += subdivision.getNumber() + "/" + pointIndex;
+			result += " " + subdivision.getNumber() + "/" + pointIndex;
 		if(country != null)
 			result += " in country " + (0 + country.getIndex());
 		if(region != null)
diff --git a/src/uk/me/parabola/imgfmt/app/lbl/LBLFile.java b/src/uk/me/parabola/imgfmt/app/lbl/LBLFile.java
index 4d88bab..a2422ea 100644
--- a/src/uk/me/parabola/imgfmt/app/lbl/LBLFile.java
+++ b/src/uk/me/parabola/imgfmt/app/lbl/LBLFile.java
@@ -17,6 +17,7 @@
 package uk.me.parabola.imgfmt.app.lbl;
 
 import java.util.HashMap;
+import java.util.Locale;
 import java.util.Map;
 
 import uk.me.parabola.imgfmt.Utils;
@@ -27,6 +28,7 @@ import uk.me.parabola.imgfmt.app.Label;
 import uk.me.parabola.imgfmt.app.labelenc.BaseEncoder;
 import uk.me.parabola.imgfmt.app.labelenc.CharacterEncoder;
 import uk.me.parabola.imgfmt.app.labelenc.CodeFunctions;
+import uk.me.parabola.imgfmt.app.labelenc.Format6Encoder;
 import uk.me.parabola.imgfmt.app.trergn.Subdivision;
 import uk.me.parabola.imgfmt.fs.ImgChannel;
 import uk.me.parabola.log.Logger;
@@ -107,6 +109,13 @@ public class LBLFile extends ImgFile {
 	 * @return A reference to the created label.
 	 */
 	public Label newLabel(String text) {
+		// if required, fold case now so that labelCache doesn't
+		// contain multiple labels that only differ in letter case
+		if(text != null &&
+		   (textEncoder instanceof Format6Encoder ||
+			textEncoder instanceof BaseEncoder &&
+			((BaseEncoder)textEncoder).isUpperCase()))
+			text = text.toUpperCase(Locale.ENGLISH);
 		Label l = labelCache.get(text);
 		if (l == null) {
 			l = new Label(text);
diff --git a/src/uk/me/parabola/imgfmt/app/mdr/MDRFile.java b/src/uk/me/parabola/imgfmt/app/mdr/MDRFile.java
index bcb1867..7b2cff8 100644
--- a/src/uk/me/parabola/imgfmt/app/mdr/MDRFile.java
+++ b/src/uk/me/parabola/imgfmt/app/mdr/MDRFile.java
@@ -148,11 +148,7 @@ public class MDRFile extends ImgFile {
 	 * @return The name as it will go into the index.
 	 */
 	private String cleanUpName(String name) {
-		// TODO Currently just drop an initial shield, but more to do
-		if (name.charAt(0) < ' ')
-			return name.substring(1);
-		
-		return name;
+		return Label.stripGarminCodes(name);
 	}
 
 	public void write() {
diff --git a/src/uk/me/parabola/imgfmt/app/net/NETFile.java b/src/uk/me/parabola/imgfmt/app/net/NETFile.java
index 82f220b..e7abf26 100644
--- a/src/uk/me/parabola/imgfmt/app/net/NETFile.java
+++ b/src/uk/me/parabola/imgfmt/app/net/NETFile.java
@@ -18,8 +18,11 @@ package uk.me.parabola.imgfmt.app.net;
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
 
 import uk.me.parabola.imgfmt.Utils;
 import uk.me.parabola.imgfmt.app.BufferedImgFileWriter;
@@ -61,23 +64,44 @@ public class NETFile extends ImgFile {
 
 	public void writePost(ImgFileWriter rgn, boolean sortRoads) {
 		List<Sortable<Label, RoadDef>> sortedRoads = new ArrayList<Sortable<Label, RoadDef>>(roads.size());
+		// cleanedLabels holds "cleaned up" versions of the Label
+		// strings that are used when sorting the road names - the
+		// hope is that retrieving the String from the Map is faster than
+		// cleaning the Label text for each comparison in the sort
+		final Map<Label, String> cleanedLabels = new HashMap<Label, String>();
 
 		for (RoadDef rd : roads) {
 			rd.writeRgnOffsets(rgn);
 			if(sortRoads) {
 				Label[] l = rd.getLabels();
-				for(int i = 0; i < l.length && l[i] != null; ++i)
-					if(l[i].getLength() != 0)
+				for(int i = 0; i < l.length && l[i] != null; ++i) {
+					if(l[i].getLength() != 0) {
+						cleanedLabels.put(l[i], l[i].getTextSansGarminCodes());
+						//	System.err.println("Road " + rd + " has label " + l[i]);
 						sortedRoads.add(new Sortable<Label, RoadDef>(l[i], rd));
+					}
+				}
 			}
 		}
 
 		if(sortedRoads.size() > 0) {
-			Collections.sort(sortedRoads);
+			Collections.sort(sortedRoads, new Comparator<Sortable<Label, RoadDef>>() {
+					public int compare(Sortable<Label, RoadDef> a, Sortable<Label, RoadDef> b) {
+						// sort using cleaned versions of the labels
+						int diff = cleanedLabels.get(a.getKey()).compareToIgnoreCase(cleanedLabels.get(b.getKey()));
+						if(diff != 0)
+							return diff;
+						// the labels were the same, sort on the
+						// RoadDefs
+						return a.getValue().compareTo(b.getValue());
+					}
+				});
 			sortedRoads = simplifySortedRoads(new LinkedList<Sortable<Label, RoadDef>>(sortedRoads));
 			ImgFileWriter writer = netHeader.makeSortedRoadWriter(getWriter());
-			for(Sortable<Label, RoadDef> srd : sortedRoads)
+			for(Sortable<Label, RoadDef> srd : sortedRoads) {
+				//System.err.println("Road " + srd.getKey() + " is " + srd.getValue() + " " + srd.getValue().getCity());
 				srd.getValue().putSortedRoadEntry(writer, srd.getKey());
+			}
 			Utils.closeFile(writer);
 		}
 
@@ -95,7 +119,7 @@ public class NETFile extends ImgFile {
 	private List<Sortable<Label, RoadDef>> simplifySortedRoads(LinkedList<Sortable<Label, RoadDef>> in) {
 		List<Sortable<Label, RoadDef>> out = new ArrayList<Sortable<Label, RoadDef>>(in.size());
 		while(in.size() > 0) {
-			Label name0 = in.get(0).getKey();
+			String name0 = in.get(0).getKey().getTextSansGarminCodes();
 			RoadDef road0 = in.get(0).getValue();
 			City city0 = road0.getCity();
 			// transfer the 0'th entry to the output
@@ -104,7 +128,7 @@ public class NETFile extends ImgFile {
 			// firstly determine the entries whose name and city match
 			// name0 and city0
 			for(n = 0; (n < in.size() &&
-						name0 == in.get(n).getKey() &&
+						name0.equalsIgnoreCase(in.get(n).getKey().getTextSansGarminCodes()) &&
 						city0 == in.get(n).getValue().getCity()); ++n) {
 				// relax
 			}
diff --git a/src/uk/me/parabola/imgfmt/app/trergn/RGNFileReader.java b/src/uk/me/parabola/imgfmt/app/trergn/RGNFileReader.java
index 4711d2a..0ee3c6d 100644
--- a/src/uk/me/parabola/imgfmt/app/trergn/RGNFileReader.java
+++ b/src/uk/me/parabola/imgfmt/app/trergn/RGNFileReader.java
@@ -145,7 +145,7 @@ public class RGNFileReader extends ImgReader {
 		while (position() < end) {
 			Polyline line = new Polyline(div);
 			byte type = reader.get();
-			line.setType(type & 0x7f);
+			line.setType(type & 0x3f);
 
 			int labelOffset = reader.getu3();
 			Label label;
diff --git a/src/uk/me/parabola/imgfmt/app/trergn/Subdivision.java b/src/uk/me/parabola/imgfmt/app/trergn/Subdivision.java
index 1202186..b2547ea 100644
--- a/src/uk/me/parabola/imgfmt/app/trergn/Subdivision.java
+++ b/src/uk/me/parabola/imgfmt/app/trergn/Subdivision.java
@@ -274,19 +274,44 @@ public class Subdivision {
 	}
 
 	public Polyline createLine(String name, String ref) {
+		// don't be tempted to "trim()" the name as it zaps the highway shields
 		Label label = lblFile.newLabel(name);
+		String nameSansGC = Label.stripGarminCodes(name);
 		Polyline pl = new Polyline(this);
 
 		pl.setLabel(label);
+
 		if(ref != null) {
 			// ref may contain multiple ids separated by ";"
-			for(String r : ref.split(";")) {
-				String tr = r.trim();
-				if(tr.length() > 0) {
-					//System.err.println("Adding ref " + tr + " to road " + name);
+			String[] refs = ref.split(";");
+			if(refs.length == 1) {
+				// don't bother to add a single ref that looks the
+				// same as the name (sans shields) because it doesn't
+				// change the routing directions
+				String tr = refs[0].trim();
+				String trSansGC = Label.stripGarminCodes(tr);
+				if(trSansGC.length() > 0 &&
+				   !trSansGC.equalsIgnoreCase(nameSansGC)) {
 					pl.addRefLabel(lblFile.newLabel(tr));
 				}
 			}
+			else {
+				// multiple refs, always add the first so that it will
+				// be used in routing instructions if the name has a
+				// shield prefix
+				pl.addRefLabel(lblFile.newLabel(refs[0].trim()));
+
+				// only add the remaining refs if they differ from the
+				// name (sans shields)
+				for(int i = 1; i < refs.length; ++i) {
+					String tr = refs[i].trim();
+					String trSansGC = Label.stripGarminCodes(tr);
+					if(trSansGC.length() > 0 &&
+					   !trSansGC.equalsIgnoreCase(nameSansGC)) {
+						pl.addRefLabel(lblFile.newLabel(tr));
+					}
+				}
+			}
 		}
 
 		return pl;
diff --git a/src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java b/src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java
index 58500ef..de58e11 100644
--- a/src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java
+++ b/src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java
@@ -599,6 +599,24 @@ public class StyledConverter implements OsmConverter {
 			// use first ref as name
 			name = SEMI_PATTERN.split(refs)[0].trim();
 		}
+		else if(name != null) {
+			// remove leading spaces (don't use trim() to avoid zapping
+			// shield codes)
+			char leadingCode = 0;
+			if(name.length() > 1 &&
+			   name.charAt(0) < 0x20 &&
+			   name.charAt(1) == ' ') {
+				leadingCode = name.charAt(0);
+				name = name.substring(2);
+			}
+				
+			while(name.length() > 0 && name.charAt(0) == ' ')
+				name = name.substring(1);
+
+			if(leadingCode != 0)
+				name = new Character(leadingCode) + name;
+		}
+
 		if(name != null)
 			ms.setName(name);
 		if(refs != null)
_______________________________________________
mkgmap-dev mailing list
mkgmap-dev@lists.mkgmap.org.uk
http://www.mkgmap.org.uk/mailman/listinfo/mkgmap-dev

Reply via email to