Index: applications/editors/josm/plugins/smed2/src/panels/PanelMain.java
===================================================================
--- applications/editors/josm/plugins/smed2/src/panels/PanelMain.java	(revision 30149)
+++ applications/editors/josm/plugins/smed2/src/panels/PanelMain.java	(revision 30150)
@@ -22,6 +22,6 @@
 import s57.S57att.Att;
 import s57.S57obj.Obj;
-import seamap.Renderer;
-import seamap.SeaMap.*;
+import s57.S57map.*;
+import render.Renderer;
 import smed2.Smed2Action;
 
@@ -48,5 +48,5 @@
         int returnVal = ifc.showOpenDialog(Main.parent);
         if (returnVal == JFileChooser.APPROVE_OPTION) {
-          Smed2Action.panelS57.startImport(ifc.getSelectedFile());
+//          xxx.startImport(ifc.getSelectedFile());
          } else {
            messageBar.setText("");
@@ -64,5 +64,5 @@
         int returnVal = efc.showOpenDialog(Main.parent);
         if (returnVal == JFileChooser.APPROVE_OPTION) {
-          Smed2Action.panelS57.startExport(efc.getSelectedFile());
+//          xxx.startExport(efc.getSelectedFile());
          } else {
            messageBar.setText("");
@@ -113,5 +113,5 @@
 		decode.setBounds(0, 0, 480, 420);
 		decode.setTabSize(1);
-//		add(decode);
+		add(decode);
 	}
 	
Index: applications/editors/josm/plugins/smed2/src/panels/ShowFrame.java
===================================================================
--- applications/editors/josm/plugins/smed2/src/panels/ShowFrame.java	(revision 30149)
+++ applications/editors/josm/plugins/smed2/src/panels/ShowFrame.java	(revision 30150)
@@ -12,17 +12,17 @@
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
 
-import seamap.MapContext;
-import seamap.Renderer;
-import seamap.SeaMap;
-import seamap.SeaMap.*;
+import render.MapContext;
+import render.Renderer;
+import s57.S57map;
+import s57.S57map.*;
 
 public class ShowFrame extends JFrame {
 	
-	SeaMap showMap;
+	S57map showMap;
 	Picture picture;
 
 	class Picture extends JPanel implements MapContext {
 
-		public void drawPicture(OsmPrimitive osm, SeaMap map) {
+		public void drawPicture(OsmPrimitive osm, S57map map) {
 			long id;
 			Feature feature;
@@ -30,5 +30,5 @@
 			id = osm.getUniqueId();
 			feature = map.index.get(id);
-			showMap = new SeaMap();
+			showMap = new S57map();
 			showMap.nodes = map.nodes;
 			showMap.edges = map.edges;
@@ -66,5 +66,5 @@
 	}
 	
-	public void showFeature(OsmPrimitive osm, SeaMap map) {
+	public void showFeature(OsmPrimitive osm, S57map map) {
 		picture.drawPicture(osm, map);
 	}
Index: applications/editors/josm/plugins/smed2/src/render/MapContext.java
===================================================================
--- applications/editors/josm/plugins/smed2/src/render/MapContext.java	(revision 30150)
+++ applications/editors/josm/plugins/smed2/src/render/MapContext.java	(revision 30150)
@@ -0,0 +1,10 @@
+package render;
+
+import java.awt.geom.Point2D;
+
+import s57.S57map.*;
+
+public interface MapContext {
+	Point2D getPoint(Snode coord);
+	double mile(Feature feature);
+}
Index: applications/editors/josm/plugins/smed2/src/render/Renderer.java
===================================================================
--- applications/editors/josm/plugins/smed2/src/render/Renderer.java	(revision 30150)
+++ applications/editors/josm/plugins/smed2/src/render/Renderer.java	(revision 30150)
@@ -0,0 +1,563 @@
+/* Copyright 2013 Malcolm Herring
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3 of the License.
+ *
+ * For a copy of the GNU General Public License, see <http://www.gnu.org/licenses/>.
+ */
+
+package render;
+
+import java.awt.*;
+import java.awt.font.*;
+import java.awt.geom.*;
+import java.awt.image.*;
+import java.util.*;
+
+import s57.S57val.*;
+import s57.S57map;
+import s57.S57map.*;
+import s57.S57map.Area;
+import symbols.Areas;
+import symbols.Symbols;
+import symbols.Symbols.*;
+
+public class Renderer {
+
+	public static final double symbolScale[] = { 256.0, 128.0, 64.0, 32.0, 16.0, 8.0, 4.0, 2.0, 1.0, 0.61, 0.372, 0.227, 0.138, 0.0843, 0.0514, 0.0313, 0.0191, 0.0117, 0.007, 0.138 };
+
+	static final EnumMap<ColCOL, Color> bodyColours = new EnumMap<ColCOL, Color>(ColCOL.class);
+	static {
+		bodyColours.put(ColCOL.COL_UNK, new Color(0, true));
+		bodyColours.put(ColCOL.COL_WHT, new Color(0xffffff));
+		bodyColours.put(ColCOL.COL_BLK, new Color(0x000000));
+		bodyColours.put(ColCOL.COL_RED, new Color(0xd40000));
+		bodyColours.put(ColCOL.COL_GRN, new Color(0x00d400));
+		bodyColours.put(ColCOL.COL_BLU, Color.blue);
+		bodyColours.put(ColCOL.COL_YEL, new Color(0xffd400));
+		bodyColours.put(ColCOL.COL_GRY, Color.gray);
+		bodyColours.put(ColCOL.COL_BRN, new Color(0x8b4513));
+		bodyColours.put(ColCOL.COL_AMB, new Color(0xfbf00f));
+		bodyColours.put(ColCOL.COL_VIO, new Color(0xee82ee));
+		bodyColours.put(ColCOL.COL_ORG, Color.orange);
+		bodyColours.put(ColCOL.COL_MAG, new Color(0xf000f0));
+		bodyColours.put(ColCOL.COL_PNK, Color.pink);
+	}
+
+	static final EnumMap<ColPAT, Patt> pattMap = new EnumMap<ColPAT, Patt>(ColPAT.class);
+	static {
+		pattMap.put(ColPAT.PAT_UNKN, Patt.Z);
+		pattMap.put(ColPAT.PAT_HORI, Patt.H);
+		pattMap.put(ColPAT.PAT_VERT, Patt.V);
+		pattMap.put(ColPAT.PAT_DIAG, Patt.D);
+		pattMap.put(ColPAT.PAT_BRDR, Patt.B);
+		pattMap.put(ColPAT.PAT_SQUR, Patt.S);
+		pattMap.put(ColPAT.PAT_CROS, Patt.C);
+		pattMap.put(ColPAT.PAT_SALT, Patt.X);
+		pattMap.put(ColPAT.PAT_STRP, Patt.H);
+	}
+	
+	public enum LabelStyle { NONE, RRCT, RECT, ELPS, CIRC, VCLR, PCLR, HCLR }
+
+	static MapContext context;
+	static S57map map;
+	static double sScale;
+	static Graphics2D g2;
+	static int zoom;
+
+	public static void reRender(Graphics2D g, int z, double factor, S57map m, MapContext c) {
+		g2 = g;
+		zoom = z;
+		context = c;
+		map = m;
+		sScale = symbolScale[zoom] * factor;
+		if (map != null) {
+			g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+			g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_GASP);
+			g2.setStroke(new BasicStroke(0, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER));
+			Rules.rules();
+		}
+	}
+
+	public static void symbol(Feature feature, Symbol symbol) {
+		Point2D point = context.getPoint(feature.centre);
+		Symbols.drawSymbol(g2, symbol, sScale, point.getX(), point.getY(), null, null);
+	}
+	public static void symbol(Feature feature, Symbol symbol, Scheme scheme) {
+		Point2D point = context.getPoint(feature.centre);
+		Symbols.drawSymbol(g2, symbol, sScale, point.getX(), point.getY(), scheme, null);
+	}
+	public static void symbol(Feature feature, Symbol symbol, Delta delta) {
+		Point2D point = context.getPoint(feature.centre);
+		Symbols.drawSymbol(g2, symbol, sScale, point.getX(), point.getY(), null, delta);
+	}
+	public static void symbol(Feature feature, Symbol symbol, Scheme scheme, Delta delta) {
+		Point2D point = context.getPoint(feature.centre);
+		Symbols.drawSymbol(g2, symbol, sScale, point.getX(), point.getY(), scheme, delta);
+	}
+	
+	public static void cluster(Feature feature, ArrayList<Symbol> symbols) {
+		Rectangle2D.Double bbox = null;
+		if (symbols.size() > 4) {
+			for (Instr instr : symbols.get(0)) {
+				if (instr.type == Prim.BBOX) {
+					bbox = (Rectangle2D.Double) instr.params;
+					break;
+				}
+			}
+			if (bbox == null) return;
+		}
+		switch (symbols.size()) {
+		case 1:
+			symbol(feature, symbols.get(0), new Delta(Handle.CC, new AffineTransform()));
+			break;
+		case 2:
+			symbol(feature, symbols.get(0), new Delta(Handle.RC, new AffineTransform()));
+			symbol(feature, symbols.get(1), new Delta(Handle.LC, new AffineTransform()));
+			break;
+		case 3:
+			symbol(feature, symbols.get(0), new Delta(Handle.BC, new AffineTransform()));
+			symbol(feature, symbols.get(1), new Delta(Handle.TR, new AffineTransform()));
+			symbol(feature, symbols.get(2), new Delta(Handle.TL, new AffineTransform()));
+			break;
+		case 4:
+			symbol(feature, symbols.get(0), new Delta(Handle.BR, new AffineTransform()));
+			symbol(feature, symbols.get(1), new Delta(Handle.BL, new AffineTransform()));
+			symbol(feature, symbols.get(2), new Delta(Handle.TR, new AffineTransform()));
+			symbol(feature, symbols.get(3), new Delta(Handle.TL, new AffineTransform()));
+			break;
+		case 5:
+			symbol(feature, symbols.get(0), new Delta(Handle.BR, new AffineTransform()));
+			symbol(feature, symbols.get(1), new Delta(Handle.BL, new AffineTransform()));
+			symbol(feature, symbols.get(2), new Delta(Handle.TR, AffineTransform.getTranslateInstance(-bbox.width/2, 0)));
+			symbol(feature, symbols.get(3), new Delta(Handle.TC, new AffineTransform()));
+			symbol(feature, symbols.get(4), new Delta(Handle.TL, AffineTransform.getTranslateInstance(bbox.width/2, 0)));
+			break;
+		case 6:
+			symbol(feature, symbols.get(0), new Delta(Handle.BR, AffineTransform.getTranslateInstance(-bbox.width/2, 0)));
+			symbol(feature, symbols.get(1), new Delta(Handle.BC, new AffineTransform()));
+			symbol(feature, symbols.get(2), new Delta(Handle.BL, AffineTransform.getTranslateInstance(bbox.width/2, 0)));
+			symbol(feature, symbols.get(3), new Delta(Handle.TR, AffineTransform.getTranslateInstance(-bbox.width/2, 0)));
+			symbol(feature, symbols.get(4), new Delta(Handle.TC, new AffineTransform()));
+			symbol(feature, symbols.get(5), new Delta(Handle.TL, AffineTransform.getTranslateInstance(bbox.width/2, 0)));
+			break;
+		case 7:
+			symbol(feature, symbols.get(0), new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -bbox.height/2)));
+			symbol(feature, symbols.get(1), new Delta(Handle.RC, AffineTransform.getTranslateInstance(-bbox.width/2, 0)));
+			symbol(feature, symbols.get(2), new Delta(Handle.CC, new AffineTransform()));
+			symbol(feature, symbols.get(3), new Delta(Handle.LC, AffineTransform.getTranslateInstance(bbox.width/2, 0)));
+			symbol(feature, symbols.get(4), new Delta(Handle.TR, AffineTransform.getTranslateInstance(-bbox.width/2, bbox.height/2)));
+			symbol(feature, symbols.get(5), new Delta(Handle.TC, AffineTransform.getTranslateInstance(0, bbox.height/2)));
+			symbol(feature, symbols.get(6), new Delta(Handle.TL, AffineTransform.getTranslateInstance(bbox.width/2, bbox.height/2)));
+			break;
+		case 8:
+			symbol(feature, symbols.get(0), new Delta(Handle.BR, AffineTransform.getTranslateInstance(0, -bbox.height/2)));
+			symbol(feature, symbols.get(1), new Delta(Handle.BL, AffineTransform.getTranslateInstance(0, -bbox.height/2)));
+			symbol(feature, symbols.get(2), new Delta(Handle.RC, AffineTransform.getTranslateInstance(-bbox.width/2, 0)));
+			symbol(feature, symbols.get(3), new Delta(Handle.CC, new AffineTransform()));
+			symbol(feature, symbols.get(4), new Delta(Handle.LC, AffineTransform.getTranslateInstance(bbox.width/2, 0)));
+			symbol(feature, symbols.get(5), new Delta(Handle.TR, AffineTransform.getTranslateInstance(-bbox.width/2, bbox.height/2)));
+			symbol(feature, symbols.get(6), new Delta(Handle.TC, AffineTransform.getTranslateInstance(0, bbox.height/2)));
+			symbol(feature, symbols.get(7), new Delta(Handle.TL, AffineTransform.getTranslateInstance(bbox.width/2, bbox.height/2)));
+			break;
+		case 9:
+			symbol(feature, symbols.get(0), new Delta(Handle.BR, AffineTransform.getTranslateInstance(-bbox.width/2, -bbox.height/2)));
+			symbol(feature, symbols.get(1), new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -bbox.height/2)));
+			symbol(feature, symbols.get(2), new Delta(Handle.BL, AffineTransform.getTranslateInstance(bbox.width/2, -bbox.height/2)));
+			symbol(feature, symbols.get(3), new Delta(Handle.RC, AffineTransform.getTranslateInstance(-bbox.width/2, 0)));
+			symbol(feature, symbols.get(4), new Delta(Handle.CC, new AffineTransform()));
+			symbol(feature, symbols.get(5), new Delta(Handle.LC, AffineTransform.getTranslateInstance(bbox.width/2, 0)));
+			symbol(feature, symbols.get(6), new Delta(Handle.TR, AffineTransform.getTranslateInstance(-bbox.width/2, bbox.height/2)));
+			symbol(feature, symbols.get(7), new Delta(Handle.TC, AffineTransform.getTranslateInstance(0, bbox.height/2)));
+			symbol(feature, symbols.get(8), new Delta(Handle.TL, AffineTransform.getTranslateInstance(bbox.width/2, bbox.height/2)));
+			break;
+		}
+	}
+
+	private static Rectangle2D.Double symbolSize(Symbol symbol) {
+		Symbol ssymb = symbol;
+		while (ssymb != null) {
+			for (Instr item : symbol) {
+				if (item.type == Prim.BBOX) {
+					return (Rectangle2D.Double) item.params;
+				}
+				if (item.type == Prim.SYMB) {
+					ssymb = ((SubSymbol) item.params).instr;
+					break;
+				}
+			}
+			if (ssymb == symbol)
+				break;
+		}
+		return null;
+	}
+
+	public static void lineSymbols(Feature feature, Symbol prisymb, double space, Symbol secsymb, Symbol tersymb, int ratio, Color col) {
+		Area area;
+		switch (feature.flag) {
+		case LINE:
+			Edge edge = map.edges.get(feature.refs);
+			area = map.new Area();
+			area.add(map.new Bound(map.new Side(edge, true), true));
+			break;
+		case AREA:
+			area = map.areas.get(feature.refs);
+			break;
+		default:
+			return;
+		}
+		Rectangle2D.Double prect = symbolSize(prisymb);
+		Rectangle2D.Double srect = symbolSize(secsymb);
+		Rectangle2D.Double trect = symbolSize(tersymb);
+		if (srect == null)
+			ratio = 0;
+		if (prect != null) {
+			double psize = Math.abs(prect.getY()) * sScale;
+			double ssize = (srect != null) ? Math.abs(srect.getY()) * sScale : 0;
+			double tsize = (trect != null) ? Math.abs(srect.getY()) * sScale : 0;
+			Point2D prev = new Point2D.Double();
+			Point2D next = new Point2D.Double();
+			Point2D curr = new Point2D.Double();
+			Point2D succ = new Point2D.Double();
+			boolean gap = true;
+			boolean piv = false;
+			double len = 0;
+			double angle = 0;
+			int stcount = ratio;
+			boolean stflag = false;
+			Symbol symbol = prisymb;
+			for (Bound bound : area) {
+				BoundIterator bit = map.new BoundIterator(bound);
+				boolean first = true;
+				while (bit.hasNext()) {
+					prev = next;
+					next = context.getPoint(bit.next());
+					angle = Math.atan2(next.getY() - prev.getY(), next.getX() - prev.getX());
+					piv = true;
+					if (first) {
+						curr = succ = next;
+						gap = (space > 0);
+						stcount = ratio - 1;
+						symbol = prisymb;
+						len = gap ? psize * space * 0.5 : psize;
+						first = false;
+					} else {
+						while (curr.distance(next) >= len) {
+							if (piv) {
+								double rem = len;
+								double s = prev.distance(next);
+								double p = curr.distance(prev);
+								if ((s > 0) && (p > 0)) {
+									double n = curr.distance(next);
+									double theta = Math.acos((s * s + p * p - n * n) / 2 / s / p);
+									double phi = Math.asin(p / len * Math.sin(theta));
+									rem = len * Math.sin(Math.PI - theta - phi) / Math.sin(theta);
+								}
+								succ = new Point2D.Double(prev.getX() + (rem * Math.cos(angle)), prev.getY() + (rem * Math.sin(angle)));
+								piv = false;
+							} else {
+								succ = new Point2D.Double(curr.getX() + (len * Math.cos(angle)), curr.getY() + (len * Math.sin(angle)));
+							}
+							if (!gap) {
+								Symbols.drawSymbol(g2, symbol, sScale, curr.getX(), curr.getY(), new Scheme(col),
+										new Delta(Handle.BC, AffineTransform.getRotateInstance(Math.atan2((succ.getY() - curr.getY()), (succ.getX() - curr.getX())) + Math.toRadians(90))));
+							}
+							if (space > 0)
+								gap = !gap;
+							curr = succ;
+							len = gap ? (psize * space) : (--stcount == 0) ? (stflag ? tsize : ssize) : psize;
+							if (stcount == 0) {
+								symbol = stflag ? tersymb : secsymb;
+								if (trect != null) stflag = !stflag;
+								stcount = ratio;
+							} else {
+								symbol = prisymb;
+							}
+						}
+					}
+				}
+			}
+		}
+	}
+
+	public static void lineVector(Feature feature, LineStyle style) {
+		Path2D.Double p = new Path2D.Double();
+		p.setWindingRule(GeneralPath.WIND_EVEN_ODD);
+		Point2D point;
+		switch (feature.flag) {
+		case LINE:
+			EdgeIterator eit = map.new EdgeIterator(map.edges.get(feature.refs), true);
+			point = context.getPoint(eit.next());
+			p.moveTo(point.getX(), point.getY());
+			while (eit.hasNext()) {
+				point = context.getPoint(eit.next());
+				p.lineTo(point.getX(), point.getY());
+			}
+			break;
+		case AREA:
+			for (Bound bound : map.areas.get(feature.refs)) {
+				BoundIterator bit = map.new BoundIterator(bound);
+				point = context.getPoint(bit.next());
+				p.moveTo(point.getX(), point.getY());
+				while (bit.hasNext()) {
+					point = context.getPoint(bit.next());
+					p.lineTo(point.getX(), point.getY());
+				}
+			}
+			break;
+		}
+		if (style.line != null) {
+			if (style.dash != null) {
+				float[] dash = new float[style.dash.length];
+				System.arraycopy(style.dash, 0, dash, 0, style.dash.length);
+				for (int i = 0; i < style.dash.length; i++) {
+					dash[i] *= (float) sScale;
+				}
+				g2.setStroke(new BasicStroke((float) (style.width * sScale), BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND, 1, dash, 0));
+			} else {
+				g2.setStroke(new BasicStroke((float) (style.width * sScale), BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND));
+			}
+			g2.setPaint(style.line);
+			g2.draw(p);
+		}
+		if (style.fill != null) {
+			g2.setPaint(style.fill);
+			g2.fill(p);
+		}
+	}
+	
+	public static void lineCircle(Feature feature, LineStyle style, double radius, UniHLU units) {
+		switch (units) {
+		case HLU_FEET:
+			radius /= 6076;
+			break;
+		case HLU_KMTR:
+			radius /= 1.852;
+			break;
+		case HLU_HMTR:
+			radius /= 18.52;
+			break;
+		case HLU_SMIL:
+			radius /= 1.15078;
+			break;
+		case HLU_NMIL:
+			break;
+		default:
+			radius /= 1852;
+			break;
+		}
+		radius *= context.mile(feature);
+		Symbol circle = new Symbol();
+		if (style.fill != null) {
+			circle.add(new Instr(Prim.FILL, style.fill));
+			circle.add(new Instr(Prim.RSHP, new Ellipse2D.Double(-radius,-radius,radius*2,radius*2)));
+		}
+		circle.add(new Instr(Prim.FILL, style.line));
+		circle.add(new Instr(Prim.STRK, new BasicStroke(style.width, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 1, style.dash, 0)));
+		circle.add(new Instr(Prim.ELPS, new Ellipse2D.Double(-radius,-radius,radius*2,radius*2)));
+		Point2D point = context.getPoint(feature.centre);
+		Symbols.drawSymbol(g2, circle, 1, point.getX(), point.getY(), null, null);
+	}
+
+	
+	public static void fillPattern(Feature feature, BufferedImage image) {
+		Path2D.Double p = new Path2D.Double();
+		p.setWindingRule(GeneralPath.WIND_EVEN_ODD);
+		Point2D point;
+		switch (feature.flag) {
+		case POINT:
+			point = context.getPoint(feature.centre);
+			g2.drawImage(image, new AffineTransformOp(AffineTransform.getScaleInstance(sScale, sScale), AffineTransformOp.TYPE_NEAREST_NEIGHBOR),
+					(int)(point.getX() - (50 * sScale)), (int)(point.getY() - (50 * sScale)));
+			break;
+		case AREA:
+			for (Bound bound : map.areas.get(feature.refs)) {
+				BoundIterator bit = map.new BoundIterator(bound);
+				point = context.getPoint(bit.next());
+				p.moveTo(point.getX(), point.getY());
+				while (bit.hasNext()) {
+					point = context.getPoint(bit.next());
+					p.lineTo(point.getX(), point.getY());
+				}
+			}
+	    g2.setPaint(new TexturePaint(image, new Rectangle(0, 0, 1 + (int)(100 * sScale), 1 + (int)(100 * sScale))));
+	    g2.fill(p);
+	    break;
+		}
+	}
+
+	public static void labelText(Feature feature, String str, Font font, LabelStyle style, Color fg) {
+		labelText(feature, str, font, style, fg, null, null);
+	}
+	public static void labelText(Feature feature, String str, Font font, LabelStyle style, Color fg, Color bg) {
+		labelText(feature, str, font, style, fg, bg, null);
+	}
+	public static void labelText(Feature feature, String str, Font font, LabelStyle style, Color fg, Delta delta) {
+		labelText(feature, str, font, style, fg, null, delta);
+	}
+	public static void labelText(Feature feature, String str, Font font, LabelStyle style, Color fg, Color bg, Delta delta) {
+		if (delta == null) delta = new Delta(Handle.CC);
+		if (bg == null) bg = new Color(0x00000000, true);
+		if ((str == null) || (str.isEmpty())) str = " ";
+    FontRenderContext frc = g2.getFontRenderContext();
+    GlyphVector gv = font.deriveFont((float)(font.getSize())).createGlyphVector(frc, str.equals(" ") ? "!" : str);
+    Rectangle2D bounds = gv.getVisualBounds();
+    double width = bounds.getWidth();
+    double height = bounds.getHeight();
+		Symbol label = new Symbol();
+		double lx, ly, tx, ty;
+		switch (style) {
+		case RRCT:
+			width += height * 1.0;
+			height *= 1.5;
+	    if (width < height) width = height;
+	    lx = -width / 2;
+	    ly = -height / 2;
+	    tx = lx + (height * 0.34);
+	    ty = ly + (height * 0.17);
+			label.add(new Instr(Prim.BBOX, new Rectangle2D.Double(lx,ly,width,height)));
+			label.add(new Instr(Prim.FILL, bg));
+			label.add(new Instr(Prim.RSHP, new RoundRectangle2D.Double(lx,ly,width,height,height,height)));
+			label.add(new Instr(Prim.FILL, fg));
+			label.add(new Instr(Prim.STRK, new BasicStroke(1 + (int)(height/10), BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER)));
+			label.add(new Instr(Prim.RRCT, new RoundRectangle2D.Double(lx,ly,width,height,height,height)));
+			break;
+		case VCLR:
+			width += height * 1.0;
+			height *= 2.0;
+	    if (width < height) width = height;
+	    lx = -width / 2;
+	    ly = -height / 2;
+	    tx = lx + (height * 0.27);
+	    ty = ly + (height * 0.25);
+			label.add(new Instr(Prim.BBOX, new Rectangle2D.Double(lx,ly,width,height)));
+			label.add(new Instr(Prim.FILL, bg));
+			label.add(new Instr(Prim.RSHP, new RoundRectangle2D.Double(lx,ly,width,height,height,height)));
+			label.add(new Instr(Prim.FILL, fg));
+			int sw = 1 + (int)(height/10);
+			double po = sw / 2;
+			label.add(new Instr(Prim.STRK, new BasicStroke(sw, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER)));
+			Path2D.Double p = new Path2D.Double(); p.moveTo(-height*0.2,-ly-po); p.lineTo(height*0.2,-ly-po); p.moveTo(0,-ly-po); p.lineTo(0,-ly-po-(height*0.15));
+			p.moveTo(-height*0.2,ly+po); p.lineTo((height*0.2),ly+po); p.moveTo(0,ly+po); p.lineTo(0,ly+po+(height*0.15));
+			label.add(new Instr(Prim.PLIN, p));
+			break;
+		case PCLR:
+			width += height * 1.0;
+			height *= 2.0;
+	    if (width < height) width = height;
+	    lx = -width / 2;
+	    ly = -height / 2;
+	    tx = lx + (height * 0.27);
+	    ty = ly + (height * 0.25);
+			label.add(new Instr(Prim.BBOX, new Rectangle2D.Double(lx,ly,width,height)));
+			label.add(new Instr(Prim.FILL, bg));
+			label.add(new Instr(Prim.RSHP, new RoundRectangle2D.Double(lx,ly,width,height,height,height)));
+			label.add(new Instr(Prim.FILL, fg));
+			sw = 1 + (int)(height/10);
+			po = sw / 2;
+			label.add(new Instr(Prim.STRK, new BasicStroke(sw, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER)));
+			p = new Path2D.Double(); p.moveTo(-height*0.2,-ly-po); p.lineTo(height*0.2,-ly-po); p.moveTo(0,-ly-po); p.lineTo(0,-ly-po-(height*0.15));
+			p.moveTo(-height*0.2,ly+po); p.lineTo((height*0.2),ly+po); p.moveTo(0,ly+po); p.lineTo(0,ly+po+(height*0.15));
+			label.add(new Instr(Prim.PLIN, p));
+			label.add(new Instr(Prim.SYMB, new Symbols.SubSymbol(Areas.CableFlash, 1, 0, 0, null, new Delta(Handle.CC, new AffineTransform(0,-1,1,0,-width/2,0)))));
+			label.add(new Instr(Prim.SYMB, new Symbols.SubSymbol(Areas.CableFlash, 1, 0, 0, null, new Delta(Handle.CC, new AffineTransform(0,-1,1,0,width/2,0)))));
+			break;
+		case HCLR:
+			width += height * 1.5;
+			height *= 1.5;
+	    if (width < height) width = height;
+	    lx = -width / 2;
+	    ly = -height / 2;
+	    tx = lx + (height * 0.5);
+	    ty = ly + (height * 0.17);
+			label.add(new Instr(Prim.BBOX, new Rectangle2D.Double(lx,ly,width,height)));
+			label.add(new Instr(Prim.FILL, bg));
+			label.add(new Instr(Prim.RSHP, new RoundRectangle2D.Double(lx,ly,width,height,height,height)));
+			label.add(new Instr(Prim.FILL, fg));
+			sw = 1 + (int)(height/10);
+			double vo = height / 4;
+			label.add(new Instr(Prim.STRK, new BasicStroke(sw, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER)));
+			p = new Path2D.Double(); p.moveTo(-width*0.4-sw,-ly-vo); p.lineTo(-width*0.4-sw,ly+vo); p.moveTo(-width*0.4-sw,0); p.lineTo(-width*0.4+sw,0);
+			p.moveTo(width*0.4+sw,-ly-vo); p.lineTo(width*0.4+sw,ly+vo); p.moveTo(width*0.4-sw,0); p.lineTo(width*0.4+sw,0);
+			label.add(new Instr(Prim.PLIN, p));
+			break;
+		default:
+			lx = -width / 2;
+			ly = -height / 2;
+			tx = lx;
+			ty = ly;
+			label.add(new Instr(Prim.BBOX, new Rectangle2D.Double(lx,ly,width,height)));
+			break;
+		}
+		label.add(new Instr(Prim.TEXT, new Caption(str, font, fg, new Delta(Handle.TL, AffineTransform.getTranslateInstance(tx, ty)))));
+		Point2D point = context.getPoint(feature.centre);
+		Symbols.drawSymbol(g2, label, sScale, point.getX(), point.getY(), null, delta);
+	}
+
+	public static void lineText(Feature feature, String str, Font font, Color colour, double offset, double dy) {
+		Area area;
+		switch (feature.flag) {
+		case LINE:
+			Edge edge = map.edges.get(feature.refs);
+			area = map.new Area();
+			area.add(map.new Bound(map.new Side(edge, true), true));
+			break;
+		case AREA:
+			area = map.areas.get(feature.refs);
+			break;
+		default:
+			return;
+		}
+//		Rectangle prect = symbolSize(prisymb);
+		if (!str.isEmpty()) {
+			g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+	    FontRenderContext frc = g2.getFontRenderContext();
+	    GlyphVector gv = font.deriveFont((float)(font.getSize()*sScale)).createGlyphVector(frc, str);
+//			double psize = Math.abs(prect.getX());
+			Point2D prev = new Point2D.Double();
+			Point2D next = new Point2D.Double();
+			Point2D curr = new Point2D.Double();
+			Point2D succ = new Point2D.Double();
+			boolean piv = false;
+			double len = 0;
+			double angle = 0;
+			for (Bound bound : area) {
+				BoundIterator bit = map.new BoundIterator(bound);
+				boolean first = true;
+				while (bit.hasNext()) {
+					prev = next;
+					next = context.getPoint(bit.next());
+					angle = Math.atan2(next.getY() - prev.getY(), next.getX() - prev.getX());
+					piv = true;
+					if (first) {
+						curr = succ = next;
+//						len = psize;
+						first = false;
+					} else {
+						while (curr.distance(next) >= len) {
+							if (piv) {
+								double rem = len;
+								double s = prev.distance(next);
+								double p = curr.distance(prev);
+								if ((s > 0) && (p > 0)) {
+									double n = curr.distance(next);
+									double theta = Math.acos((s * s + p * p - n * n) / 2 / s / p);
+									double phi = Math.asin(p / len * Math.sin(theta));
+									rem = len * Math.sin(Math.PI - theta - phi) / Math.sin(theta);
+								}
+								succ = new Point2D.Double(prev.getX() + (rem * Math.cos(angle)), prev.getY() + (rem * Math.sin(angle)));
+								piv = false;
+							} else {
+								succ = new Point2D.Double(curr.getX() + (len * Math.cos(angle)), curr.getY() + (len * Math.sin(angle)));
+							}
+//								Symbols.drawSymbol(g2, symbol, sScale, curr.getX(), curr.getY(), new Delta(Handle.BC, AffineTransform.getRotateInstance(Math.atan2((succ.getY() - curr.getY()), (succ.getX() - curr.getX())) + Math.toRadians(90))), null);
+							curr = succ;
+//							len = psize;
+						}
+					}
+				}
+			}
+		}
+	}
+}
Index: applications/editors/josm/plugins/smed2/src/render/Rules.java
===================================================================
--- applications/editors/josm/plugins/smed2/src/render/Rules.java	(revision 30150)
+++ applications/editors/josm/plugins/smed2/src/render/Rules.java	(revision 30150)
@@ -0,0 +1,1050 @@
+/* Copyright 2013 Malcolm Herring
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3 of the License.
+ *
+ * For a copy of the GNU General Public License, see <http://www.gnu.org/licenses/>.
+ */
+
+package render;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.geom.AffineTransform;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import s57.S57val;
+import s57.S57val.*;
+import s57.S57att.*;
+import s57.S57obj.*;
+import s57.S57map.*;
+import render.Renderer.*;
+import symbols.*;
+import symbols.Symbols.*;
+
+public class Rules {
+
+	public static final Color Yland = new Color(0x50b0ff);
+	public static final Color Mline = new Color(0xc480ff);
+	public static final Color Msymb = new Color(0xa30075);
+	
+	static String getName(Feature feature) {
+		AttItem name = feature.atts.get(Att.OBJNAM);
+		if (name == null) {
+			AttMap atts = feature.objs.get(feature.type).get(0);
+			if (atts != null) {
+				name = atts.get(Att.OBJNAM);
+			}
+		}
+		return (name != null) ? (String)name.val: null;
+	}
+
+	static AttMap getAtts(Feature feature, Obj obj, int idx) {
+		HashMap<Integer, AttMap> objs = feature.objs.get(obj);
+		if (objs == null)
+			return null;
+		else
+			return objs.get(idx);
+	}
+
+	public static Object getAttVal(Feature feature, Obj obj, int idx, Att att) {
+		AttMap atts = null;
+		HashMap<Integer, AttMap> objs = feature.objs.get(obj);
+		if (objs != null)
+			atts = objs.get(idx);
+		if (atts == null)
+			return S57val.nullVal(att);
+		else {
+			AttItem item = atts.get(att);
+			if (item == null)
+				return S57val.nullVal(att);
+			return item.val;
+		}
+	}
+	
+	static Scheme getScheme(Feature feature, Obj obj) {
+		ArrayList<Color> colours = new ArrayList<Color>();
+		for (ColCOL col : (ArrayList<ColCOL>)getAttVal(feature, obj, 0, Att.COLOUR)) {
+			colours.add(Renderer.bodyColours.get(col));
+		}
+		ArrayList<Patt> patterns = new ArrayList<Patt>();
+		for(ColPAT pat: (ArrayList<ColPAT>) getAttVal(feature, obj, 0, Att.COLPAT)) {
+			patterns.add(Renderer.pattMap.get(pat));
+		}
+		return new Scheme(patterns, colours);
+	}
+	
+	static boolean hasObject(Feature feature, Obj obj) {
+		return (feature.objs.containsKey(obj));
+	}
+	
+	static boolean hasAttribute(Feature feature, Obj obj, Att att) {
+		AttMap atts = getAtts(feature, obj, 0);
+		return ((atts != null) && (atts.containsKey(att)));
+	}
+	
+	static boolean testAttribute(Feature feature, Obj obj, Att att, Object val) {
+		AttMap atts = getAtts(feature, obj, 0);
+		if (atts != null) {
+			AttItem item = atts.get(att);
+			if (item != null) {
+				switch (item.conv) {
+				case S:
+				case A:
+					return ((String)item.val).equals(val);
+				case L:
+					return ((ArrayList<?>)item.val).contains(val);
+				case E:
+				case F:
+				case I:
+					return item.val.equals(val);
+				}
+			}
+		}
+		return false;
+	}
+	
+	public static void rules () {
+		ArrayList<Feature> objects;
+		if ((objects = Renderer.map.features.get(Obj.SLCONS)) != null) for (Feature feature : objects) shoreline(feature);
+		if ((objects = Renderer.map.features.get(Obj.PIPSOL)) != null) for (Feature feature : objects) pipelines(feature);
+		if ((objects = Renderer.map.features.get(Obj.CBLSUB)) != null) for (Feature feature : objects) cables(feature);
+		if ((objects = Renderer.map.features.get(Obj.PIPOHD)) != null) for (Feature feature : objects) pipelines(feature);
+		if ((objects = Renderer.map.features.get(Obj.CBLOHD)) != null) for (Feature feature : objects) cables(feature);
+		if ((objects = Renderer.map.features.get(Obj.TSEZNE)) != null) for (Feature feature : objects) separation(feature);
+		if ((objects = Renderer.map.features.get(Obj.TSSCRS)) != null) for (Feature feature : objects) separation(feature);
+		if ((objects = Renderer.map.features.get(Obj.TSSRON)) != null) for (Feature feature : objects) separation(feature);
+		if ((objects = Renderer.map.features.get(Obj.TSELNE)) != null) for (Feature feature : objects) separation(feature);
+		if ((objects = Renderer.map.features.get(Obj.TSSLPT)) != null) for (Feature feature : objects) separation(feature);
+		if ((objects = Renderer.map.features.get(Obj.TSSBND)) != null) for (Feature feature : objects) separation(feature);
+		if ((objects = Renderer.map.features.get(Obj.ISTZNE)) != null) for (Feature feature : objects) separation(feature);
+		if ((objects = Renderer.map.features.get(Obj.SNDWAV)) != null) for (Feature feature : objects) areas(feature);
+		if ((objects = Renderer.map.features.get(Obj.OSPARE)) != null) for (Feature feature : objects) areas(feature);
+		if ((objects = Renderer.map.features.get(Obj.FAIRWY)) != null) for (Feature feature : objects) areas(feature);
+		if ((objects = Renderer.map.features.get(Obj.DRGARE)) != null) for (Feature feature : objects) areas(feature);
+		if ((objects = Renderer.map.features.get(Obj.RESARE)) != null) for (Feature feature : objects) areas(feature);
+		if ((objects = Renderer.map.features.get(Obj.SPLARE)) != null) for (Feature feature : objects) areas(feature);
+		if ((objects = Renderer.map.features.get(Obj.SEAARE)) != null) for (Feature feature : objects) areas(feature);
+		if ((objects = Renderer.map.features.get(Obj.OBSTRN)) != null) for (Feature feature : objects) obstructions(feature);
+		if ((objects = Renderer.map.features.get(Obj.UWTROC)) != null) for (Feature feature : objects) obstructions(feature);
+		if ((objects = Renderer.map.features.get(Obj.MARCUL)) != null) for (Feature feature : objects) areas(feature);
+		if ((objects = Renderer.map.features.get(Obj.WTWAXS)) != null) for (Feature feature : objects) waterways(feature);
+		if ((objects = Renderer.map.features.get(Obj.RECTRC)) != null) for (Feature feature : objects) transits(feature);
+		if ((objects = Renderer.map.features.get(Obj.NAVLNE)) != null) for (Feature feature : objects) transits(feature);
+		if ((objects = Renderer.map.features.get(Obj.HRBFAC)) != null) for (Feature feature : objects) harbours(feature);
+		if ((objects = Renderer.map.features.get(Obj.ACHARE)) != null) for (Feature feature : objects) harbours(feature);
+		if ((objects = Renderer.map.features.get(Obj.ACHBRT)) != null) for (Feature feature : objects) harbours(feature);
+		if ((objects = Renderer.map.features.get(Obj.BERTHS)) != null) for (Feature feature : objects) harbours(feature);
+		if ((objects = Renderer.map.features.get(Obj.LOKBSN)) != null) for (Feature feature : objects) locks(feature);
+		if ((objects = Renderer.map.features.get(Obj.LKBSPT)) != null) for (Feature feature : objects) locks(feature);
+		if ((objects = Renderer.map.features.get(Obj.GATCON)) != null) for (Feature feature : objects) locks(feature);
+		if ((objects = Renderer.map.features.get(Obj.DISMAR)) != null) for (Feature feature : objects) distances(feature);
+		if ((objects = Renderer.map.features.get(Obj.HULKES)) != null) for (Feature feature : objects) ports(feature);
+		if ((objects = Renderer.map.features.get(Obj.CRANES)) != null) for (Feature feature : objects) ports(feature);
+		if ((objects = Renderer.map.features.get(Obj.LNDMRK)) != null) for (Feature feature : objects) landmarks(feature);
+		if ((objects = Renderer.map.features.get(Obj.BUISGL)) != null) for (Feature feature : objects) harbours(feature);
+		if ((objects = Renderer.map.features.get(Obj.MORFAC)) != null) for (Feature feature : objects) moorings(feature);
+		if ((objects = Renderer.map.features.get(Obj.NOTMRK)) != null) for (Feature feature : objects) notices(feature);
+		if ((objects = Renderer.map.features.get(Obj.SMCFAC)) != null) for (Feature feature : objects) marinas(feature);
+		if ((objects = Renderer.map.features.get(Obj.BRIDGE)) != null) for (Feature feature : objects) bridges(feature);
+		if ((objects = Renderer.map.features.get(Obj.PILPNT)) != null) for (Feature feature : objects) lights(feature);
+		if ((objects = Renderer.map.features.get(Obj.LITMIN)) != null) for (Feature feature : objects) lights(feature);
+		if ((objects = Renderer.map.features.get(Obj.LITMAJ)) != null) for (Feature feature : objects) lights(feature);
+		if ((objects = Renderer.map.features.get(Obj.LIGHTS)) != null) for (Feature feature : objects) lights(feature);
+		if ((objects = Renderer.map.features.get(Obj.SISTAT)) != null) for (Feature feature : objects) stations(feature);
+		if ((objects = Renderer.map.features.get(Obj.SISTAW)) != null) for (Feature feature : objects) stations(feature);
+		if ((objects = Renderer.map.features.get(Obj.CGUSTA)) != null) for (Feature feature : objects) stations(feature);
+		if ((objects = Renderer.map.features.get(Obj.RDOSTA)) != null) for (Feature feature : objects) stations(feature);
+		if ((objects = Renderer.map.features.get(Obj.RADSTA)) != null) for (Feature feature : objects) stations(feature);
+		if ((objects = Renderer.map.features.get(Obj.RSCSTA)) != null) for (Feature feature : objects) stations(feature);
+		if ((objects = Renderer.map.features.get(Obj.PILBOP)) != null) for (Feature feature : objects) stations(feature);
+		if ((objects = Renderer.map.features.get(Obj.WTWGAG)) != null) for (Feature feature : objects) gauges(feature);
+		if ((objects = Renderer.map.features.get(Obj.OFSPLF)) != null) for (Feature feature : objects) platforms(feature);
+		if ((objects = Renderer.map.features.get(Obj.WRECKS)) != null) for (Feature feature : objects) wrecks(feature);
+		if ((objects = Renderer.map.features.get(Obj.LITVES)) != null) for (Feature feature : objects) floats(feature);
+		if ((objects = Renderer.map.features.get(Obj.LITFLT)) != null) for (Feature feature : objects) floats(feature);
+		if ((objects = Renderer.map.features.get(Obj.BOYINB)) != null) for (Feature feature : objects) floats(feature);
+		if ((objects = Renderer.map.features.get(Obj.BOYLAT)) != null) for (Feature feature : objects) buoys(feature);
+		if ((objects = Renderer.map.features.get(Obj.BOYCAR)) != null) for (Feature feature : objects) buoys(feature);
+		if ((objects = Renderer.map.features.get(Obj.BOYISD)) != null) for (Feature feature : objects) buoys(feature);
+		if ((objects = Renderer.map.features.get(Obj.BOYSAW)) != null) for (Feature feature : objects) buoys(feature);
+		if ((objects = Renderer.map.features.get(Obj.BOYSPP)) != null) for (Feature feature : objects) buoys(feature);
+		if ((objects = Renderer.map.features.get(Obj.BOYWTW)) != null) for (Feature feature : objects) buoys(feature);
+		if ((objects = Renderer.map.features.get(Obj.BCNLAT)) != null) for (Feature feature : objects) beacons(feature);
+		if ((objects = Renderer.map.features.get(Obj.BCNCAR)) != null) for (Feature feature : objects) beacons(feature);
+		if ((objects = Renderer.map.features.get(Obj.BCNISD)) != null) for (Feature feature : objects) beacons(feature);
+		if ((objects = Renderer.map.features.get(Obj.BCNSAW)) != null) for (Feature feature : objects) beacons(feature);
+		if ((objects = Renderer.map.features.get(Obj.BCNSPP)) != null) for (Feature feature : objects) beacons(feature);
+		if ((objects = Renderer.map.features.get(Obj.BCNWTW)) != null) for (Feature feature : objects) beacons(feature);
+	}
+	
+	private static void areas(Feature feature) {
+		String name = getName(feature);
+		switch (feature.type) {
+		case DRGARE:
+			if (Renderer.zoom < 16)
+				Renderer.lineVector(feature, new LineStyle(Color.black, 8, new float[] { 25, 25 }, new Color(0x40ffffff, true)));
+			else
+				Renderer.lineVector(feature, new LineStyle(Color.black, 8, new float[] { 25, 25 }));
+			if ((Renderer.zoom >= 12) && (name != null))
+				Renderer.labelText(feature, name, new Font("Arial", Font.PLAIN, 100), LabelStyle.NONE, Color.black);
+			break;
+		case FAIRWY:
+			if (feature.area > 2.0) {
+				if (Renderer.zoom < 16)
+					Renderer.lineVector(feature, new LineStyle(Mline, 8, new float[] { 50, 50 }, new Color(0x40ffffff, true)));
+				else
+					Renderer.lineVector(feature, new LineStyle(Mline, 8, new float[] { 50, 50 }));
+			} else {
+				if (Renderer.zoom >= 14)
+					Renderer.lineVector(feature, new LineStyle(null, 0, new Color(0x40ffffff, true)));
+			}
+			break;
+		case MARCUL:
+			if (Renderer.zoom >= 12) {
+				if (Renderer.zoom >= 14) {
+					Renderer.symbol(feature, Areas.MarineFarm);
+				}
+				if ((feature.area > 0.2) || ((feature.area > 0.05) && (Renderer.zoom >= 14)) || ((feature.area > 0.005) && (Renderer.zoom >= 16))) {
+					Renderer.lineVector(feature, new LineStyle(Color.black, 4, new float[] { 10, 10 }));
+				}
+			}
+			break;
+		case OSPARE:
+			if (testAttribute(feature, feature.type, Att.CATPRA, CatPRA.PRA_WFRM)) {
+				Renderer.symbol(feature, Areas.WindFarm);
+				Renderer.lineVector(feature, new LineStyle(Color.black, 20, new float[] { 40, 40 }));
+				if ((Renderer.zoom >= 15) && (name != null))
+					Renderer.labelText(feature, name, new Font("Arial", Font.BOLD, 80), LabelStyle.NONE, Color.black, new Delta(Handle.TC, AffineTransform.getTranslateInstance(0, 10)));
+			}
+			break;
+		case RESARE:
+			if (Renderer.zoom >= 12) {
+				Renderer.lineSymbols(feature, Areas.Restricted, 1.0, null, null, 0, Mline);
+				if (testAttribute(feature, feature.type, Att.CATREA, CatREA.REA_NWAK)) {
+					Renderer.symbol(feature, Areas.NoWake);
+				}
+			}
+			break;
+		case SEAARE:
+			switch ((CatSEA) getAttVal(feature, feature.type, 0, Att.CATSEA)) {
+			case SEA_RECH:
+				if ((Renderer.zoom >= 10) && (name != null))
+					if (feature.flag == Fflag.LINE) {
+						Renderer.lineText(feature, name, new Font("Arial", Font.PLAIN, 150), Color.black, 0.5, -40);
+					} else {
+						Renderer.labelText(feature, name, new Font("Arial", Font.PLAIN, 150), LabelStyle.NONE, Color.black, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -40)));
+					}
+				break;
+			case SEA_BAY:
+				if ((Renderer.zoom >= 12) && (name != null))
+					if (feature.flag == Fflag.LINE) {
+						Renderer.lineText(feature, name, new Font("Arial", Font.PLAIN, 150), Color.black, 0.5, -40);
+					} else {
+						Renderer.labelText(feature, name, new Font("Arial", Font.PLAIN, 150), LabelStyle.NONE, Color.black, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -40)));
+					}
+				break;
+			case SEA_SHOL:
+				if (Renderer.zoom >= 14) {
+					if (feature.flag == Fflag.AREA) {
+						Renderer.lineVector(feature, new LineStyle(new Color(0xc480ff), 4, new float[] { 25, 25 }));
+						if (name != null) {
+							Renderer.labelText(feature, name, new Font("Arial", Font.ITALIC, 75), LabelStyle.NONE, Color.black, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -40)));
+							Renderer.labelText(feature, "(Shoal)", new Font("Arial", Font.PLAIN, 60), LabelStyle.NONE, Color.black, new Delta(Handle.BC));
+						}
+					} else if (feature.flag == Fflag.LINE) {
+						if (name != null) {
+							Renderer.lineText(feature, name, new Font("Arial", Font.ITALIC, 75), Color.black, 0.5, -40);
+							Renderer.lineText(feature, "(Shoal)", new Font("Arial", Font.PLAIN, 60), Color.black, 0.5, 0);
+						}
+					} else {
+						if (name != null) {
+							Renderer.labelText(feature, name, new Font("Arial", Font.ITALIC, 75), LabelStyle.NONE, Color.black, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -40)));
+							Renderer.labelText(feature, "(Shoal)", new Font("Arial", Font.PLAIN, 60), LabelStyle.NONE, Color.black, new Delta(Handle.BC));
+						}
+					}
+				}
+				break;
+			case SEA_GAT:
+			case SEA_NRRW:
+				if ((Renderer.zoom >= 12) && (name != null))
+					Renderer.labelText(feature, name, new Font("Arial", Font.PLAIN, 100), LabelStyle.NONE, Color.black);
+				break;
+			default:
+				break;
+			}
+			break;
+		case SNDWAV:
+			if (Renderer.zoom >= 12) Renderer.fillPattern(feature, Areas.Sandwaves);
+			break;
+		case SPLARE:
+			if (Renderer.zoom >= 12) {
+				Renderer.symbol(feature, Areas.Plane, new Scheme(Msymb));
+				Renderer.lineSymbols(feature, Areas.Restricted, 0.5, Areas.LinePlane, null, 10, Mline);
+			}
+			if ((Renderer.zoom >= 15) && (name != null))
+				Renderer.labelText(feature, name, new Font("Arial", Font.BOLD, 80), LabelStyle.NONE, Color.black, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -90)));
+			break;
+		default:
+			break;
+		}
+	}
+	
+	private static void beacons(Feature feature) {
+		BcnSHP shape = (BcnSHP) getAttVal(feature, feature.type, 0, Att.BCNSHP);
+		if (((shape == BcnSHP.BCN_PRCH) || (shape == BcnSHP.BCN_WTHY)) && (feature.type == Obj.BCNLAT)) {
+			CatLAM cat = (CatLAM) getAttVal(feature, feature.type, 0, Att.CATLAM);
+			switch (cat) {
+			case LAM_PORT:
+				if (shape == BcnSHP.BCN_PRCH)
+					Renderer.symbol(feature, Beacons.PerchPort);
+				else
+					Renderer.symbol(feature, Beacons.WithyPort);
+				break;
+			case LAM_STBD:
+				if (shape == BcnSHP.BCN_PRCH)
+					Renderer.symbol(feature, Beacons.PerchStarboard);
+				else
+					Renderer.symbol(feature, Beacons.WithyStarboard);
+				break;
+			default:
+				Renderer.symbol(feature, Beacons.Stake, getScheme(feature, feature.type));
+			}
+		} else {
+			Renderer.symbol(feature, Beacons.Shapes.get(shape), getScheme(feature, feature.type));
+			if (feature.objs.get(Obj.TOPMAR) != null)
+				Renderer.symbol(feature, Topmarks.Shapes.get(feature.objs.get(Obj.TOPMAR).get(0).get(Att.TOPSHP).val), getScheme(feature, Obj.TOPMAR), Topmarks.BeaconDelta);
+		}
+		Signals.addSignals(feature);
+	}
+	
+	private static void buoys(Feature feature) {
+		BoySHP shape = (BoySHP) getAttVal(feature, feature.type, 0, Att.BOYSHP);
+		Renderer.symbol(feature, Buoys.Shapes.get(shape), getScheme(feature, feature.type));
+		if (hasObject(feature, Obj.TOPMAR)) {
+			Renderer.symbol(feature, Topmarks.Shapes.get(feature.objs.get(Obj.TOPMAR).get(0).get(Att.TOPSHP).val), getScheme(feature, Obj.TOPMAR), Topmarks.BuoyDeltas.get(shape));
+		}
+		Signals.addSignals(feature);
+	}
+	
+	private static void bridges(Feature feature) {
+		if (Renderer.zoom >= 16) {
+			double verclr, verccl, vercop, horclr;
+			AttMap atts = feature.objs.get(Obj.BRIDGE).get(0);
+			String vstr = "";
+			String hstr = "";
+			if (atts != null) {
+				if (atts.containsKey(Att.HORCLR)) {
+					horclr = (Double) atts.get(Att.HORCLR).val;
+					hstr = String.valueOf(horclr);
+				}
+					if (atts.containsKey(Att.VERCLR)) {
+						verclr = (Double) atts.get(Att.VERCLR).val;
+				} else {
+					verclr = atts.containsKey(Att.VERCSA) ? (Double) atts.get(Att.VERCSA).val : 0;
+				}
+				verccl = atts.containsKey(Att.VERCCL) ? (Double) atts.get(Att.VERCCL).val : 0;
+				vercop = atts.containsKey(Att.VERCOP) ? (Double) atts.get(Att.VERCOP).val : 0;
+				if (verclr > 0) {
+					vstr += String.valueOf(verclr);
+				} else if (verccl > 0) {
+					if (vercop == 0) {
+						vstr += String.valueOf(verccl) + "/-";
+					} else {
+						vstr += String.valueOf(verccl) + "/" + String.valueOf(vercop);
+					}
+				}
+				if (hstr.isEmpty() && !vstr.isEmpty()) {
+					Renderer.labelText(feature, vstr, new Font("Arial", Font.PLAIN, 30), LabelStyle.VCLR, Color.black, Color.white, new Delta(Handle.CC));
+				} else if (!hstr.isEmpty() && !vstr.isEmpty()) {
+					Renderer.labelText(feature, vstr, new Font("Arial", Font.PLAIN, 30), LabelStyle.VCLR, Color.black, Color.white, new Delta(Handle.BC));
+					Renderer.labelText(feature, hstr, new Font("Arial", Font.PLAIN, 30), LabelStyle.HCLR, Color.black, Color.white, new Delta(Handle.TC));
+				} else if (!hstr.isEmpty() && vstr.isEmpty()) {
+					Renderer.labelText(feature, hstr, new Font("Arial", Font.PLAIN, 30), LabelStyle.HCLR, Color.black, Color.white, new Delta(Handle.CC));
+				}
+			}
+		}
+	}
+	
+	private static void cables(Feature feature) {
+		if ((Renderer.zoom >= 16) && (feature.length < 2)) {
+			if (feature.type == Obj.CBLSUB) {
+				Renderer.lineSymbols(feature, Areas.Cable, 0.0, null, null, 0, Mline);
+			} else if (feature.type == Obj.CBLOHD) {
+				AttMap atts = feature.objs.get(Obj.CBLOHD).get(0);
+				if ((atts != null) && (atts.containsKey(Att.CATCBL)) && (atts.get(Att.CATCBL).val == CatCBL.CBL_POWR)) {
+					Renderer.lineSymbols(feature, Areas.CableDash, 0, Areas.CableDot, Areas.CableFlash, 2, Color.black);
+				} else {
+					Renderer.lineSymbols(feature, Areas.CableDash, 0, Areas.CableDot, null, 2, Color.black);
+				}
+				if (atts != null) {
+					if (atts.containsKey(Att.VERCLR)) {
+						Renderer.labelText(feature, String.valueOf((Double) atts.get(Att.VERCLR).val), new Font("Arial", Font.PLAIN, 50), LabelStyle.VCLR, Color.black, new Delta(Handle.TC, AffineTransform.getTranslateInstance(0,25)));
+					} else if (atts.containsKey(Att.VERCSA)) {
+						Renderer.labelText(feature, String.valueOf((Double) atts.get(Att.VERCSA).val), new Font("Arial", Font.PLAIN, 50), LabelStyle.PCLR, Color.black, new Delta(Handle.TC, AffineTransform.getTranslateInstance(0,25)));
+					}
+				}
+			}
+		}
+	}
+	
+	private static void distances(Feature feature) {
+		if (Renderer.zoom >= 14) {
+			if (!testAttribute(feature, Obj.DISMAR, Att.CATDIS, CatDIS.DIS_NONI)) {
+				Renderer.symbol(feature, Harbours.DistanceI);
+			} else {
+				Renderer.symbol(feature, Harbours.DistanceU);
+			}
+			if ((Renderer.zoom >=15) && hasAttribute(feature, Obj.DISMAR, Att.WTWDIS)) {
+				AttMap atts = feature.objs.get(Obj.DISMAR).get(0);
+				Double dist = (Double) atts.get(Att.WTWDIS).val;
+				String str = "";
+				if (atts.containsKey(Att.HUNITS)) {
+					switch ((UniHLU) atts.get(Att.HUNITS).val) {
+					case HLU_METR:
+						str += "m ";
+						break;
+					case HLU_FEET:
+						str += "ft ";
+						break;
+					case HLU_HMTR:
+						str += "hm ";
+						break;
+					case HLU_KMTR:
+						str += "km ";
+						break;
+					case HLU_SMIL:
+						str += "M ";
+						break;
+					case HLU_NMIL:
+						str += "NM ";
+						break;
+					default:
+						break;
+					}
+				}
+				str += String.format("%1.0f", dist);
+				Renderer.labelText(feature, str, new Font("Arial", Font.PLAIN, 40), LabelStyle.NONE, Color.black, null, new Delta(Handle.CC, AffineTransform.getTranslateInstance(0, 45)));
+			}
+		}
+	}
+	
+	private static void floats(Feature feature) {
+		switch (feature.type) {
+		case LITVES:
+			Renderer.symbol(feature, Buoys.Super, getScheme(feature, feature.type));
+			break;
+		case LITFLT:
+			Renderer.symbol(feature, Buoys.Float, getScheme(feature, feature.type));
+			break;
+		case BOYINB:
+			Renderer.symbol(feature, Buoys.Super, getScheme(feature, feature.type));
+			break;
+		default:
+			break;
+		}
+		if (feature.objs.get(Obj.TOPMAR) != null)
+			Renderer.symbol(feature, Topmarks.Shapes.get(feature.objs.get(Obj.TOPMAR).get(0).get(Att.TOPSHP).val), getScheme(feature, Obj.TOPMAR), Topmarks.FloatDelta);
+		Signals.addSignals(feature);
+	}
+	
+	private static void gauges(Feature feature) {
+		if (Renderer.zoom >= 14) {
+			Renderer.symbol(feature, Harbours.TideGauge);
+			Signals.addSignals(feature);
+		}
+	}
+	
+	private static void harbours(Feature feature) {
+		String name = getName(feature);
+		switch (feature.type) {
+		case ACHBRT:
+			if (Renderer.zoom >= 14) {
+				Renderer.symbol(feature, Harbours.Anchorage, new Scheme(Mline));
+			Renderer.labelText(feature, name == null ? "" : name, new Font("Arial", Font.PLAIN, 30), LabelStyle.RRCT, Mline, Color.white, new Delta(Handle.BC));
+			}
+			double radius = (Double)getAttVal(feature, Obj.ACHBRT, 0, Att.RADIUS);
+			if (radius != 0) {
+				UniHLU units = (UniHLU)getAttVal(feature, Obj.ACHBRT, 0, Att.HUNITS);
+				Renderer.lineCircle (feature, new LineStyle(Mline, 4, new float[] { 10, 10 }, null), radius, units);
+			}
+			break;
+		case ACHARE:
+			if (Renderer.zoom >= 12) {
+				if (feature.flag != Fflag.AREA) {
+					Renderer.symbol(feature, Harbours.Anchorage, new Scheme(Color.black));
+				} else {
+					Renderer.symbol(feature, Harbours.Anchorage, new Scheme(Mline));
+					Renderer.lineSymbols(feature, Areas.Restricted, 1.0, Areas.LineAnchor, null, 10, Mline);
+				}
+				if ((Renderer.zoom >= 15) && ((name) != null)) {
+					Renderer.labelText(feature, name, new Font("Arial", Font.BOLD, 60), LabelStyle.NONE, Mline, null, new Delta(Handle.LC, AffineTransform.getTranslateInstance(70, 0)));
+				}
+				ArrayList<StsSTS> sts = (ArrayList<StsSTS>)getAttVal(feature, Obj.ACHARE, 0, Att.STATUS);
+				if ((Renderer.zoom >= 15) && (sts != null) && (sts.contains(StsSTS.STS_RESV))) {
+					Renderer.labelText(feature, "Reserved", new Font("Arial", Font.PLAIN, 50), LabelStyle.NONE, Mline, null, new Delta(Handle.TC, AffineTransform.getTranslateInstance(0, 60)));
+				}
+			}
+			ArrayList<CatACH> cats = (ArrayList<CatACH>)getAttVal(feature, Obj.ACHARE, 0, Att.CATACH);
+			int dy = (cats.size() - 1) * -30;
+			for (CatACH cat : cats) {
+				switch (cat) {
+				case ACH_DEEP:
+					Renderer.labelText(feature, "DW", new Font("Arial", Font.BOLD, 50), LabelStyle.NONE, Mline, null, new Delta(Handle.RC, AffineTransform.getTranslateInstance(-60, dy)));
+					dy += 60;
+					break;
+				case ACH_TANK:
+					Renderer.labelText(feature, "Tanker", new Font("Arial", Font.BOLD, 50), LabelStyle.NONE, Mline, null, new Delta(Handle.RC, AffineTransform.getTranslateInstance(-60, dy)));
+					dy += 60;
+					break;
+				case ACH_H24P:
+					Renderer.labelText(feature, "24h", new Font("Arial", Font.BOLD, 50), LabelStyle.NONE, Mline, null, new Delta(Handle.RC, AffineTransform.getTranslateInstance(-60, dy)));
+					dy += 60;
+					break;
+				case ACH_EXPL:
+					Renderer.symbol(feature, Harbours.Explosives, new Scheme(Mline), new Delta(Handle.RC, AffineTransform.getTranslateInstance(-60, dy)));
+					dy += 60;
+					break;
+				case ACH_QUAR:
+					Renderer.symbol(feature, Harbours.Hospital, new Scheme(Mline), new Delta(Handle.RC, AffineTransform.getTranslateInstance(-60, dy)));
+					dy += 60;
+					break;
+				case ACH_SEAP:
+					Renderer.symbol(feature, Areas.Seaplane, new Scheme(Mline), new Delta(Handle.RC, AffineTransform.getTranslateInstance(-60, dy)));
+					dy += 60;
+					break;
+				}
+			}
+			break;
+		case BERTHS:
+			if (Renderer.zoom >= 14) {
+				Renderer.labelText(feature, name == null ? " " : name, new Font("Arial", Font.PLAIN, 40), LabelStyle.RRCT, Mline, Color.white, null);
+			}
+			break;
+		case BUISGL:
+		  if (Renderer.zoom >= 16) {
+		  	ArrayList<Symbol> symbols = new ArrayList<Symbol>();
+		  	ArrayList<FncFNC> fncs = (ArrayList<FncFNC>) getAttVal(feature, Obj.BUISGL, 0, Att.FUNCTN);
+		  	for (FncFNC fnc : fncs) {
+		  		symbols.add(Landmarks.Funcs.get(fnc));
+		  	}
+			  if (feature.objs.containsKey(Obj.SMCFAC))  {
+			  	ArrayList<CatSCF> scfs = (ArrayList<CatSCF>) getAttVal(feature, Obj.SMCFAC, 0, Att.CATSCF);
+			  	for (CatSCF scf : scfs) {
+			  		symbols.add(Facilities.Cats.get(scf));
+			  	}
+			  }
+			  Renderer.cluster(feature, symbols);
+		  }
+			break;
+		case HRBFAC:
+			if (Renderer.zoom >= 12) {
+				ArrayList<CatHAF> cathaf = (ArrayList<CatHAF>) getAttVal(feature, Obj.HRBFAC, 0, Att.CATHAF);
+				if (cathaf.size() == 1) {
+					switch (cathaf.get(0)) {
+					case HAF_MRNA:
+						Renderer.symbol(feature, Harbours.Marina);
+						break;
+					case HAF_MANF:
+						Renderer.symbol(feature, Harbours.MarinaNF);
+						break;
+					case HAF_FISH:
+						Renderer.symbol(feature, Harbours.Fishing);
+						break;
+					default:
+						Renderer.symbol(feature, Harbours.Harbour);
+						break;
+					}
+				} else {
+					Renderer.symbol(feature, Harbours.Harbour);
+				}
+			}
+			break;
+		default:
+			break;
+		}
+	}
+	
+	private static void landmarks(Feature feature) {
+		ArrayList<CatLMK> cats = (ArrayList<CatLMK>) getAttVal(feature, feature.type, 0, Att.CATLMK);
+		Symbol catSym = Landmarks.Shapes.get(cats.get(0));
+		ArrayList<FncFNC> fncs = (ArrayList<FncFNC>) getAttVal(feature, feature.type, 0, Att.FUNCTN);
+		Symbol fncSym = Landmarks.Funcs.get(fncs.get(0));
+		if ((fncs.get(0) == FncFNC.FNC_CHCH) && (cats.get(0) == CatLMK.LMK_TOWR))
+			catSym = Landmarks.ChurchTower;
+		if ((cats.get(0) == CatLMK.LMK_UNKN) && (fncs.get(0) == FncFNC.FNC_UNKN) && (feature.objs.get(Obj.LIGHTS) != null))
+			catSym = Beacons.LightMajor;
+		if (cats.get(0) == CatLMK.LMK_RADR)
+			fncSym = Landmarks.RadioTV;
+		Renderer.symbol(feature, catSym);
+		Renderer.symbol(feature, fncSym);
+/*  if (!has_attribute("function") && !has_attribute("category") && has_object("light")) {
+    symbol("lighthouse");
+    if ((zoom >= 15) && has_item_attribute("name"))
+      text(item_attribute("name"), "font-family:Arial; font-weight:bold; font-size:80; text-anchor:middle", 0, -70);
+  } else {
+    if ((zoom >= 15) && has_item_attribute("name"))
+      text(item_attribute("name"), "font-family:Arial; font-weight:bold; font-size:80; text-anchor:start", 60, -50);
+  }
+}
+*/
+		Signals.addSignals(feature);
+	}
+	
+	private static void buildings(Feature feature) {
+	}
+	
+	private static void lights(Feature feature) {
+		switch (feature.type) {
+		case LITMAJ:
+			Renderer.symbol(feature, Beacons.LightMajor);
+			break;
+		case LITMIN:
+		case LIGHTS:
+			Renderer.symbol(feature, Beacons.LightMinor);
+			break;
+		case PILPNT:
+			if (hasObject(feature, Obj.LIGHTS))
+				Renderer.symbol(feature, Beacons.LightMinor);
+			else
+				Renderer.symbol(feature, Harbours.Post);
+			break;
+		}
+		Signals.addSignals(feature);
+	}
+
+	private static void locks(Feature feature) {
+	}
+
+	private static void marinas(Feature feature) {
+		if (Renderer.zoom >= 16) {
+			
+		}
+	}
+	private static void moorings(Feature feature) {
+		switch ((CatMOR) getAttVal(feature, feature.type, 0, Att.CATMOR)) {
+		case MOR_DLPN:
+			Renderer.symbol(feature, Harbours.Dolphin);
+			break;
+		case MOR_DDPN:
+			Renderer.symbol(feature, Harbours.DeviationDolphin);
+			break;
+		case MOR_BLRD:
+		case MOR_POST:
+			Renderer.symbol(feature, Harbours.Bollard);
+			break;
+		case MOR_BUOY:
+			BoySHP shape = (BoySHP) getAttVal(feature, feature.type, 0, Att.BOYSHP);
+			if (shape == BoySHP.BOY_UNKN)
+				shape = BoySHP.BOY_SPHR;
+			Renderer.symbol(feature, Buoys.Shapes.get(shape), getScheme(feature, feature.type));
+			break;
+		}
+		Signals.addSignals(feature);
+	}
+
+	private static void notices(Feature feature) {
+		if (Renderer.zoom >= 14) {
+			double dx = 0.0, dy = 0.0;
+			switch (feature.type) {
+			case BCNCAR:
+			case BCNISD:
+			case BCNLAT:
+			case BCNSAW:
+			case BCNSPP:
+			case BCNWTW:
+				dy = 45.0;
+				break;
+			case NOTMRK:
+				dy = 0.0;
+				break;
+			default:
+				return;
+			}
+			Symbol s1 = null, s2 = null;
+			MarSYS sys = MarSYS.SYS_CEVN;
+			BnkWTW bnk = BnkWTW.BWW_UNKN;
+			AttItem att = feature.atts.get(Att.MARSYS);
+			if (att != null) sys = (MarSYS)att.val;
+			ObjTab objs = feature.objs.get(Obj.NOTMRK);
+			int n = objs.size();
+			if (n > 2) {
+				s1 = Notices.Notice;
+				n = 1;
+			} else {
+				for (AttMap atts : objs.values()) {
+					if (atts.get(Att.MARSYS) != null) sys = (MarSYS)atts.get(Att.MARSYS).val;
+					CatNMK cat = CatNMK.NMK_UNKN;
+					if (atts.get(Att.CATNMK) != null) cat = (CatNMK)atts.get(Att.CATNMK).val;
+					s2 = Notices.getNotice(cat, sys);
+				}
+			}
+/*      Obj_t *obj = getObj(item, NOTMRK, i);
+      if (obj == NULL) continue;
+      Atta_t add;
+      int idx = 0;
+      while ((add = getAttEnum(obj, ADDMRK, idx++)) != MRK_UNKN) {
+        if ((add == MRK_LTRI) && (i == 2)) swap = true;
+        if ((add == MRK_RTRI) && (i != 2)) swap = true;
+      }
+    }
+  } else {
+  	
+  }
+  for (int i = 0; i <=2; i++) {
+    Obj_t *obj = getObj(item, NOTMRK, i);
+    if (obj == NULL) continue;
+    Atta_t category = getAttEnum(obj, CATNMK, i);
+    Atta_t add;
+    int idx = 0;
+    int top=0, bottom=0, left=0, right=0;
+    while ((add = getAttEnum(obj, ADDMRK, idx++)) != MRK_UNKN) {
+      switch (add) {
+        case MRK_TOPB:
+          top = add;
+          break;
+        case MRK_BOTB:
+        case MRK_BTRI:
+          bottom = add;
+          break;
+        case MRK_LTRI:
+          left = add;
+          break;
+        case MRK_RTRI:
+          right = add;
+          break;
+        default:
+          break;
+      }
+    }
+    double orient = getAtt(obj, ORIENT) != NULL ? getAtt(obj, ORIENT)->val.val.f : 0.0;
+    int system = getAtt(obj, MARSYS) != NULL ? getAtt(obj, MARSYS)->val.val.e : 0;
+    double flip = 0.0;
+    char *symb = "";
+    char *base = "";
+    char *colour = "black";
+    if ((system == SYS_BWR2) || (system == SYS_BNWR)) {
+      symb = bniwr_map[category];
+      switch (category) {
+        case NMK_NANK:
+        case NMK_LMHR:
+        case NMK_KTPM...NMK_RSPD:
+        {
+          int bank = getAtt(obj, BNKWTW) != NULL ? getAtt(obj, BNKWTW)->val.val.e : 0;
+          switch (bank) {
+            case BWW_LEFT:
+              base = "notice_blb";
+              colour = "red";
+              break;
+            case BWW_RGHT:
+              base = "notice_brb";
+              colour = "green";
+              break;
+            default:
+              base = "notice_bsi";
+              colour = "black";
+              break;
+          }
+        }
+        default:
+          break;
+      }
+    } else if (system == SYS_PPWB) {
+      int bank = getAtt(obj, BNKWTW) != NULL ? getAtt(obj, BNKWTW)->val.val.e : 0;
+      if (bank != 0) {
+        switch (category) {
+          case NMK_WLAR:
+            if (bank == BNK_LEFT)
+              base = "notice_pwlarl";
+            else
+              base = "notice_pwlarr";
+            break;
+          case NMK_WRAL:
+            if (bank == BNK_LEFT)
+              base = "notice_pwrall";
+            else
+              base = "notice_pwralr";
+            break;
+          case NMK_KTPM:
+            if (bank == BNK_LEFT)
+              base = "notice_ppml";
+            else
+              base = "notice_ppmr";
+            break;
+          case NMK_KTSM:
+            if (bank == BNK_LEFT)
+              base = "notice_psml";
+            else
+              base = "notice_psmr";
+            break;
+          case NMK_KTMR:
+            if (bank == BNK_LEFT)
+              base = "notice_pmrl";
+            else
+              base = "notice_pmrr";
+            break;
+          case NMK_CRTP:
+          if (bank == BNK_LEFT)
+              base = "notice_pcpl";
+            else
+              base = "notice_pcpr";
+            break;
+          case NMK_CRTS:
+            if (bank == BNK_LEFT)
+              base = "notice_pcsl";
+            else
+              base = "notice_pcsr";
+            break;
+          default:
+            break;
+        }
+      }
+    } else {
+      symb = notice_map[category];
+      switch (category) {
+        case NMK_NOVK...NMK_NWSH:
+        case NMK_NMTC...NMK_NLBG:
+          base = "notice_a";
+          break;
+        case NMK_MVTL...NMK_CHDR:
+          base = "notice_b";
+          break;
+        case NMK_PRTL...NMK_PRTR:
+        case NMK_OVHC...NMK_LBGP:
+          base = "notice_e";
+          colour = "white";
+          break;
+        default:
+          break;
+      }
+      switch (category) {
+        case NMK_MVTL:
+        case NMK_ANKP:
+        case NMK_PRTL:
+        case NMK_MWAL:
+        case NMK_MWAR:
+          flip = 180.0;
+          break;
+        case NMK_SWWR:
+        case NMK_WRSL:
+        case NMK_WARL:
+          flip = -90.0;
+          break;
+        case NMK_SWWC:
+        case NMK_SWWL:
+        case NMK_WLSR:
+        case NMK_WALR:
+          flip = 90.0;
+          break;
+        default:
+          break;
+      }
+    }
+    if (n == 2) {
+      dx = (((i != 2) && swap) || ((i == 2) && !swap)) ? -30.0 : 30.0;
+    }
+    if (top == MRK_TOPB)
+      renderSymbol(item, NOTMRK, "notice_board", "", "", BC, dx, dy, orient);
+    if (bottom == MRK_BOTB)
+      renderSymbol(item, NOTMRK, "notice_board", "", "", BC, dx, dy, orient+180);
+    if (bottom == MRK_BTRI)
+      renderSymbol(item, NOTMRK, "notice_triangle", "", "", BC, dx, dy, orient+180);
+    if (left == MRK_LTRI)
+      renderSymbol(item, NOTMRK, "notice_triangle", "", "", BC, dx, dy, orient-90);
+    if (right == MRK_RTRI)
+      renderSymbol(item, NOTMRK, "notice_triangle", "", "", BC, dx, dy, orient+90);
+    renderSymbol(item, NOTMRK, base, "", "", CC, dx, dy, orient);
+    renderSymbol(item, NOTMRK, symb, "", colour, CC, dx, dy, orient+flip);
+  }
+*/
+		}
+	}
+
+	private static void obstructions(Feature feature) {
+		if ((Renderer.zoom >= 14) && (feature.type == Obj.UWTROC)) {
+			WatLEV lvl = (WatLEV) getAttVal(feature, feature.type, 0, Att.WATLEV);
+			switch (lvl) {
+			case LEV_CVRS:
+				Renderer.symbol(feature, Areas.RockC);
+				break;
+			case LEV_AWSH:
+				Renderer.symbol(feature, Areas.RockA);
+				break;
+			default:
+				Renderer.symbol(feature, Areas.Rock);
+			}
+		} else {
+			Renderer.symbol(feature, Areas.Rock);
+		}
+	}
+
+	private static void pipelines(Feature feature) {
+		if ((Renderer.zoom >= 16) && (feature.length < 2)) {
+			if (feature.type == Obj.PIPSOL) {
+				Renderer.lineSymbols(feature, Areas.Pipeline, 1.0, null, null, 0, Mline);
+			} else if (feature.type == Obj.PIPOHD) {
+				Renderer.lineVector(feature, new LineStyle(Color.black, 8));
+				AttMap atts = feature.objs.get(Obj.PIPOHD).get(0);
+				double verclr = 0;
+				if (atts != null) {
+					if (atts.containsKey(Att.VERCLR)) {
+						verclr = (Double) atts.get(Att.VERCLR).val;
+					} else {
+						verclr = atts.containsKey(Att.VERCSA) ? (Double) atts.get(Att.VERCSA).val : 0;
+					}
+					if (verclr > 0) {
+						Renderer.labelText(feature, String.valueOf(verclr), new Font("Arial", Font.PLAIN, 50), LabelStyle.VCLR, Color.black, new Delta(Handle.TC, AffineTransform.getTranslateInstance(0,25)));
+					}
+				}
+			}
+		}
+	}
+
+	private static void platforms(Feature feature) {
+		ArrayList<CatOFP> cats = (ArrayList<CatOFP>)getAttVal(feature, Obj.OFSPLF, 0, Att.CATOFP);
+		if ((CatOFP) cats.get(0) == CatOFP.OFP_FPSO)
+			Renderer.symbol(feature, Buoys.Storage);
+		else
+			Renderer.symbol(feature, Landmarks.Platform);
+		String name = getName(feature);
+		if ((Renderer.zoom >= 15) && (name != null))
+			Renderer.labelText(feature, name, new Font("Arial", Font.BOLD, 40), LabelStyle.NONE, Color.black, null, new Delta(Handle.BL, AffineTransform.getTranslateInstance(20, -50)));
+		Signals.addSignals(feature);
+	}
+
+	private static void ports(Feature feature) {
+		if (Renderer.zoom >= 14) {
+			if (feature.type == Obj.CRANES) {
+				if ((CatCRN) getAttVal(feature, feature.type, 0, Att.CATCRN) == CatCRN.CRN_CONT)
+					Renderer.symbol(feature, Harbours.ContainerCrane);
+				else
+					Renderer.symbol(feature, Harbours.PortCrane);
+			} else if (feature.type == Obj.HULKES) {
+				Renderer.lineVector(feature, new LineStyle(Color.black, 4, null, new Color(0xffe000)));
+				String name = getName(feature);
+				if ((Renderer.zoom >= 15) && (name != null))
+					Renderer.labelText(feature, name, new Font("Arial", Font.BOLD, 80), LabelStyle.NONE, Color.black, null, null);
+			}
+		}
+	}
+
+	private static void separation(Feature feature) {
+		switch (feature.type) {
+		case TSEZNE:
+		case TSSCRS:
+		case TSSRON:
+			if (Renderer.zoom <= 15)
+				Renderer.lineVector(feature, new LineStyle(null, 0, null, new Color(0x80c48080, true)));
+			else
+				Renderer.lineVector(feature, new LineStyle(new Color(0x80c48080, true), 20, null, null));
+			String name = getName(feature);
+			if ((Renderer.zoom >= 10) && (name != null))
+				Renderer.labelText(feature, name, new Font("Arial", Font.BOLD, 150), LabelStyle.NONE, new Color(0x80c48080, true), null, null);
+			break;
+		case TSELNE:
+			Renderer.lineVector(feature, new LineStyle(new Color(0x80c48080, true), 20, null, null));
+			break;
+		case TSSLPT:
+			Renderer.lineSymbols(feature, Areas.LaneArrow, 0.5, null, null, 0, new Color(0x80c48080, true));
+			break;
+		case TSSBND:
+			Renderer.lineVector(feature, new LineStyle(new Color(0x80c48080, true), 20, new float[] { 40, 40 }, null));
+			break;
+		case ISTZNE:
+			Renderer.lineSymbols(feature, Areas.Restricted, 1.0, null, null, 0, new Color(0x80c48080, true));
+			break;
+		}
+	}
+
+	private static void shoreline(Feature feature) {
+		if (Renderer.zoom >= 12) {
+			switch ((CatSLC) getAttVal(feature, feature.type, 0, Att.CATSLC)) {
+			case SLC_TWAL:
+				WatLEV lev = (WatLEV) getAttVal(feature, feature.type, 0, Att.WATLEV);
+				if (lev == WatLEV.LEV_CVRS) {
+					Renderer.lineVector(feature, new LineStyle(Color.black, 10, new float[] { 40, 40 }, null));
+					if (Renderer.zoom >= 15)
+						Renderer.lineText(feature, "(covers)", new Font("Arial", Font.PLAIN, 80), Color.black, 0.5, 20);
+				} else {
+					Renderer.lineVector(feature, new LineStyle(Color.black, 10, null, null));
+				}
+				if (Renderer.zoom >= 15)
+					Renderer.lineText(feature, "Training Wall", new Font("Arial", Font.PLAIN, 80), Color.black, 0.5, -20);
+			}
+		}
+	}
+
+	private static void stations(Feature feature) {
+		if (Renderer.zoom >= 14) {
+			String str = "";
+			switch (feature.type) {
+			case SISTAT:
+			case SISTAW:
+				Renderer.symbol(feature, Harbours.SignalStation);
+				str = "SS";
+				//  Append (cat) to str
+				break;
+			case RDOSTA:
+				Renderer.symbol(feature, Harbours.SignalStation);
+				Renderer.symbol(feature, Beacons.RadarStation);
+				break;
+			case RADSTA:
+				Renderer.symbol(feature, Harbours.SignalStation);
+				Renderer.symbol(feature, Beacons.RadarStation);
+				break;
+			case PILBOP:
+				Renderer.symbol(feature, Harbours.Pilot);
+				break;
+			case CGUSTA:
+				Renderer.symbol(feature, Harbours.SignalStation);
+				str = "CG";
+				break;
+			case RSCSTA:
+				Renderer.symbol(feature, Harbours.Rescue);
+				break;
+			}
+			if ((Renderer.zoom >= 15) && !str.isEmpty()) {
+				Renderer.labelText(feature, str, new Font("Arial", Font.PLAIN, 40), LabelStyle.NONE, Color.black, null, new Delta(Handle.LC, AffineTransform.getTranslateInstance(30, 0)));
+			}
+		}
+		Signals.addSignals(feature);
+	}
+
+	private static void transits(Feature feature) {
+	  if (Renderer.zoom >= 12) {
+	  	if (feature.type == Obj.RECTRC) Renderer.lineVector (feature, new LineStyle(Color.black, 10, null, null));
+	  	else if (feature.type == Obj.NAVLNE) Renderer.lineVector (feature, new LineStyle(Color.black, 10, new float[] { 25, 25 }, null));
+	  }
+	  if (Renderer.zoom >= 15) {
+	  	String str = "";
+			String name = getName(feature);
+			if (name != null) str += name + " ";
+			Double ort = (Double) getAttVal(feature, feature.type, 0, Att.ORIENT);
+			if (ort != null) str += ort.toString() + "\u0152";
+			if (!str.isEmpty()) Renderer.lineText(feature, str, new Font("Arial", Font.PLAIN, 80), Color.black, 0.5, -20);
+	  }
+	}
+
+	private static void waterways(Feature feature) {
+		
+	}
+
+	private static void wrecks(Feature feature) {
+		if (Renderer.zoom >= 14) {
+			CatWRK cat = (CatWRK) getAttVal(feature, feature.type, 0, Att.CATWRK);
+			switch (cat) {
+			case WRK_DNGR:
+			case WRK_MSTS:
+				Renderer.symbol(feature, Areas.WreckD);
+				break;
+			case WRK_HULS:
+				Renderer.symbol(feature, Areas.WreckS);
+				break;
+			default:
+				Renderer.symbol(feature, Areas.WreckND);
+			}
+		}
+	}
+}
Index: applications/editors/josm/plugins/smed2/src/render/Signals.java
===================================================================
--- applications/editors/josm/plugins/smed2/src/render/Signals.java	(revision 30150)
+++ applications/editors/josm/plugins/smed2/src/render/Signals.java	(revision 30150)
@@ -0,0 +1,610 @@
+/* Copyright 2013 Malcolm Herring
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3 of the License.
+ *
+ * For a copy of the GNU General Public License, see <http://www.gnu.org/licenses/>.
+ */
+
+package render;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.geom.*;
+import java.util.ArrayList;
+
+import s57.S57att.*;
+import s57.S57obj.*;
+import s57.S57val.*;
+import s57.S57map.*;
+import render.Renderer.*;
+import symbols.Beacons;
+import symbols.Topmarks;
+import symbols.Symbols.*;
+
+public class Signals {
+
+	public static void addSignals(Feature feature) {
+	  if (feature.objs.containsKey(Obj.FOGSIG)) fogSignals(feature);
+	  if (feature.objs.containsKey(Obj.RTPBCN)) radarStations(feature);
+	  if (feature.objs.containsKey(Obj.RADSTA)) radarStations(feature);
+	  if (feature.objs.containsKey(Obj.RDOSTA)) radioStations(feature);
+	  if (feature.objs.containsKey(Obj.LIGHTS)) lights(feature);
+	}
+	
+	public static void fogSignals(Feature feature) {
+		
+	}
+
+	public static void radarStations(Feature feature) {
+		Renderer.symbol(feature, Beacons.RadarStation);
+		String bstr = "";
+		CatRTB cat = (CatRTB) Rules.getAttVal(feature, Obj.RTPBCN, 0, Att.CATRTB);
+		switch (cat) {
+		case RTB_RAMK:
+			bstr += " Ramark";
+			break;
+		case RTB_RACN:
+			bstr += " Racon";
+			String astr = (String) Rules.getAttVal(feature, Obj.RTPBCN, 0, Att.SIGGRP);
+			if (!astr.isEmpty()) {
+				bstr += "(" + astr + ")";
+			}
+			Double per = (Double) Rules.getAttVal(feature, Obj.RTPBCN, 0, Att.SIGPER);
+			Double mxr = (Double) Rules.getAttVal(feature, Obj.RTPBCN, 0, Att.VALMXR);
+			if ((per != 0) || (mxr != 0)) {
+				bstr += (astr.isEmpty() ? " " : "");
+				bstr += (per != 0) ? per.toString() + "s" : "";
+				bstr += (mxr != 0) ? mxr.toString() + "M" : "";
+			}
+			break;
+		default:
+			break;
+		}
+		if ((Renderer.zoom >= 15) && !bstr.isEmpty()) {
+			Renderer.labelText(feature, bstr, new Font("Arial", Font.PLAIN, 40), LabelStyle.NONE, Color.black, null, new Delta(Handle.BR, AffineTransform.getTranslateInstance(-30, -30)));
+		}
+	}
+
+	public static void radioStations(Feature feature) {
+		Renderer.symbol(feature, Beacons.RadarStation);
+		ArrayList<CatROS> cats = (ArrayList<CatROS>)Rules.getAttVal(feature, Obj.RDOSTA, 0, Att.CATROS);
+		boolean vais = false;
+		String bstr = "";
+		for (CatROS ros : cats) {
+			switch (ros) {
+			case ROS_OMNI:
+				bstr += " RC";
+				break;
+			case ROS_DIRL:
+				bstr += " RD";
+				break;
+			case ROS_ROTP:
+				bstr += " RW";
+				break;
+			case ROS_CNSL:
+				bstr += " Consol";
+				break;
+			case ROS_RDF:
+				bstr += " RG";
+				break;
+			case ROS_QTA:
+				bstr += " R";
+				break;
+			case ROS_AERO:
+				bstr += " AeroRC";
+				break;
+			case ROS_DECA:
+				bstr += " Decca";
+				break;
+			case ROS_LORN:
+				bstr += " Loran";
+				break;
+			case ROS_DGPS:
+				bstr += " DGPS";
+				break;
+			case ROS_TORN:
+				bstr += " Toran";
+				break;
+			case ROS_OMGA:
+				bstr += " Omega";
+				break;
+			case ROS_SYLD:
+				bstr += " Syledis";
+				break;
+			case ROS_CHKA:
+				bstr += " Chiaka";
+				break;
+			case ROS_PCOM:
+			case ROS_COMB:
+			case ROS_FACS:
+			case ROS_TIME:
+				break;
+			case ROS_PAIS:
+			case ROS_SAIS:
+				bstr += " AIS";
+				break;
+			case ROS_VAIS:
+				vais = true;
+				break;
+			case ROS_VANC:
+				vais = true;
+				Renderer.symbol(feature, Topmarks.TopNorth, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -25)));
+				break;
+			case ROS_VASC:
+				vais = true;
+				Renderer.symbol(feature, Topmarks.TopSouth, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -25)));
+				break;
+			case ROS_VAEC:
+				vais = true;
+				Renderer.symbol(feature, Topmarks.TopEast, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -25)));
+				break;
+			case ROS_VAWC:
+				vais = true;
+				Renderer.symbol(feature, Topmarks.TopWest, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -25)));
+				break;
+			case ROS_VAPL:
+				vais = true;
+				Renderer.symbol(feature, Topmarks.TopCan, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -25)));
+				break;
+			case ROS_VASL:
+				vais = true;
+				Renderer.symbol(feature, Topmarks.TopCone, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -25)));
+				break;
+			case ROS_VAID:
+				vais = true;
+				Renderer.symbol(feature, Topmarks.TopIsol, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -25)));
+				break;
+			case ROS_VASW:
+				vais = true;
+				Renderer.symbol(feature, Topmarks.TopSphere, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -25)));
+				break;
+			case ROS_VASP:
+				vais = true;
+				Renderer.symbol(feature, Topmarks.TopX, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -25)));
+				break;
+			case ROS_VAWK:
+				vais = true;
+				Renderer.symbol(feature, Topmarks.TopCross, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, -25)));
+				break;
+			default:
+				break;
+			}
+		}
+		if (Renderer.zoom >= 15) {
+			if (vais) {
+				Renderer.labelText(feature, "V-AIS", new Font("Arial", Font.PLAIN, 40), LabelStyle.NONE, Color.black, null, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, 70)));
+			}
+			if (!bstr.isEmpty()) {
+				Renderer.labelText(feature, bstr, new Font("Arial", Font.PLAIN, 40), LabelStyle.NONE, Color.black, null, new Delta(Handle.BR, AffineTransform.getTranslateInstance(-30, -70)));
+			}
+		}
+	}
+
+	public static void lights(Feature feature) {
+		
+	}
+	
+	private static Point2D.Double radial(Snode centre, double radius, double angle) {
+		Point2D origin = Renderer.context.getPoint(centre);
+		double mile = Renderer.context.getPoint(Renderer.map.new Snode((centre.lat + Math.toRadians(1/60)), centre.lon)).getY() - origin.getY();
+		return new Point2D.Double(origin.getX() - (radius * mile * Math.sin(angle)), origin.getY() - (radius * mile * Math.cos(angle)));
+	}
+/*
+void renderFlare(Item_t *item) {
+  char *col = light_colours[COL_MAG];
+  Obj_t *obj = getObj(item, LIGHTS, 0);
+  Att_t *att;
+  if (((att = getAtt(obj, COLOUR)) != NULL) && (att->val.val.l->next == NULL)) {
+    col = light_colours[att->val.val.l->val];
+  }
+  renderSymbol(item, LIGHTS, "light", "", col, CC, 0, 0, 120);
+}
+
+void renderSector(Item_t *item, int s, char *text, char *style, double offset, int dy) {
+  Obj_t *sector;
+  double start, end;
+  Att_t *att;
+  XY_t p0, p1;
+  double r0, r1;
+  double b0, b1, span;
+  char *col;
+  XY_t pos = findCentroid(item);
+  if ((sector = getObj(item, LIGHTS, s)) != NULL) {
+    strcpy(string1, (att = getAtt(sector, LITRAD)) != NULL ? att->val.val.a : "0.2");
+    if (((att = getAtt(sector, CATLIT)) != NULL) && (testAtt(att, LIT_DIR)) && ((att = getAtt(sector, ORIENT)) != NULL)) {
+      b0 = fmod(540.0 - att->val.val.f, 360.0);
+      if ((att = getAtt(sector, COLOUR)) != NULL) {
+        col = light_colours[att->val.val.l->val];
+        r0 = atof(string1);
+        p0 = radial(pos, r0, b0);
+        printf("<path d=\"M %g,%g L %g,%g\" style=\"fill:none;stroke:#808080;stroke-width:%g;stroke-dasharray:%g\"/>\n",
+               pos.x, pos.y, p0.x, p0.y, (4 * symbolScale[zoom]), (20 * symbolScale[zoom]));
+        start = fmod(b0 + 2.0, 360.0);
+        end = fmod(360.0 + b0 - 2.0, 360.0);
+        Obj_t *adj;
+        for (int i = s-1; i <= s+1; i++) {
+          if (i == s) continue;
+          if ((adj = getObj(item, LIGHTS, i)) == NULL) continue;
+          Att_t *att;
+          if (((att = getAtt(adj, CATLIT)) != NULL) && (testAtt(att, LIT_DIR)) && ((att = getAtt(adj, ORIENT)) != NULL)) {
+            b1 = fmod(540.0 - att->val.val.f, 360.0);
+            if (fabs(b0 - b1) > 180.0) {
+              if (b0 < b1) b0 += 360.0;
+              else b1 += 360.0;
+            }
+            if (fabs(b0 - b1) < 4.0) {
+              if (b1 > b0) start = fmod((720.0 + b0 + b1) / 2.0, 360.0);
+              else end = fmod((720.0 + b0 + b1) / 2.0, 360.0);
+            }
+          }
+        }
+        p0 = radial(pos, r0, start);
+        p1 = radial(pos, r0, end);
+        printf("<path id=\"%d\" d=\"M %g,%g A %g,%g,0,0,1,%g,%g\" style=\"fill:none;stroke:%s;stroke-width:%g\"/>\n",
+               ++ref, p0.x, p0.y, r0*mile, r0*mile, p1.x, p1.y, col, (20 * symbolScale[zoom]));
+        if (att->val.val.l->next != NULL) {
+          char *col = light_colours[att->val.val.l->next->val];
+          r1 = r0 - (20 * symbolScale[zoom]/mile);
+          p0 = radial(pos, r1, start);
+          p1 = radial(pos, r1, end);
+          printf("<path d=\"M %g,%g A %g,%g,0,0,1,%g,%g\" style=\"fill:none;stroke:%s;stroke-width:%g\"/>\n",
+                 p0.x, p0.y, r1*mile, r1*mile, p1.x, p1.y, col, (20 * symbolScale[zoom]));
+        }
+      }
+    } else if ((att = getAtt(sector, SECTR1)) != NULL) {
+      start = fmod(540.0 - att->val.val.f, 360.0);
+      if ((att = getAtt(sector, SECTR2)) != NULL) {
+        end = fmod(540.0 - att->val.val.f, 360.0);
+        start += start < end ? 360.0 : 0.0;
+        if ((att = getAtt(sector, COLOUR)) != NULL) {
+          char *ttok, *etok;
+          char *radstr = strdup(string1);
+          int arc = 0;
+          col = light_colours[att->val.val.l->val];
+          r0 = 0.0;
+          b0 = b1 = start;
+          for (char *tpl = strtok_r(radstr, ";", &ttok); tpl != NULL; tpl = strtok_r(NULL, ";", &ttok)) {
+            p0 = radial(pos, r0, b0);
+            span = 0.0;
+            char *ele = strtok_r(tpl, ":", &etok);
+            if ((*tpl == ':') && (r0 == 0.0)) {
+              r1 = 0.2;
+            } else if (*tpl != ':') {
+              r1 = atof(tpl);
+              ele = strtok_r(NULL, ":", &etok);
+            }
+            while (ele != NULL) {
+              if (isalpha(*ele)) {
+                if (strcmp(ele, "suppress") == 0) arc = 2;
+                else if (strcmp(ele, "dashed") == 0) arc = 1;
+                else arc = 0;
+              } else {
+                span = atof(ele);
+              }
+              ele = strtok_r(NULL, ":", &etok);
+            }
+            if (span == 0.0) {
+              char *back = (ttok != NULL) ? strstr(ttok, "-") : NULL;
+              if (back != NULL) {
+                span = b0 - end + atof(back);
+              } else {
+                span = b0 - end;
+              }
+            }
+            if (r1 != r0) {
+              p1 = radial(pos, r1, b0);
+              if (!((start == 180.0) && (end == 180.0)))
+                printf("<path d=\"M %g,%g L %g,%g\" style=\"fill:none;stroke:#808080;stroke-width:%g;stroke-dasharray:%g\"/>\n",
+                       p0.x, p0.y, p1.x, p1.y, (4 * symbolScale[zoom]), (20 * symbolScale[zoom]));
+              r0 = r1;
+              p0 = p1;
+            }
+            if (span < 0.0) {
+              b1 = end - span;
+              b1 = b1 > b0 ? b0 : b1;
+              b0 = b1;
+              b1 = end;
+              p0 = radial(pos, r0, b0);
+            } else {
+              b1 = b0 - span;
+              b1 = b1 < end ? end : b1;
+            }
+            p1 = radial(pos, r1, b1);
+            if ((b0 == 180.0) && (b1 == 180.0)) {
+              span = 360.0;
+              p1 = radial(pos, r1, b1+0.01);
+            }
+            if (arc == 0) {
+              if (p0.x < p1.x)
+                printf("<path id=\"%d\" d=\"M %g,%g A %g,%g,0,%d,1,%g,%g\" style=\"fill:none;stroke:%s;stroke-width:%g\"/>\n",
+                       ++ref, p0.x, p0.y, r1*mile, r1*mile, span>180.0, p1.x, p1.y, col, (20 * symbolScale[zoom]));
+              else
+                printf("<path id=\"%d\" d=\"M %g,%g A %g,%g,0,%d,0,%g,%g\" style=\"fill:none;stroke:%s;stroke-width:%g\"/>\n",
+                       ++ref, p1.x, p1.y, r1*mile, r1*mile, span>180.0, p0.x, p0.y, col, (20 * symbolScale[zoom]));
+              if (text != NULL) {
+                double chord = sqrt(pow((p0.x - p1.x), 2) + pow((p0.y - p1.y), 2));
+                if ((chord > (strlen(text) * textScale[zoom] * 50)) || ((b0 == 180.0) && (b1 == 180.0)))
+                  drawLineText(item, text, style, offset, dy, ref);
+              }
+            } else if (arc == 1) {
+              printf("<path d=\"M %g,%g A %g,%g,0,%d,1,%g,%g\" style=\"fill:none;stroke:%s;stroke-width:%g;stroke-opacity:0.5;stroke-dasharray:%g\"/>\n",
+                     p0.x, p0.y, r1*mile, r1*mile, span>180.0, p1.x, p1.y, col, (10 * symbolScale[zoom]), (30 * symbolScale[zoom]));
+            }
+            if ((arc == 0) && (att->val.val.l->next != NULL)) {
+              char *col = light_colours[att->val.val.l->next->val];
+              double r2 = r1 - (20 * symbolScale[zoom]/mile);
+              XY_t p2 = radial(pos, r2, b0);
+              XY_t p3 = radial(pos, r2, b1);
+              printf("<path d=\"M %g,%g A %g,%g,0,%d,1,%g,%g\" style=\"fill:none;stroke:%s;stroke-width:%g\"/>\n",
+                     p2.x, p2.y, r1*mile, r1*mile, span>180.0, p3.x, p3.y, col, (20 * symbolScale[zoom]));
+            }
+            b0 = b1;
+            if (b0 == end) break;
+          }
+          if (!((start == 180.0) && (end == 180.0)))
+            printf("<path d=\"M %g,%g L %g,%g\" style=\"fill:none;stroke:#808080;stroke-width:%g;stroke-dasharray:%g\"/>\n",
+                   pos.x, pos.y, p1.x, p1.y, (4 * symbolScale[zoom]), (20 * symbolScale[zoom]));
+          free(radstr);
+        }
+      }
+    }
+  }
+}
+char *charString(Item_t *item, char *type, int idx) {
+  strcpy(string1, "");
+  Att_t *att = NULL;
+  Obj_t *obj = getObj(item, enumType(type), idx);
+  switch (enumType(type)) {
+    case CGUSTA:
+      strcpy(string1, "CG");
+      if ((obj != NULL) && (att = getAtt(obj, COMCHA)) != NULL)
+        sprintf(strchr(string1, 0), " Ch.%s", stringValue(att->val));
+      break;
+    case FOGSIG:
+      if (obj != NULL) {
+        if ((att = getAtt(obj, CATFOG)) != NULL)
+          strcat(string1, fog_signals[att->val.val.e]);
+        if ((att = getAtt(obj, SIGGRP)) != NULL)
+          sprintf(strchr(string1, 0), "(%s)", stringValue(att->val));
+        else 
+          strcat(string1, " ");
+        if ((att = getAtt(obj, SIGPER)) != NULL)
+          sprintf(strchr(string1, 0), "%ss ", stringValue(att->val));
+        if ((att = getAtt(obj, VALMXR)) != NULL)
+          sprintf(strchr(string1, 0), "%sM", stringValue(att->val));
+      }
+      break;
+    case SISTAT:
+      strcpy(string1, "SS");
+      if (obj != NULL) {
+        if ((att = getAtt(obj, CATSIT)) != NULL)
+          strcat(string1, sit_map[att->val.val.l->val]);
+        if ((att = getAtt(obj, COMCHA)) != NULL)
+          sprintf(strchr(string1, 0), "\nCh.%s", stringValue(att->val));
+      }
+      break;
+    case SISTAW:
+      strcpy(string1, "SS");
+      if (obj != NULL) {
+        if ((att = getAtt(obj, CATSIW)) != NULL)
+          strcat(string1, siw_map[att->val.val.l->val]);
+        if ((att = getAtt(obj, COMCHA)) != NULL)
+          sprintf(strchr(string1, 0), "\nCh.%s", stringValue(att->val));
+      }
+      break;
+    case LIGHTS:
+    {
+      int secmax = countObjects(item, "light");
+      if ((idx == 0) && (secmax > 0)) {
+        struct SECT {
+          struct SECT *next;
+          int dir;
+          LitCHR_t chr;
+          ColCOL_t col;
+          ColCOL_t alt;
+          char *grp;
+          double per;
+          double rng;
+        } *lights = NULL;
+        for (int i = secmax; i > 0; i--) {
+          struct SECT *tmp = calloc(1, sizeof(struct SECT));
+          tmp->next = lights;
+          lights = tmp;
+          obj = getObj(item, LIGHTS, i);
+          if ((att = getAtt(obj, CATLIT)) != NULL) {
+            lights->dir = testAtt(att, LIT_DIR);
+          }
+          if ((att = getAtt(obj, LITCHR)) != NULL) {
+            lights->chr = att->val.val.e;
+            switch (lights->chr) {
+              case CHR_AL:
+                lights->chr = CHR_F;
+                break;
+              case CHR_ALOC:
+                lights->chr = CHR_OC;
+                break;
+              case CHR_ALLFL:
+                lights->chr = CHR_LFL;
+                break;
+              case CHR_ALFL:
+                lights->chr = CHR_FL;
+                break;
+              case CHR_ALFFL:
+                lights->chr = CHR_FFL;
+                break;
+              default:
+                break;
+            }
+          }
+          if ((att = getAtt(obj, SIGGRP)) != NULL) {
+            lights->grp = att->val.val.a;
+          } else {
+            lights->grp = "";
+          }
+          if ((att = getAtt(obj, SIGPER)) != NULL) {
+            lights->per = att->val.val.f;
+          }
+          if ((att = getAtt(obj, VALNMR)) != NULL) {
+            lights->rng = att->val.val.f;
+          }
+          if ((att = getAtt(obj, COLOUR)) != NULL) {
+            lights->col = att->val.val.l->val;
+            if (att->val.val.l->next != NULL)
+              lights->alt = att->val.val.l->next->val;
+          }
+        }
+        struct COLRNG {
+          int col;
+          double rng;
+        } colrng[14];
+        while (lights != NULL) {
+          strcpy(string2, "");
+          bzero(colrng, 14*sizeof(struct COLRNG));
+          colrng[lights->col].col = 1;
+          colrng[lights->col].rng = lights->rng;
+          struct SECT *this = lights;
+          struct SECT *next = lights->next;
+          while (next != NULL) {
+            if ((this->dir == next->dir) && (this->chr == next->chr) &&
+                (strcmp(this->grp, next->grp) == 0) && (this->per == next->per)) {
+              colrng[next->col].col = 1;
+              if (next->rng > colrng[next->col].rng)
+                colrng[next->col].rng = next->rng;
+              struct SECT *tmp = lights;
+              while (tmp->next != next) tmp = tmp->next;
+              tmp->next = next->next;
+              free(next);
+              next = tmp->next;
+            } else {
+              next = next->next;
+            }
+          }
+          if (this->chr != CHR_UNKN) {
+            if (this->dir) strcpy(string2, "Dir.");
+            strcat(string2, light_characters[this->chr]);
+            if (strcmp(this->grp, "") != 0) {
+              if (this->grp[0] == '(')
+                sprintf(strchr(string2, 0), "%s", this->grp);
+              else
+                sprintf(strchr(string2, 0), "(%s)", this->grp);
+            } else {
+              if (strlen(string2) > 0) strcat(string2, ".");
+            }
+            int n = 0;
+            for (int i = 0; i < 14; i++) if (colrng[i].col) n++;
+            double max = 0.0;
+            for (int i = 0; i < 14; i++) if (colrng[i].col && (colrng[i].rng > max)) max = colrng[i].rng;
+            double min = max;
+            for (int i = 0; i < 14; i++) if (colrng[i].col && (colrng[i].rng > 0.0) && (colrng[i].rng < min)) min = colrng[i].rng;
+            if (min == max) {
+              for (int i = 0; i < 14; i++) if (colrng[i].col) strcat(string2, light_letters[i]);
+            } else {
+              for (int i = 0; i < 14; i++) if (colrng[i].col && (colrng[i].rng == max)) strcat(string2, light_letters[i]);
+              for (int i = 0; i < 14; i++) if (colrng[i].col && (colrng[i].rng < max) && (colrng[i].rng > min)) strcat(string2, light_letters[i]);
+              for (int i = 0; i < 14; i++) if (colrng[i].col && colrng[i].rng == min) strcat(string2, light_letters[i]);
+            }
+            strcat(string2, ".");
+            if (this->per > 0.0) sprintf(strchr(string2, 0), "%gs", this->per);
+            if (max > 0.0) {
+              sprintf(strchr(string2, 0), "%g", max);
+              if (min != max) {
+                if (n == 2) strcat(string2, "/");
+                else if (n > 2) strcat(string2, "-");
+                if (min < max) sprintf(strchr(string2, 0), "%g", min);
+              }
+              strcat(string2, "M");
+            }
+            if (strlen(string1) > 0) strcat(string1, "\n");
+            strcat(string1, string2);
+          }
+          lights = this->next;
+          free(this);
+          this = lights;
+        }
+      } else {
+        if ((att = getAtt(obj, CATLIT)) != NULL) {
+          if (testAtt(att, LIT_DIR))
+            strcat(string1, "Dir");
+        }
+        if ((att = getAtt(obj, MLTYLT)) != NULL)
+          sprintf(strchr(string1, 0), "%s", stringValue(att->val));
+        if ((att = getAtt(obj, LITCHR)) != NULL) {
+          char *chrstr = strdup(stringValue(att->val));
+          Att_t *grp = getAtt(obj, SIGGRP);
+          if (grp != NULL) {
+            char *strgrp = strdup(stringValue(grp->val));
+            char *grpstr = strtok(strgrp, "()");
+            switch (att->val.val.e) {
+              case CHR_QLFL:
+                sprintf(strchr(string1, 0), "Q(%s)+LFl", grpstr);
+                break;
+              case CHR_VQLFL:
+                sprintf(strchr(string1, 0), "VQ(%s)+LFl", grpstr);
+                break;
+              case CHR_UQLFL:
+                sprintf(strchr(string1, 0), "UQ(%s)+LFl", grpstr);
+                break;
+              default:
+                sprintf(strchr(string1, 0), "%s(%s)", chrstr, grpstr);
+                break;
+            }
+            free(strgrp);
+          } else {
+            sprintf(strchr(string1, 0), "%s", chrstr);
+          }
+          free(chrstr);
+        }
+        if ((att = getAtt(obj, COLOUR)) != NULL) {
+          int n = countValues(att);
+          if (!((n == 1) && (idx == 0) && (testAtt(att, COL_WHT)))) {
+            if ((strlen(string1) > 0) && ((string1[strlen(string1)-1] != ')')))
+              strcat(string1, ".");
+            Lst_t *lst = att->val.val.l;
+            while (lst != NULL) {
+              strcat(string1, light_letters[lst->val]);
+              lst = lst->next;
+            }
+          }
+        }
+        if ((idx == 0) && (att = getAtt(obj, CATLIT)) != NULL) {
+          if (testAtt(att, LIT_VERT))
+            strcat(string1, "(vert)");
+          if (testAtt(att, LIT_HORI))
+            strcat(string1, "(hor)");
+        }
+        if ((strlen(string1) > 0) &&
+            ((getAtt(obj, SIGPER) != NULL) ||
+             (getAtt(obj, HEIGHT) != NULL) ||
+             (getAtt(obj, VALMXR) != NULL)) &&
+            (string1[strlen(string1)-1] != ')'))
+          strcat(string1, ".");
+        if ((att = getAtt(obj, SIGPER)) != NULL)
+          sprintf(strchr(string1, 0), "%ss", stringValue(att->val));
+        if ((idx == 0) && (item->objs.obj != LITMIN)) {
+          if ((att = getAtt(obj, HEIGHT)) != NULL)
+            sprintf(strchr(string1, 0), "%sm", stringValue(att->val));
+          if ((att = getAtt(obj, VALNMR)) != NULL)
+            sprintf(strchr(string1, 0), "%sM", stringValue(att->val));
+        }
+        if ((idx == 0) && (att = getAtt(obj, CATLIT)) != NULL) {
+          if (testAtt(att, LIT_FRNT))
+            strcat(string1, "(Front)");
+          if (testAtt(att, LIT_REAR))
+            strcat(string1, "(Rear)");
+          if (testAtt(att, LIT_UPPR))
+            strcat(string1, "(Upper)");
+          if (testAtt(att, LIT_LOWR))
+            strcat(string1, "(Lower)");
+        }
+      }
+    }
+      break;
+    default: break;
+ }
+  return string1;
+}
+*/
+	
+}
Index: applications/editors/josm/plugins/smed2/src/s57/S57dat.java
===================================================================
--- applications/editors/josm/plugins/smed2/src/s57/S57dat.java	(revision 30149)
+++ applications/editors/josm/plugins/smed2/src/s57/S57dat.java	(revision 30150)
@@ -1,28 +1,5 @@
 package s57;
 
