v2 - moving swiftly on.

Added support for lights and reworked a few things.

mkgmap:xt-foundation-colour is now just mkgmap:xt-colour

mkgmap:xt-light-colour has been replaced with mkgmap:xt-light (more on
this below)

mkgmap:xt-light-type is now just mkgmap:xt-type

Light colour, range (in nm) and angle is now specified with the
mkgmap:xt-light tag - the values are just separated with commas, and
only the colour is required for simple lights. All angles are in
degrees. So, for example:

mkgmap:xt-light=red (a red light)
mkgmap:xt-light=red,5 (... visible at up to 5 nm)
mkgmap:xt-light=red,5,172 (... sector starts at 172 degrees)

The sector start angle only makes sense if you have multiple lights
defined (separate light defs with one of /;:) - for example:

mkgmap:xt-light=red,5,172/green,5,230/unlit,,300

you can add a text note, international designator or local designator
to a buoy or light with:

mkgmap:xt-note=hello world
mkgmap:xt-int-desig=ABC
mkgmap:xt-local-desig=XYZ

These show up in the POI properties window in mapsource.

There is also:

mkgmap:xt-height-above-foundation (a height)
mkgmap:xt-height-above-datum (a height)
mkgmap:xt-leading-angle (an angle)

you can now specify multiple period values to give a flash sequence
but, unfortunately, it only encodes 2 values at the moment, the
encoding for more than 2 values is yet to be discovered.

The full range of light colours is now supported:

        "unlit",
        "red",
        "green",
        "white",
        "blue",
        "yellow",
        "violet",
        "amber"

