Yo Landlubbers,

Here's the first cut at encoding support for extended type attributes.

This is mostly concerned with marine entities like (wreck depths, buoy
types, light types, colours, etc.) As there is quite a lot of it and I
haven't sussed it all out yet, I thought it would make sense to post
patches as I go along so that people can try it out and find the
bugs sooner rather than later.

The attribute values are supplied as special tags with a mkgmap:xt-
prefix. So, for example, mkgmap:xt-depth=40.6m specifies the depth of
something (in metres).

This first tranche supports:

Point types 0x0102xx (buoys/beacons) can have:
  mkgmap:xt-foundation-colour (a colour name from list FC below)
  mkgmap:xt-light-colour (a colour name from list LC below)
  mkgmap:xt-light-type (a type name from list LT below)
  mkgmap:xt-period (optional period in secs (not limited to integer)

Point types 0x0103xx (entities with depth/height) and 0x0104xx (obstructions)
  mkgmap:xt-depth (a depth with optional units (ft or m) default is m)
  mkgmap:xt-height (alias for, and mutually exclusive to, xt-depth)
  mkgmap:xt-position (an optional position from list P below)

These attributes are turned into a sequence of bytes that are embodied
in the map object. For testing purposes you can also specify those bytes
explicitly as a sequence of hex digits using the mkgmap:xt-extra-bytes
tag but you won't want to use that unless you are working on attribute
value encoding (like me!)

Next up will be lights which I understand how to encode except for
multiple lights and angles - still working on that.

Then will come lines and areas.

All feedback welcome.

BTW a good source of info is the cgpsmapper manual which is readily
available on the net. It has a chapter on marine types.

---------------------------------

List FC
                        "red",
                        "green",
                        "yellow",
                        "white",
                        "black",
                        "black-yellow",
                        "white-red",
                        "black-red",
                        "white-green",
                        "red-yellow",
                        "red-green",
                        "orange",
                        "black-yellow-black",
                        "yellow-black",
                        "yellow-black-yellow",
                        "red-white",
                        "green-red-green",
                        "red-green-red",
                        "black-red-black",
                        "yellow-red-yellow",
                        "green-red",
                        "black-white",
                        "white-orange",
                        "orange-white",
                        "green-white"

List LC
                        "unlit",
                        "red",
                        "green",
                        "white",

There is possibly some more light colours but I don't yet know how to encode 
them so we should stick with these for the moment.

List LT
                        "fixed",
                        "isophase",
                        "flashing",
                        "group flashing",
                        "composite group flashing",
                        "occulting",
                        "group occulting",
                        "composite group occulting",
                        "long flashing",
                        "group long flashing",
                        "morse",
                        "quick",
                        "group quick",
                        "group quick and long flashing",
                        "interrupted quick",
                        "very quick",
                        "group very quick",
                        "group very quick and long flashing",
                        "interrupted very quick",
                        "ultra quick",
                        "interrupted ultra quick",
                        "fixed and occulting",
                        "fixed and group occulting",
                        "fixed and isophase",
                        "fixed and flashing",
                        "fixed and group flashing",
                        "fixed and long flashing",
                        "alternating",
                        "alternating occulting",
                        "alternating flashing",
                        "alternating group flashing"

The morse type takes a letter argument but I don't know how to encode
that yet.

 List P
                        "unknown",
                        "doubtful",
                        "existence doubtful",
                        "approximate",
                        "reported"
diff --git a/src/uk/me/parabola/imgfmt/app/trergn/ExtTypeAttributes.java b/src/uk/me/parabola/imgfmt/app/trergn/ExtTypeAttributes.java
new file mode 100644
index 0000000..aadde77
--- /dev/null
+++ b/src/uk/me/parabola/imgfmt/app/trergn/ExtTypeAttributes.java
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2006 Steve Ratcliffe
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License 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.
+ * 
+ * 
+ * Author: Steve Ratcliffe
+ * Create date: 12-Dec-2006
+ */
+package uk.me.parabola.imgfmt.app.trergn;
+
+import uk.me.parabola.imgfmt.app.Label;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import java.text.DecimalFormat;
+import java.text.ParsePosition;
+
+public class ExtTypeAttributes {
+
+	private Map<String, String> attributes;
+
+	private final int DISTANCE_FLAG_METRIC = 0;
+	private final int DISTANCE_FLAG_TENTHS = 1;
+
+	public ExtTypeAttributes(Map<String, String> attributes) {
+		this.attributes = attributes;
+	}
+
+	protected byte[] getExtTypeExtraBytes(MapObject mapObject) {
+
+		// first see if a string of raw hex digits has been supplied
+		// if so, use that and ignore everything else
+		String eb = attributes.get("extra-bytes");
+		if(eb != null) {
+			byte[] extraBytes = new byte[(eb.length() + 1) / 2];
+			for(int i = 0; i < eb.length(); ++i) {
+				int d = Integer.parseInt(eb.substring(i, i + 1), 16);
+				extraBytes[i / 2] |= (byte)(d << (4 * (1 - (i & 1))));
+			}
+			return extraBytes;
+		}
+
+		DecimalFormat df = new DecimalFormat();
+		int type8to15 = (mapObject.getType() >> 8) & 255;
+
+		if(mapObject instanceof Point) {
+
+			if(type8to15 == 0x02) { // buoys
+				int fc = foundationColour();
+				int lc = lightColour();
+				int lt = lightType();
+				boolean havePeriod = false;
+				int nob = 6;
+				String ps = attributes.get("period");
+				if(ps != null) {
+					++nob;
+					havePeriod = true;
+				}
+				byte[] extraBytes = new byte[nob + 2];
+				extraBytes[0] = (byte)0xe0;
+				extraBytes[1] = (byte)((nob << 1) | 1);
+				extraBytes[2] = (byte)((lc << 6) | fc);
+				extraBytes[4] = (byte)lt;
+				if(havePeriod) {
+					extraBytes[4] |= 0x80;
+					extraBytes[5] = (byte)(Double.parseDouble(ps) * 10);
+					extraBytes[6] = 0x01;
+				}
+				else {
+					extraBytes[5] = 0x01;
+				}
+
+				return extraBytes;
+			}
+			else if(type8to15 == 0x03 || // things with depth/height
+					type8to15 == 0x04) { // obstructions
+				String ds = attributes.get("depth");
+				if(ds == null)
+					ds = attributes.get("height");
+				if(ds != null) {
+					boolean[] distFlags = new boolean[2];
+					Integer di = parseDistance(ds, distFlags);
+					if(di != null) {
+						byte[] extraBytes;
+						if(di > 255) {
+							extraBytes = new byte[3];
+							extraBytes[0] = (byte)0xa0;
+						}
+						else {
+							extraBytes = new byte[2];
+							extraBytes[0] = (byte)0x80;
+						}
+						if(distFlags[DISTANCE_FLAG_METRIC])
+							extraBytes[0] |= 0x10;
+						if(distFlags[DISTANCE_FLAG_TENTHS])
+							extraBytes[0] |= 0x08;
+						extraBytes[1] = (byte)(int)di;
+						if(di > 255)
+							extraBytes[2] = (byte)(di >> 8);
+
+						if(type8to15 == 0x04) { // obstructions
+							extraBytes[0] |= position();
+						}
+
+						return extraBytes;
+					}
+				}
+			}
+		}
+
+		return null;
+	}
+
+	private Integer parseDistance(String ds, boolean[] flags) {
+		DecimalFormat df = new DecimalFormat();
+		ParsePosition pp = new ParsePosition(0);
+		Number dn = df.parse(ds, pp);
+		if(dn != null) {
+			double dd = dn.doubleValue();
+			int di = dn.intValue();
+			flags[DISTANCE_FLAG_METRIC] = true;
+			flags[DISTANCE_FLAG_TENTHS] = false;
+			if("ft".equals(ds.substring(pp.getIndex()).toLowerCase()))
+				flags[DISTANCE_FLAG_METRIC] = false;
+			if((double)di != dd) {
+				// number has fractional part
+				di = (int)(dd * 10);
+				flags[DISTANCE_FLAG_TENTHS] = true;
+			}
+
+			return di;
+		}
+
+		return null;
+	}
+
+	private int foundationColour() {
+
+		String fc = attributes.get("foundation-colour");
+		if(fc == null)
+			fc = attributes.get("foundation-color");
+		if(fc == null)
+			return 0;
+
+		String[] colours = {
+			"",
+			"red",
+			"green",
+			"yellow",
+			"white",
+			"black",
+			"black-yellow",
+			"white-red",
+			"black-red",
+			"white-green",
+			"red-yellow",
+			"red-green",
+			"orange",
+			"black-yellow-black",
+			"yellow-black",
+			"yellow-black-yellow",
+			"red-white",
+			"green-red-green",
+			"red-green-red",
+			"black-red-black",
+			"yellow-red-yellow",
+			"green-red",
+			"black-white",
+			"white-orange",
+			"orange-white",
+			"green-white"
+		};
+
+		fc = fc.toLowerCase();
+
+		for(int i = 0; i < colours.length; ++i)
+			if(colours[i].equals(fc))
+				return i;
+
+		return 0;
+	}
+
+	private int lightColour() {
+		String lc = attributes.get("light-colour");
+		if(lc == null)
+			lc = attributes.get("light-color");
+		if(lc == null)
+			return 0;
+
+		String[] colours = {
+			"unlit",
+			"red",
+			"green",
+			"white",
+			"blue",
+			"yellow",
+			"violet",
+			"amber"
+		};
+
+		lc = lc.toLowerCase();
+
+		for(int i = 0; i < colours.length; ++i)
+			if(colours[i].equals(lc))
+				return i;
+
+		return 0;
+	}
+
+	private int lightType() {
+		String lt = attributes.get("light-type");
+		if(lt == null)
+			return 0;
+
+		String[] types = {
+			"",
+			"fixed",
+			"isophase",
+			"flashing",
+			"group flashing",
+			"composite group flashing",
+			"occulting",
+			"group occulting",
+			"composite group occulting",
+			"long flashing",
+			"group long flashing",
+			"morse",
+			"quick",
+			"group quick",
+			"group quick and long flashing",
+			"interrupted quick",
+			"very quick",
+			"group very quick",
+			"group very quick and long flashing",
+			"interrupted very quick",
+			"ultra quick",
+			"interrupted ultra quick",
+			"fixed and occulting",
+			"fixed and group occulting",
+			"fixed and isophase",
+			"fixed and flashing",
+			"fixed and group flashing",
+			"fixed and long flashing",
+			"alternating",
+			"alternating occulting",
+			"alternating flashing",
+			"alternating group flashing"
+		};
+
+		lt = lt.toLowerCase();
+
+		for(int i = 0; i < types.length; ++i)
+			if(types[i].equals(lt))
+				return i;
+
+		return 0;
+	}
+
+	private int position() {
+		String ps = attributes.get("position");
+		if(ps == null)
+			return 0;
+
+		String[] positions = {
+			"unknown",
+			"",
+			"doubtful",
+			"existence doubtful",
+			"approximate",
+			"reported"
+		};
+
+		ps = ps.toLowerCase();
+
+		for(int i = 0; i < positions.length; ++i)
+			if(positions[i].equals(ps))
+				return i;
+
+		return 0;
+	}
+}
diff --git a/src/uk/me/parabola/imgfmt/app/trergn/MapObject.java b/src/uk/me/parabola/imgfmt/app/trergn/MapObject.java
index 142f859..bbbd411 100644
--- a/src/uk/me/parabola/imgfmt/app/trergn/MapObject.java
+++ b/src/uk/me/parabola/imgfmt/app/trergn/MapObject.java
@@ -55,6 +55,8 @@ public abstract class MapObject {
 	private int deltaLong;
 	private int deltaLat;
 
+	private ExtTypeAttributes extTypeAttributes;
+
 	/**
 	 * Write this object to the given file.
 	 *
@@ -147,4 +149,12 @@ public abstract class MapObject {
 	public List<Label> getRefLabels() {
 		return refLabels;
 	}
+
+	protected byte[] getExtTypeExtraBytes() {
+		return (extTypeAttributes != null)? extTypeAttributes.getExtTypeExtraBytes(this) : null;
+	}
+
+	public void setExtTypeAttributes(ExtTypeAttributes eta) {
+		extTypeAttributes = eta;
+	}
 }
diff --git a/src/uk/me/parabola/imgfmt/app/trergn/Point.java b/src/uk/me/parabola/imgfmt/app/trergn/Point.java
index 84b6434..65f48f7 100644
--- a/src/uk/me/parabola/imgfmt/app/trergn/Point.java
+++ b/src/uk/me/parabola/imgfmt/app/trergn/Point.java
@@ -83,6 +83,7 @@ public class Point extends MapObject {
 		assert hasExtendedType();
 		int type = getType();
 		int labelOff = getLabel().getOffset();
+		byte[] extraBytes = getExtTypeExtraBytes();
 
 		if (poi != null) {
 			labelOff = poi.getOffset();
@@ -90,9 +91,11 @@ public class Point extends MapObject {
 		}
 		if(labelOff != 0)
 			type |= 0x20;		// has label
-
+		if(extraBytes != null)
+			type |= 0x80;		// has extra bytes
 		stream.write(type >> 8);
 		stream.write(type);
+
 		int deltaLong = getDeltaLong();
 		int deltaLat = getDeltaLat();
 		stream.write(deltaLong);
@@ -105,7 +108,9 @@ public class Point extends MapObject {
 			stream.write(labelOff >> 8);
 			stream.write(labelOff >> 16);
 		}
-		// FIXME - extra bytes?
+
+		if(extraBytes != null)
+			stream.write(extraBytes);
 	}
 
 	public void setPOIRecord(POIRecord poirecord) {
diff --git a/src/uk/me/parabola/imgfmt/app/trergn/Polyline.java b/src/uk/me/parabola/imgfmt/app/trergn/Polyline.java
index 64d7faa..b52774c 100644
--- a/src/uk/me/parabola/imgfmt/app/trergn/Polyline.java
+++ b/src/uk/me/parabola/imgfmt/app/trergn/Polyline.java
@@ -147,9 +147,12 @@ public class Polyline extends MapObject {
 		assert hasExtendedType();
 		int type = getType();
 		int labelOff = getLabel().getOffset();
+		byte[] extraBytes = getExtTypeExtraBytes();
 
 		if(labelOff != 0)
 			type |= 0x20;		// has label
+		if(extraBytes != null)
+			type |= 0x80;		// has extra bytes
 		stream.write(type >> 8);
 		stream.write(type);
 
@@ -182,7 +185,9 @@ public class Polyline extends MapObject {
 			stream.write(labelOff >> 8);
 			stream.write(labelOff >> 16);
 		}
-		// FIXME - extra bytes?
+
+		if(extraBytes != null)
+			stream.write(extraBytes);
 	}
 
 	public void addCoord(Coord co) {
diff --git a/src/uk/me/parabola/mkgmap/build/MapBuilder.java b/src/uk/me/parabola/mkgmap/build/MapBuilder.java
index efe7899..938c5d6 100644
--- a/src/uk/me/parabola/mkgmap/build/MapBuilder.java
+++ b/src/uk/me/parabola/mkgmap/build/MapBuilder.java
@@ -37,6 +37,7 @@ import uk.me.parabola.imgfmt.app.map.Map;
 import uk.me.parabola.imgfmt.app.net.NETFile;
 import uk.me.parabola.imgfmt.app.net.NODFile;
 import uk.me.parabola.imgfmt.app.net.RoadDef;
+import uk.me.parabola.imgfmt.app.trergn.ExtTypeAttributes;
 import uk.me.parabola.imgfmt.app.trergn.Overview;
 import uk.me.parabola.imgfmt.app.trergn.Point;
 import uk.me.parabola.imgfmt.app.trergn.PointOverview;
@@ -309,7 +310,7 @@ public class MapBuilder implements Configurable {
 			if(p.isExit()) {
 				processExit(map, (MapExitPoint)p);
 			}
-			else if(!p.isCity() && (p.isRoadNamePOI() || poiAddresses)) {
+			else if(!p.isCity() && !p.hasExtendedType() && (p.isRoadNamePOI() || poiAddresses)) {
 				boolean doAutofill;
 				if(locationAutofillLevel > 0 || p.isRoadNamePOI())
 					doAutofill = true;
@@ -738,6 +739,9 @@ public class MapBuilder implements Configurable {
 			Point p = div.createPoint(name);
 			p.setType(point.getType());
 
+			if(point.hasExtendedType())
+				p.setExtTypeAttributes(new ExtTypeAttributes(point.getExtTypeAttributes()));
+
 			Coord coord = point.getLocation();
 			p.setLatitude(coord.getLatitude());
 			p.setLongitude(coord.getLongitude());
@@ -952,6 +956,9 @@ public class MapBuilder implements Configurable {
 			Polyline pl = div.createLine(line.getName(), line.getRef());
 			if(!element.hasExtendedType())
 				div.setPolylineNumber(pl);
+			else
+				pl.setExtTypeAttributes(new ExtTypeAttributes(element.getExtTypeAttributes()));
+
 			pl.setDirection(line.isDirection());
 
 			for (Coord co : line.getPoints())
@@ -991,6 +998,9 @@ public class MapBuilder implements Configurable {
 				pg.addCoord(co);
 
 			pg.setType(shape.getType());
+			if(element.hasExtendedType())
+				pg.setExtTypeAttributes(new ExtTypeAttributes(element.getExtTypeAttributes()));
+
 			map.addMapObject(pg);
 		}
 	}
diff --git a/src/uk/me/parabola/mkgmap/general/MapElement.java b/src/uk/me/parabola/mkgmap/general/MapElement.java
index c6c2abd..2fb1b15 100644
--- a/src/uk/me/parabola/mkgmap/general/MapElement.java
+++ b/src/uk/me/parabola/mkgmap/general/MapElement.java
@@ -33,11 +33,8 @@ public abstract class MapElement {
 
 	private int minResolution = 24;
 	private int maxResolution = 24;
-	
-	private String zipCode;
-	private String city;
-	private String region;
-	private String country;	
+
+	private Map<String, String> extTypeAttributes;
 	
 	private final Map<String, String> attributes = new HashMap<String, String>();
 
@@ -76,36 +73,44 @@ public abstract class MapElement {
 		this.ref = ref;
 	}
 
+	public Map<String, String> getExtTypeAttributes() {
+		return extTypeAttributes;
+	}
+
+	public void setExtTypeAttributes(Map<String, String> eta) {
+		extTypeAttributes = eta;
+	}
+
 	public String getCity() {
-		return city;
+		return attributes.get("city");
 	}
 
 	public void setCity(String city) {
-		this.city = city;
+		attributes.put("city", city);
 	}
 	
 	public String getZip() {
-		return zipCode;
+		return attributes.get("zip");
 	}
 
 	public void setZip(String zip) {
-		this.zipCode = zip;
+		attributes.put("zip", zip);
 	}
 
 	public String getCountry() {
-		return country;
+		return attributes.get("country");
 	}
 
 	public void setCountry(String country) {
-		this.country = country;
+		attributes.put("country", country);
 	}
 	
 	public String getRegion() {
-		return region;
+		return attributes.get("region");
 	}
 
 	public void setRegion(String region) {
-  		this.region = region;
+  		attributes.put("region", region);
 	}	
 	
 	public String getStreet() {
diff --git a/src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java b/src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java
index e3b7d32..43ab044 100644
--- a/src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java
+++ b/src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java
@@ -509,6 +509,9 @@ public class StyledConverter implements OsmConverter {
 
 		if(region != null)
 		  ms.setRegion(region);			
+
+		if(MapElement.hasExtendedType(gt.getType()))
+			ms.setExtTypeAttributes(element.getTagsWithPrefix("mkgmap:xt-", true));
 	}
 
 	void addRoad(Way way, GType gt) {
diff --git a/src/uk/me/parabola/mkgmap/reader/osm/Element.java b/src/uk/me/parabola/mkgmap/reader/osm/Element.java
index a49e7c7..c7dc51b 100644
--- a/src/uk/me/parabola/mkgmap/reader/osm/Element.java
+++ b/src/uk/me/parabola/mkgmap/reader/osm/Element.java
@@ -17,6 +17,7 @@ package uk.me.parabola.mkgmap.reader.osm;
 
 import java.util.Collections;
 import java.util.Iterator;
+import java.util.Map;
 
 /**
  * Superclass of the node, segment and way OSM elements.
@@ -97,4 +98,8 @@ public abstract class Element implements Iterable<String> {
 		if (this.name == null)
 			this.name = name;
 	}
+
+	public Map<String, String> getTagsWithPrefix(String prefix, boolean removePrefix) {
+		return tags.getTagsWithPrefix(prefix, removePrefix);
+	}
 }
diff --git a/src/uk/me/parabola/mkgmap/reader/osm/Tags.java b/src/uk/me/parabola/mkgmap/reader/osm/Tags.java
index e312316..57d2ba6 100644
--- a/src/uk/me/parabola/mkgmap/reader/osm/Tags.java
+++ b/src/uk/me/parabola/mkgmap/reader/osm/Tags.java
@@ -16,7 +16,9 @@
  */
 package uk.me.parabola.mkgmap.reader.osm;
 
+import java.util.HashMap;
 import java.util.Iterator;
+import java.util.Map;
 
 /**
  * Store the tags that belong to an Element.
@@ -271,4 +273,20 @@ public class Tags implements Iterable<String> {
 					put(e.key, e.value);
 		}
 	}
+
+	public Map<String, String> getTagsWithPrefix(String prefix, boolean removePrefix) {
+		Map<String, String> map = new HashMap<String, String>();
+
+		int prefixLen = prefix.length();
+		for(int i = 0; i < capacity; ++i) {
+			if(keys[i] != null && keys[i].startsWith(prefix)) {
+				if(removePrefix)
+					map.put(keys[i].substring(prefixLen), values[i]);
+				else
+					map.put(keys[i], values[i]);
+			}
+		}
+
+		return map;
+	}
 }
_______________________________________________
mkgmap-dev mailing list
mkgmap-dev@lists.mkgmap.org.uk
http://www.mkgmap.org.uk/mailman/listinfo/mkgmap-dev

Reply via email to