-import static org.openstreetmap.josm.tools.I18n.tr;
-
-import java.awt.Dimension;
-import java.awt.event.ActionListener;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-
-import javax.swing.*;
-
-import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.gui.layer.OsmDataLayer;
-import org.openstreetmap.josm.data.osm.DataSet;
-
-import panels.PanelMain;
-
-import smed2.Smed2Action;
-
-import s57.S57obj;
-import s57.S57att;
-import s57.S57val;
-
-public class S57dat extends JPanel {
+public class S57dat {
 	
 	private enum S57field { DSID, DSSI, DSPM, DSPR, DSRC, DSHT, DSAC, CATD, CATX, DDDF, DDDR, DDDI, DDOM, DDRF, DDSI, DDSC,
@@ -64,111 +41,3 @@
 	private enum S57ct2d { YCOO, XCOO }
 	
-	private File importFile;
-	private FileInputStream inp;
-	private File exportFile;
-	private FileOutputStream outp;
-	private OsmDataLayer layer;
-	private DataSet data;
-
-	private JButton importButton;
-	private ActionListener alImport = new ActionListener() {
-		public void actionPerformed(java.awt.event.ActionEvent e) {
-			if (e.getSource() == importButton) {
-				readFile();
-		    Smed2Action.panelS57.setVisible(true);
-		    Smed2Action.panelMain.setVisible(false);
-        PanelMain.messageBar.setText("File imported");
-      }
-		}
-	};
-	private JButton exportButton;
-	private ActionListener alExport = new ActionListener() {
-		public void actionPerformed(java.awt.event.ActionEvent e) {
-			if (e.getSource() == exportButton) {
-				writeFile();
-		    Smed2Action.panelS57.setVisible(true);
-		    Smed2Action.panelMain.setVisible(false);
-        PanelMain.messageBar.setText("File exported");
-      }
-		}
-	};
-	private JButton cancelButton;
-	private ActionListener alCancel = new ActionListener() {
-		public void actionPerformed(java.awt.event.ActionEvent e) {
-			if (e.getSource() == cancelButton) {
-				try {
-					inp.close();
-				} catch (IOException e1) {}
-		    Smed2Action.panelS57.setVisible(false);
-		    Smed2Action.panelMain.setVisible(true);
-        PanelMain.messageBar.setText("Operation cancelled");
-      }
-		}
-	};
-
-	public S57dat() {
-		
-		setLayout(null);
-		setSize(new Dimension(480, 480));
-		cancelButton = new JButton();
-		cancelButton.setBounds(250, 430, 100, 20);
-		cancelButton.setText(tr("Cancel"));
-		cancelButton.addActionListener(alCancel);
-		add(cancelButton);
-		importButton = new JButton();
-		importButton.setBounds(370, 430, 100, 20);
-		importButton.setText(tr("Import"));
-		importButton.addActionListener(alImport);
-		add(importButton);
-		exportButton = new JButton();
-		exportButton.setBounds(370, 430, 100, 20);
-		exportButton.setText(tr("Export"));
-		exportButton.addActionListener(alExport);
-		add(exportButton);
-	}
-
-	public void startImport(File file) {
-		importFile = file;
-		try {
-			inp = new FileInputStream(file);
-		} catch (IOException e) {
-      PanelMain.messageBar.setText("Failed to open file");
-			return;
-		}
-    Smed2Action.panelMain.setVisible(false);
-    Smed2Action.panelS57.setVisible(true);
-	}
-
-	public void startExport(File file) {
-		exportFile = file;
-		try {
-			outp = new FileOutputStream(file);
-		} catch (IOException e) {
-      PanelMain.messageBar.setText("Failed to open file");
-			return;
-		}
-    Smed2Action.panelMain.setVisible(false);
-    Smed2Action.panelS57.setVisible(true);
-	}
-
-	private void writeFile() {
-	}
-
-	private void readFile() {
-		// Read & convert file.
-			// read record at a time
-				// read leader
-				// if DDR, build conversion tables
-				// if DR, build data structures
-		// Extract list of features & present on chooser panel
-	}
-
-	private void importData() {
-		data = new DataSet();
-		// Transfer selected features
-		layer = new OsmDataLayer(data, importFile.getName(), null);
-		layer.setUploadDiscouraged(true);
-		Main.main.addLayer(layer);
-
-	}
 }
Index: applications/editors/josm/plugins/smed2/src/s57/S57map.java
===================================================================
--- applications/editors/josm/plugins/smed2/src/s57/S57map.java	(revision 30150)
+++ applications/editors/josm/plugins/smed2/src/s57/S57map.java	(revision 30150)
@@ -0,0 +1,562 @@
+/* Copyright 2013 Malcolm Herring
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3 of the License.
+ *
+ * For a copy of the GNU General Public License, see <http://www.gnu.org/licenses/>.
+ */
+
+package s57;
+
+import java.util.*;
+
+import s57.S57att;
+import s57.S57att.*;
+import s57.S57obj;
+import s57.S57obj.*;
+import s57.S57val;
+import s57.S57val.*;
+
+public class S57map {
+
+	public enum Nflag {
+		ANON,	// Edge inner nodes
+		ISOL,	// Node not part of Edge
+		CONN	// Edge first and last nodes
+	}
+
+	public class Snode {	// All coordinates in map
+		public double lat;	// Latitude
+		public double lon;	// Longitude
+		public Nflag flg;		// Role of node
+
+		public Snode() {
+			flg = Nflag.ANON;
+			lat = 0;
+			lon = 0;
+		}
+		public Snode(double ilat, double ilon) {
+			flg = Nflag.ANON;
+			lat = ilat;
+			lon = ilon;
+		}
+		public Snode(double ilat, double ilon, Nflag iflg) {
+			lat = ilat;
+			lon = ilon;
+			flg = iflg;
+		}
+	}
+
+	public class Edge {		// A polyline segment
+		public long first;	// First CONN node
+		public long last;		// Last CONN node
+		public ArrayList<Long> nodes; // Inner ANON nodes
+
+		public Edge() {
+			first = 0;
+			last = 0;
+			nodes = new ArrayList<Long>();
+		}
+	}
+
+	public class Side {	// An edge as used in a line or area feature
+		Edge edge;				// Side is formed by this Edge...
+		boolean forward;	// ... in this direction
+
+		public Side(Edge iedge, boolean ifwd) {
+			edge = iedge;
+			forward = ifwd;
+		}
+	}
+
+	public class Bound { // A single closed area
+		public boolean outer;		// Role
+		ArrayList<Side> sides;	// Sides that make up this area
+
+		public Bound() {
+			outer = true;
+			sides = new ArrayList<Side>();
+		}
+		public Bound(Side iside, boolean irole) {
+			outer = irole;
+			sides = new ArrayList<Side>();
+			sides.add(iside);
+		}
+	}
+
+	public class Area extends ArrayList<Bound> {	// The collection of bounds for an area.
+		public Area() {
+			super();
+		}
+	}
+
+	public class AttItem {
+		public Conv conv;
+		public Object val;
+
+		AttItem(Conv iconv, Object ival) {
+			conv = iconv;
+			val = ival;
+		}
+	}
+
+	public class AttMap extends EnumMap<Att, AttItem> {
+		public AttMap() {
+			super(Att.class);
+		}
+	}
+
+	public class ObjTab extends HashMap<Integer, AttMap> {
+		public ObjTab() {
+			super();
+		}
+	}
+
+	public class ObjMap extends EnumMap<Obj, ObjTab> {
+		public ObjMap() {
+			super(Obj.class);
+		}
+	}
+
+	public class NodeTab extends HashMap<Long, Snode> {
+		public NodeTab() {
+			super();
+		}
+	}
+
+	public class EdgeTab extends HashMap<Long, Edge> {
+		public EdgeTab() {
+			super();
+		}
+	}
+
+	public class AreaTab extends HashMap<Long, Area> {
+		public AreaTab() {
+			super();
+		}
+	}
+
+	public class FtrMap extends EnumMap<Obj, ArrayList<Feature>> {
+		public FtrMap() {
+			super(Obj.class);
+		}
+	}
+
+	public class FtrTab extends HashMap<Long, Feature> {
+		public FtrTab() {
+			super();
+		}
+	}
+
+	public enum Fflag {
+		UNKN, POINT, LINE, AREA
+	}
+
+	public class Feature {
+		public Fflag flag;
+		public long refs;
+		public Obj type;
+		public AttMap atts;
+		public ObjMap objs;
+		public double area;
+		public double length;
+		public Snode centre;
+
+		Feature() {
+			flag = Fflag.UNKN;
+			refs = 0;
+			type = Obj.UNKOBJ;
+			atts = new AttMap();
+			objs = new ObjMap();
+			area = 0;
+			length = 0;
+			centre = new Snode();
+		}
+	}
+
+	public NodeTab nodes;
+	public EdgeTab edges;
+	public AreaTab areas;
+
+	public FtrMap features;
+	public FtrTab index;
+
+	private Feature feature;
+	private Edge edge;
+	private ArrayList<Long> outers;
+	private ArrayList<Long> inners;
+
+	public class EdgeIterator {
+		Edge edge;
+		boolean forward;
+		ListIterator<Long> it;
+
+		public EdgeIterator(Edge iedge, boolean dir) {
+			edge = iedge;
+			forward = dir;
+			it = null;
+		}
+
+		public boolean hasNext() {
+			return (edge != null);
+		}
+
+		public Snode next() {
+			long ref = 0;
+			if (forward) {
+				if (it == null) {
+					ref = edge.first;
+					it = edge.nodes.listIterator();
+				} else {
+					if (it.hasNext()) {
+						ref = it.next();
+					} else {
+						ref = edge.last;
+						edge = null;
+					}
+				}
+			} else {
+				if (it == null) {
+					ref = edge.last;
+					it = edge.nodes.listIterator(edge.nodes.size());
+				} else {
+					if (it.hasPrevious()) {
+						ref = it.previous();
+					} else {
+						ref = edge.first;
+						edge = null;
+					}
+				}
+			}
+			return nodes.get(ref);
+		}
+	}
+
+	public class BoundIterator {
+		Bound bound;
+		Side side;
+		ListIterator<Side> sit;
+		EdgeIterator eit;
+
+		public BoundIterator(Bound ibound) {
+			bound = ibound;
+			sit = bound.sides.listIterator();
+			if (sit.hasNext()) {
+				side = sit.next();
+				eit = new EdgeIterator(side.edge, side.forward);
+			} else {
+				side = null;
+			}
+		}
+
+		public boolean hasNext() {
+			return (side != null) && ((sit.hasNext()) || (eit.hasNext()));
+		}
+
+		public Snode next() {
+			Snode node = null;
+			if (side != null) {
+				if (eit.hasNext()) {
+					node = eit.next();
+				} else {
+					if (sit.hasNext()) {
+						side = sit.next();
+						eit = new EdgeIterator(side.edge, side.forward);
+						node = eit.next();
+					} else {
+						side = null;
+					}
+				}
+			}
+			return node;
+		}
+	}
+	
+	public S57map() {
+		nodes = new NodeTab();
+		edges = new EdgeTab();
+		areas = new AreaTab();
+		feature = new Feature();
+		features = new FtrMap();
+		index = new FtrTab();
+	}
+
+	public void addNode(long id, double lat, double lon) {
+		nodes.put(id, new Snode(Math.toRadians(lat), Math.toRadians(lon)));
+		feature = new Feature();
+		feature.refs = id;
+		feature.flag = Fflag.POINT;
+		edge = null;
+	}
+
+	public void addEdge(long id) {
+		feature = new Feature();
+		feature.refs = id;
+		feature.flag = Fflag.LINE;
+		edge = new Edge();
+	}
+
+	public void addToEdge(long node) {
+		if (edge.first == 0) {
+			edge.first = node;
+		} else {
+			if (edge.last != 0) {
+				edge.nodes.add(edge.last);
+			}
+			edge.last = node;
+		}
+	}
+
+	public void addArea(long id) {
+		feature = new Feature();
+		feature.refs = id;
+		feature.flag = Fflag.AREA;
+		outers = new ArrayList<Long>();
+		inners = new ArrayList<Long>();
+		edge = null;
+	}
+
+	public void addToArea(long id, boolean outer) {
+		if (outer) {
+			outers.add(id);
+		} else {
+			inners.add(id);
+		}
+	}
+
+	public void addTag(String key, String val) {
+		String subkeys[] = key.split(":");
+		if ((subkeys.length > 1) && subkeys[0].equals("seamark")) {
+			Obj obj = S57obj.enumType(subkeys[1]);
+			if ((subkeys.length > 2) && (obj != Obj.UNKOBJ)) {
+				int idx = 0;
+				Att att = Att.UNKATT;
+				try {
+					idx = Integer.parseInt(subkeys[2]);
+					if (subkeys.length == 4) {
+						att = s57.S57att.enumAttribute(subkeys[3], obj);
+					}
+				} catch (Exception e) {
+					att = S57att.enumAttribute(subkeys[2], obj);
+				}
+				ObjTab items = feature.objs.get(obj);
+				if (items == null) {
+					items = new ObjTab();
+					feature.objs.put(obj, items);
+				}
+				AttMap atts = items.get(idx);
+				if (atts == null) {
+					atts = new AttMap();
+					items.put(idx, atts);
+				}
+				AttVal attval = S57val.convertValue(val, att);
+				if (attval.val != null)
+					atts.put(att, new AttItem(attval.conv, attval.val));
+			} else {
+				if (subkeys[1].equals("type")) {
+					feature.type = S57obj.enumType(val);
+					if (feature.objs.get(feature.type) == null) {
+						feature.objs.put(feature.type, new ObjTab());
+					}
+				} else {
+					Att att = S57att.enumAttribute(subkeys[1], Obj.UNKOBJ);
+					if (att != Att.UNKATT) {
+						AttVal attval = S57val.convertValue(val, att);
+						if (attval.val != null)
+							feature.atts.put(att, new AttItem(attval.conv, attval.val));
+					}
+				}
+			}
+		}
+	}
+
+	public void tagsDone(long id) {
+		switch (feature.flag) {
+		case POINT:
+			Snode node = nodes.get(id);
+			if (node.flg != Nflag.CONN) {
+				node.flg = Nflag.ISOL;
+			}
+			feature.length = 0;
+			feature.area = 0;
+			break;
+		case LINE:
+			edges.put(id, edge);
+			nodes.get(edge.first).flg = Nflag.CONN;
+			nodes.get(edge.last).flg = Nflag.CONN;
+			Bound ebound = (new Bound(new Side(edge, true), true));
+			feature.length = calcLength(ebound);
+			if (edge.first == edge.last) {
+				feature.flag = Fflag.AREA;
+				Area area = new Area();
+				area.add(ebound);
+				feature.area = calcArea(ebound);
+				areas.put(id, area);
+			} else {
+				feature.area = 0;
+			}
+			break;
+		case AREA:
+			Bound bound = null;
+			Area area = new Area();
+			ArrayList<Long> role = outers;
+			while (role != null) {
+				while (!role.isEmpty()) {
+					Edge edge = edges.get(role.remove(0));
+					long node1 = edge.first;
+					long node2 = edge.last;
+					bound = new Bound(new Side(edge, true), (role == outers));
+					if (node1 != node2) {
+						for (ListIterator<Long> it = role.listIterator(0); it.hasNext();) {
+							Edge nedge = edges.get(it.next());
+							if (nedge.first == node2) {
+								bound.sides.add(new Side(nedge, true));
+								it.remove();
+								if (nedge.last == node2)
+									break;
+							} else if (nedge.last == node2) {
+								bound.sides.add(new Side(nedge, false));
+								it.remove();
+								if (nedge.first == node2)
+									break;
+							}
+						}
+					}
+					area.add(bound);
+				}
+				if (role == outers) {
+					feature.length = calcLength(bound);
+					feature.area = calcArea(bound);
+					role = inners;
+				} else {
+					role = null;
+				}
+			}
+			areas.put(id, area);
+			break;
+		}
+		if ((feature.type != Obj.UNKOBJ) && !((edge != null) && (edge.last == 0))) {
+			index.put(id, feature);
+			if (features.get(feature.type) == null) {
+				features.put(feature.type, new ArrayList<Feature>());
+			}
+			feature.centre = findCentroid(feature);
+			features.get(feature.type).add(feature);
+		}
+	}
+
+	double signedArea(Bound bound) {
+		Snode node;
+		double lat, lon, llon, llat;
+		lat = lon = llon = llat = 0;
+		double sigma = 0;
+		BoundIterator it = new BoundIterator(bound);
+		while (it.hasNext()) {
+			llon = lon;
+			llat = lat;
+			node = it.next();
+			lat = node.lat;
+			lon = node.lon;
+			sigma += (lon * Math.sin(llat)) - (llon * Math.sin(lat));
+		}
+		return sigma / 2.0;
+	}
+
+	public boolean handOfArea(Bound bound) {
+		return (signedArea(bound) < 0);
+	}
+
+	public double calcArea(Bound bound) {
+		return Math.abs(signedArea(bound)) * 3444 * 3444;
+	}
+
+	public double calcLength(Bound bound) {
+		Snode node;
+		double lat, lon, llon, llat;
+		lat = lon = llon = llat = 0;
+		double sigma = 0;
+		BoundIterator it = new BoundIterator(bound);
+		if (it.hasNext()) {
+			node = it.next();
+			lat = node.lat;
+			lon = node.lon;
+			while (it.hasNext()) {
+				llon = lon;
+				llat = lat;
+				node = it.next();
+				lat = node.lat;
+				lon = node.lon;
+				sigma += Math.acos(Math.sin(lat) * Math.sin(llat) + Math.cos(lat) * Math.cos(llat) * Math.cos(llon - lon));
+			}
+		}
+		return sigma * 3444;
+	}
+
+	public Snode findCentroid(Feature feature) {
+		double lat, lon, slat, slon, llat, llon;
+		llat = llon = lat = lon = slat = slon = 0;
+		double sarc = 0;
+		boolean first = true;
+		switch (feature.flag) {
+		case POINT:
+			return nodes.get(feature.refs);
+		case LINE:
+			Edge edge = edges.get(feature.refs);
+			EdgeIterator eit = new EdgeIterator(edge, true);
+			while (eit.hasNext()) {
+				Snode node = eit.next();
+				lat = node.lat;
+				lon = node.lon;
+				if (first) {
+					first = false;
+				} else {
+					sarc += (Math.acos(Math.cos(lon - llon) * Math.cos(lat - llat)));
+				}
+				llat = lat;
+				llon = lon;
+			}
+			double harc = sarc / 2;
+			sarc = 0;
+			first = true;
+			eit = new EdgeIterator(edge, true);
+			while (eit.hasNext()) {
+				Snode node = eit.next();
+				lat = node.lat;
+				lon = node.lon;
+				if (first) {
+					first = false;
+				} else {
+					sarc = (Math.acos(Math.cos(lon - llon) * Math.cos(lat - llat)));
+					if (sarc > harc)
+						break;
+				}
+				harc -= sarc;
+				llat = lat;
+				llon = lon;
+			}
+			return new Snode(llat + ((lat - llat) * harc / sarc), llon + ((lon - llon) * harc / sarc));
+		case AREA:
+			Bound bound = areas.get(feature.refs).get(0);
+			BoundIterator bit = new BoundIterator(bound);
+			while (bit.hasNext()) {
+				Snode node = bit.next();
+				lat = node.lat;
+				lon = node.lon;
+				if (first) {
+					first = false;
+				} else {
+					double arc = (Math.acos(Math.cos(lon - llon) * Math.cos(lat - llat)));
+					slat += ((lat + llat) / 2 * arc);
+					slon += ((lon + llon) / 2 * arc);
+					sarc += arc;
+				}
+				llon = lon;
+				llat = lat;
+			}
+			return new Snode((sarc > 0.0 ? slat / sarc : 0.0), (sarc > 0.0 ? slon / sarc : 0.0));
+		}
+		return null;
+	}
+
+}
Index: applications/editors/josm/plugins/smed2/src/smed2/MapImage.java
===================================================================
--- applications/editors/josm/plugins/smed2/src/smed2/MapImage.java	(revision 30149)
+++ applications/editors/josm/plugins/smed2/src/smed2/MapImage.java	(revision 30150)
@@ -28,7 +28,7 @@
 import org.openstreetmap.josm.gui.layer.ImageryLayer;
 
-import seamap.MapContext;
-import seamap.Renderer;
-import seamap.SeaMap.*;
+import render.MapContext;
+import render.Renderer;
+import s57.S57map.*;
 
 public class MapImage extends ImageryLayer implements ZoomChangeListener, MapContext {
Index: applications/editors/josm/plugins/smed2/src/smed2/Smed2Action.java
===================================================================
--- applications/editors/josm/plugins/smed2/src/smed2/Smed2Action.java	(revision 30149)
+++ applications/editors/josm/plugins/smed2/src/smed2/Smed2Action.java	(revision 30150)
@@ -30,6 +30,6 @@
 
 import s57.S57dat;
-import seamap.SeaMap;
-import seamap.SeaMap.*;
+import s57.S57map;
+import s57.S57map.*;
 
 import panels.PanelMain;
@@ -40,9 +40,8 @@
 	public static JFrame editFrame = null;
 	public static ShowFrame showFrame = null;
-	public static S57dat panelS57;
 	private boolean isOpen = false;
 	public static PanelMain panelMain = null;
 	public MapImage rendering;
-	public SeaMap map = null;
+	public S57map map = null;
 	public DataSet data = null;
 
@@ -122,7 +121,4 @@
 		panelMain = new PanelMain(this);
 		editFrame.add(panelMain);
-		panelS57 = new S57dat();
-		panelS57.setVisible(false);
-		editFrame.add(panelS57);
 
 		showFrame = new ShowFrame(tr("Seamark Inspector"));
@@ -206,5 +202,5 @@
 
 	void makeMap() {
-		map = new SeaMap();
+		map = new S57map();
 		if (data != null) {
 			for (Node node : data.getNodes()) {
Index: applications/editors/josm/plugins/smed2/src/symbols/Harbours.java
===================================================================
--- applications/editors/josm/plugins/smed2/src/symbols/Harbours.java	(revision 30149)
+++ applications/editors/josm/plugins/smed2/src/symbols/Harbours.java	(revision 30150)
@@ -14,5 +14,5 @@
 import java.awt.geom.*;
 
-import seamap.Renderer;
+import render.Rules;
 import symbols.Symbols.*;
 
@@ -39,5 +39,5 @@
 	public static final Symbol Anchorage = new Symbol();
 	static {
-		Anchorage.add(new Instr(Prim.SYMB, new Symbols.SubSymbol(Harbours.Anchor, 1.0, 0, 0, new Scheme(Renderer.Msymb), null)));
+		Anchorage.add(new Instr(Prim.SYMB, new Symbols.SubSymbol(Harbours.Anchor, 1.0, 0, 0, new Scheme(Rules.Msymb), null)));
 	}
 	public static final Symbol Bollard = new Symbol();
@@ -79,5 +79,5 @@
 	static {
 		DistanceU.add(new Instr(Prim.STRK, new BasicStroke(3, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER)));
-		DistanceU.add(new Instr(Prim.FILL, Renderer.Msymb));
+		DistanceU.add(new Instr(Prim.FILL, Rules.Msymb));
 		DistanceU.add(new Instr(Prim.ELPS, new Ellipse2D.Double(-11,-11,22,22)));
 	}
@@ -105,5 +105,5 @@
 	static {
 		Fishing.add(new Instr(Prim.STRK, new BasicStroke(15, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER)));
-		Fishing.add(new Instr(Prim.FILL, Renderer.Msymb));
+		Fishing.add(new Instr(Prim.FILL, Rules.Msymb));
 		Fishing.add(new Instr(Prim.EARC, new Arc2D.Double(-75,-75,150,150,15,140,Arc2D.OPEN)));
 		Fishing.add(new Instr(Prim.EARC, new Arc2D.Double(-75,-75,150,150,-15,-140,Arc2D.OPEN)));
@@ -117,7 +117,7 @@
 	static {
 		Harbour.add(new Instr(Prim.STRK, new BasicStroke(15, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER)));
-		Harbour.add(new Instr(Prim.FILL, Renderer.Msymb));
+		Harbour.add(new Instr(Prim.FILL, Rules.Msymb));
 		Harbour.add(new Instr(Prim.ELPS, new Ellipse2D.Double(-75,-75,150,150)));
-		Harbour.add(new Instr(Prim.SYMB, new Symbols.SubSymbol(Harbours.Anchor, 1.0, 0, 0, new Scheme(Renderer.Msymb), null)));
+		Harbour.add(new Instr(Prim.SYMB, new Symbols.SubSymbol(Harbours.Anchor, 1.0, 0, 0, new Scheme(Rules.Msymb), null)));
 	}
 	public static final Symbol HarbourMaster = new Symbol();
@@ -139,5 +139,5 @@
 	public static final Symbol LandingSteps = new Symbol();
 	static {
-		LandingSteps.add(new Instr(Prim.FILL, Renderer.Msymb));
+		LandingSteps.add(new Instr(Prim.FILL, Rules.Msymb));
 		Path2D.Double p = new Path2D.Double(); p.moveTo(-20,-10); p.lineTo(10,20); p.lineTo(20,20); p.lineTo(20,10);
 		p.lineTo(10,10); p.lineTo(10,0); p.lineTo(0,0); p.lineTo(0,-10); p.lineTo(-10,-10); p.lineTo(-10,-20); p.lineTo(-20,-20); p.closePath();
@@ -149,6 +149,6 @@
 	static {
 		Marina.add(new Instr(Prim.STRK, new BasicStroke(15, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER)));
-		Marina.add(new Instr(Prim.FILL, Renderer.Msymb));
-		Marina.add(new Instr(Prim.SYMB, new Symbols.SubSymbol(Harbours.Yacht, 1.0, 0, 0, new Scheme(Renderer.Msymb), null)));
+		Marina.add(new Instr(Prim.FILL, Rules.Msymb));
+		Marina.add(new Instr(Prim.SYMB, new Symbols.SubSymbol(Harbours.Yacht, 1.0, 0, 0, new Scheme(Rules.Msymb), null)));
 		Marina.add(new Instr(Prim.EARC, new Arc2D.Double(-80.0,-80.0,160.0,160.0,215.0,-250.0,Arc2D.OPEN)));
 	}
@@ -156,5 +156,5 @@
 	static {
 		MarinaNF.add(new Instr(Prim.STRK, new BasicStroke(15, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER)));
-		MarinaNF.add(new Instr(Prim.SYMB, new Symbols.SubSymbol(Harbours.Yacht, 1.0, 0, 0, new Scheme(Renderer.Msymb), null)));
+		MarinaNF.add(new Instr(Prim.SYMB, new Symbols.SubSymbol(Harbours.Yacht, 1.0, 0, 0, new Scheme(Rules.Msymb), null)));
 	}
 	public static final Symbol Pilot = new Symbol();