So, lights, buoys, obstructions are now fairly well catered for so it's
probably time to look at lines and areas.

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


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..14e0838
--- /dev/null
+++ b/src/uk/me/parabola/imgfmt/app/trergn/ExtTypeAttributes.java
@@ -0,0 +1,562 @@
+/*
+ * 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 uk.me.parabola.imgfmt.app.lbl.LBLFile;
+
+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 DecimalFormat decimalFormat = new DecimalFormat();
+
+	private Map<String, String> attributes;
+
+	private byte[] extraBytes;
+
+	private Label note;
+	private Label intDesig;
+	private Label localDesig;
+
+	private final int DISTANCE_FLAG_METRIC_INDEX = 0;
+	private final int DISTANCE_FLAG_TENTHS_INDEX = 1;
+
+	private final byte LABEL_NOTE_BIT        = (1 << 1);
+	private final byte LABEL_INT_DESIG_BIT   = (1 << 2);
+	private final byte LABEL_LOCAL_DESIG_BIT = (1 << 3);
+
+	public ExtTypeAttributes(Map<String, String> attributes, LBLFile lbl) {
+		this.attributes = attributes;
+		if(lbl != null)
+			processLabels(lbl);
+	}
+
+	public void processLabels(LBLFile lbl) {
+		String ns = attributes.get("note");
+		if(ns != null)
+			note = lbl.newLabel(ns);
+		String ids = attributes.get("int-desig");
+		if(ids != null)
+			intDesig = lbl.newLabel(ids);
+		String lds = attributes.get("local-desig");
+		if(lds != null)
+			localDesig = lbl.newLabel(lds);
+	}
+
+	protected byte[] getExtTypeExtraBytes(MapObject mapObject) {
+
+		// if we get called again, just return same result as before
+		if(extraBytes != null)
+			return extraBytes;
+
+		// 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) {
+			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;
+		}
+
+		int type8to15 = (mapObject.getType() >> 8) & 255;
+
+		Light lights[] = parseLights(attributes.get("light"));
+
+		if(mapObject instanceof Point) {
+
+			if(type8to15 == 0x01) { // lights
+				int lt = lightType("");
+				int nob = 6;
+				int labelBits = 0;
+				if(lights != null && lights.length > 1) {
+					for(Light l : lights)
+						if(l.colour != 0)
+							nob += 3;
+						else
+							nob += 2;
+					nob += 2;
+				}
+				if(note != null) {
+					nob += 3;
+					labelBits |= LABEL_NOTE_BIT;
+				}
+				if(intDesig != null) {
+					nob += 3;
+					labelBits |= LABEL_INT_DESIG_BIT;
+				}
+				if(localDesig != null) {
+					nob += 3;
+					labelBits |= LABEL_LOCAL_DESIG_BIT;
+				}
+				int[] periods = null;
+				String ps = attributes.get("period");
+				if(ps != null) {
+					periods = parsePeriods(ps);
+					if(periods.length > 1)
+						nob += periods.length;
+				}
+				byte lightsDef = 0x22;
+				String hafs = attributes.get("height-above-foundation");
+				boolean[] hafsDistFlags = new boolean[2];
+				Integer hafi = null;
+				if(hafs != null) {
+					hafi = parseDistance(hafs, hafsDistFlags);
+					if(hafsDistFlags[DISTANCE_FLAG_TENTHS_INDEX])
+						hafi /= 10;
+					nob += (hafi > 255)? 2 : 1;
+					if(hafi > 255)
+						lightsDef |= 0x80;
+					else
+						lightsDef |= 0x40;
+					if(!hafsDistFlags[DISTANCE_FLAG_METRIC_INDEX])
+						lightsDef &= ~0x20;
+				}
+				String hads = attributes.get("height-above-datum");
+				boolean[] hadsDistFlags = new boolean[2];
+				Integer hadi = null;
+				if(hads != null) {
+					hadi = parseDistance(hads, hadsDistFlags);
+					if(hadsDistFlags[DISTANCE_FLAG_TENTHS_INDEX])
+						hadi /= 10;
+					nob += (hadi > 255)? 2 : 1;
+					if(hadi > 255)
+						lightsDef |= 0x08;
+					else
+						lightsDef |= 0x04;
+					if(!hadsDistFlags[DISTANCE_FLAG_METRIC_INDEX])
+						lightsDef &= ~0x02;
+				}
+				String las = attributes.get("leading-angle");
+			    Integer leadingAngle = null;
+				if(las != null) {
+					leadingAngle = (int)(Double.parseDouble(las.trim()) * 10);
+					nob += 2;
+				}
+				extraBytes = new byte[nob + 2];
+				int i = 0;
+				extraBytes[i++] = (byte)(0xe0 | labelBits);
+				extraBytes[i++] = (byte)((nob << 1) | 1); // bit0 always set?
+				extraBytes[i++] = (byte)(0x80 | lt);
+				byte flags = 0;
+				if(periods != null && periods.length > 1)
+					flags |= 0x01; // flash sequence present
+				if(leadingAngle != null)
+					flags |= 0x02; // leading angle present
+				if(lights != null && lights.length > 1)
+					flags |= 0x08; // multiple lights
+				extraBytes[i++] = flags;
+				extraBytes[i++] = lightsDef;
+				if(hafi != null) {
+					extraBytes[i++] = (byte)(int)hafi;
+					if(hafi > 255)
+						extraBytes[i++] = (byte)(hafi >> 8);
+				}
+				if(hadi != null) {
+					extraBytes[i++] = (byte)(int)hadi;
+					if(hadi > 255)
+						extraBytes[i++] = (byte)(hadi >> 8);
+				}
+				int period = 0;
+				if(periods != null)
+					for(int p : periods)
+						period += p;
+				extraBytes[i++] = (byte)period;
+				if(note != null) {
+					int off = note.getOffset();
+					extraBytes[i++] = (byte)off;
+					extraBytes[i++] = (byte)(off >> 8);
+					extraBytes[i++] = (byte)(off >> 16);
+				}
+				if(localDesig != null) {
+					int off = localDesig.getOffset();
+					extraBytes[i++] = (byte)off;
+					extraBytes[i++] = (byte)(off >> 8);
+					extraBytes[i++] = (byte)(off >> 16);
+				}
+				if(intDesig != null) {
+					int off = intDesig.getOffset();
+					extraBytes[i++] = (byte)off;
+					extraBytes[i++] = (byte)(off >> 8);
+					extraBytes[i++] = (byte)(off >> 16);
+				}
+				if(leadingAngle != null) {
+					extraBytes[i++] = (byte)(int)leadingAngle;
+					extraBytes[i++] = (byte)(leadingAngle >> 8);
+				}
+				if(lights != null && lights.length > 1) {
+					for(int l = 0; l < lights.length; ++l) {
+						int val = (lights[l].colour << 12) | (int)(lights[l].angle * 10);
+						extraBytes[i++] = (byte)val;
+						extraBytes[i++] = (byte)(val >> 8);
+						if(lights[l].colour != 0)
+							extraBytes[i++] = (byte)lights[l].range;
+					}
+					int val = (int)(lights[0].angle * 10);
+					val |= 0x8000;
+					extraBytes[i++] = (byte)val;
+					extraBytes[i++] = (byte)(val >> 8);
+				}
+				else {
+					int lc = 0;
+					int lr = 0;
+					if(lights != null && lights.length > 0) {
+						lc = lights[0].colour;
+						lr = (int)lights[0].range & 0x1f;
+					}
+					extraBytes[i++] = (byte)((lc << 5) | lr);
+				}
+				if(periods != null && periods.length > 1) {
+					extraBytes[i++] = (byte)0x81;
+					for(int p : periods)
+						extraBytes[i++] = (byte)p;
+				}
+				else
+					extraBytes[i++] = 0x01; // terminator?
+
+				return extraBytes;
+			}
+			else if(type8to15 == 0x02) { // buoys
+				int fc = colour("");
+				int lt = lightType("");
+				int nob = 4;
+				int labelBits = 0;
+				if(note != null) {
+					nob += 3;
+					labelBits |= LABEL_NOTE_BIT;
+				}
+				if(intDesig != null) {
+					nob += 3;
+					labelBits |= LABEL_INT_DESIG_BIT;
+				}
+				if(localDesig != null) {
+					nob += 3;
+					labelBits |= LABEL_LOCAL_DESIG_BIT;
+				}
+				int[] periods = null;
+				String ps = attributes.get("period");
+				if(ps != null) {
+					periods = parsePeriods(ps);
+					if(periods.length > 1)
+						nob += periods.length;
+					++nob;		// for total period
+				}
+				extraBytes = new byte[nob + 2];
+				int i = 0;
+				extraBytes[i++] = (byte)(0xe0 | labelBits);
+				extraBytes[i++] = (byte)((nob << 1) | 1); // bit0 always set?
+				byte flags = 0;
+				int lc = 0;
+				if(lights != null && lights.length > 0) {
+					lc = lights[0].colour;
+				}
+				extraBytes[i++] = (byte)((lc << 6) | fc);
+				flags |= (byte)((lc >> 2) & 1); // bit 0 is MSB of light colour
+				if(periods != null && periods.length > 1)
+					flags |= 0x02; // flash sequence present
+				extraBytes[i++] = flags;
+				if(note != null) {
+					int off = note.getOffset();
+					extraBytes[i++] = (byte)off;
+					extraBytes[i++] = (byte)(off >> 8);
+					extraBytes[i++] = (byte)(off >> 16);
+				}
+				if(localDesig != null) {
+					int off = localDesig.getOffset();
+					extraBytes[i++] = (byte)off;
+					extraBytes[i++] = (byte)(off >> 8);
+					extraBytes[i++] = (byte)(off >> 16);
+				}
+				if(intDesig != null) {
+					int off = intDesig.getOffset();
+					extraBytes[i++] = (byte)off;
+					extraBytes[i++] = (byte)(off >> 8);
+					extraBytes[i++] = (byte)(off >> 16);
+				}
+				flags = 0;
+				if(periods != null)
+					flags |= (byte)0x80;
+				extraBytes[i++] = (byte)(flags | lt);
+				if(periods != null) {
+					int period = 0;
+					for(int p : periods)
+						period += p;
+					extraBytes[i++] = (byte)period;
+					if(periods.length > 1) {
+						extraBytes[i++] = (byte)0x81;
+						for(int p : periods)
+							extraBytes[i++] = (byte)p;
+					}
+					else
+						extraBytes[i++] = 0x01; // terminator?
+				}
+				else
+					extraBytes[i++] = 0x01; // terminator?
+
+				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) {
+						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_INDEX])
+							extraBytes[0] |= 0x10;
+						if(distFlags[DISTANCE_FLAG_TENTHS_INDEX])
+							extraBytes[0] |= 0x08;
+						if(type8to15 == 0x04) { // obstructions
+							extraBytes[0] |= position();
+						}
+						extraBytes[1] = (byte)(int)di;
+						if(di > 255)
+							extraBytes[2] = (byte)(di >> 8);
+
+						return extraBytes;
+					}
+				}
+			}
+		}
+
+		return null;
+	}
+
+	private Integer parseDistance(String ds, boolean[] flags) {
+		ParsePosition pp = new ParsePosition(0);
+		Number dn = decimalFormat.parse(ds, pp);
+		if(dn != null) {
+			double dd = dn.doubleValue();
+			int di = dn.intValue();
+			flags[DISTANCE_FLAG_METRIC_INDEX] = true;
+			flags[DISTANCE_FLAG_TENTHS_INDEX] = false;
+			if("ft".equals(ds.substring(pp.getIndex()).trim().toLowerCase()))
+				flags[DISTANCE_FLAG_METRIC_INDEX] = false;
+			if((double)di != dd) {
+				// number has fractional part
+				di = (int)(dd * 10);
+				flags[DISTANCE_FLAG_TENTHS_INDEX] = true;
+			}
+
+			return di;
+		}
+
+		return null;
+	}
+
+	private int colour(String prefix) {
+
+		String fc = attributes.get(prefix + "colour");
+		if(fc == null)
+			fc = attributes.get(prefix + "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 lightType(String prefix) {
+		String lt = attributes.get(prefix + "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;
+	}
+
+	private int[] parsePeriods(String ps) {
+		String [] psa = ps.split(",");
+		int [] periods = new int[psa.length];
+		for(int i = 0; i < psa.length; ++i)
+			periods[i] = (int)(Double.parseDouble(psa[i].trim()) * 10);
+		return periods;
+	}
+
+	private Light[] parseLights(String ls) {
+		Light[] lights = null;
+		if(ls != null) {
+			//			// convert '(foo)' into ' foo;'
+			//			ls = ls.replace('(', ' ');
+			//			ls = ls.replace(')', ';');
+			String[] defs = ls.split("[:;/]");
+			lights = new Light[defs.length];
+			for(int i = 0; i < defs.length; ++i) {
+				String def = defs[i].trim();
+				if(def.length() > 0)
+					lights[i] = new Light(def);
+			}
+		}
+		return lights;
+	}
+
+	class Light {
+		private int    colour;
+		private double range;
+		private double angle;
+
+		String[] colours = {
+			"unlit",
+			"red",
+			"green",
+			"white",
+			"blue",
+			"yellow",
+			"violet",
+			"amber"
+		};
+
+		public Light(String desc) {
+			String[] parts = desc.split(",");
+			if(parts.length > 0) {
+				String lc = parts[0].toLowerCase();
+				for(int i = 0; i < colours.length; ++i) {
+					if(colours[i].equals(lc)) {
+						colour = i;
+						break;
+					}
+				}
+			}
+			if(parts.length > 1 && colour != 0)
+				range = Double.parseDouble(parts[1]);
+			if(parts.length > 2)
+				angle = Double.parseDouble(parts[2]);
+
+			//System.err.println("light = " + this);
+		}
+
+		public String toString() {
+			return "(" + colours[colour] + "," + range + "," + angle + ")";
+		}
+	}
+}
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..08d6263 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(), lbl));
+
 			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(), map.getLblFile()));
+
 			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.getLblFile()));
+
 			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