Index: /applications/editors/josm/plugins/seachart/jchart/build.xml
===================================================================
--- /applications/editors/josm/plugins/seachart/jchart/build.xml	(revision 35388)
+++ /applications/editors/josm/plugins/seachart/jchart/build.xml	(revision 35388)
@@ -0,0 +1,28 @@
+<project name="jchart" default="dist" basedir=".">
+  <property name="src" location="src"/>
+  <property name="srcmain" location="../src"/>
+  <property name="build" location="build"/>
+  <property name="jarfile" location="./jchart.jar"/>
+
+  <target name="init">
+    <mkdir dir="${build}"/>
+  </target>
+
+  <target name="compile" depends="init" description="compile the source " >
+    <javac includeantruntime="false" sourcepath="${srcmain}" srcdir="${src}" destdir="${build}" encoding="UTF-8" />
+  </target>
+
+  <target name="dist" depends="compile" description="generate the distribution" >
+    <jar jarfile="${jarfile}" basedir="${build}" manifestencoding="UTF-8">
+      <manifest>
+        <attribute name="Main-Class" value="jchart.Jchart"/>
+        <attribute name="Class-Path" value="$jarfile"/>
+      </manifest>
+    </jar>
+  </target>
+
+  <target name="clean" description="clean up" >
+    <delete dir="${build}"/>
+    <delete file="${jarfile}"/>
+  </target>
+</project>
Index: /applications/editors/josm/plugins/seachart/jchart/src/jchart/Jchart.java
===================================================================
--- /applications/editors/josm/plugins/seachart/jchart/src/jchart/Jchart.java	(revision 35388)
+++ /applications/editors/josm/plugins/seachart/jchart/src/jchart/Jchart.java	(revision 35388)
@@ -0,0 +1,172 @@
+// License: GPL. For details, see LICENSE file.
+package jchart;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Graphics2D;
+import java.awt.Rectangle;
+import java.awt.geom.Point2D;
+import java.awt.image.BufferedImage;
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.Writer;
+
+import javax.imageio.ImageIO;
+
+import org.apache.batik.dom.GenericDOMImplementation;
+import org.apache.batik.svggen.SVGGraphics2D;
+import org.apache.batik.svggen.SVGGraphics2DIOException;
+import org.w3c.dom.DOMImplementation;
+import org.w3c.dom.Document;
+
+import render.ChartContext;
+import render.Renderer;
+import s57.S57map;
+import s57.S57map.Feature;
+import s57.S57map.GeomIterator;
+import s57.S57map.Pflag;
+import s57.S57map.Snode;
+import s57.S57obj.Obj;
+import symbols.Symbols;
+import s57.S57osm;
+
+/**
+ * @author Malcolm Herring
+ */
+public final class Jchart {
+    private Jchart() {
+        // Hide default constructor for utilities classes
+    }
+
+    static int zoom;
+    static Context context;
+    static S57map map;
+
+    static class Context implements ChartContext {
+
+        static double mile;
+        static double xscale;
+        static double yscale;
+
+        Context() {
+            double x = map.bounds.maxlon - map.bounds.minlon;
+            double y = (map.bounds.maxlat - map.bounds.minlat) / Math.cos((map.bounds.maxlat + map.bounds.minlat) / 2.0 );
+            if (x > y) {
+                xscale = (4096.0 * x / y) / x;
+                yscale = 4096.0 / (map.bounds.maxlat - map.bounds.minlat);
+            } else {
+                xscale = 4096.0 / x;
+                yscale = (4096.0 * y / x) / (map.bounds.maxlat - map.bounds.minlat);
+            }
+            mile = yscale / (Math.toDegrees(y) * 60.0);
+        }
+
+        @Override
+        public Point2D getPoint(Snode coord) {
+            double x = (coord.lon - map.bounds.minlon) * xscale;
+            double y = (map.bounds.maxlat - coord.lat) * yscale;
+            return new Point2D.Double(x, y);
+        }
+
+        @Override
+        public double mile(Feature feature) {
+            return mile;
+        }
+
+        @Override
+        public boolean clip() {
+            return true;
+        }
+
+        @Override
+        public Color background(S57map map) {
+            if (map.features.containsKey(Obj.COALNE)) {
+                for (Feature feature : map.features.get(Obj.COALNE)) {
+                    if (feature.geom.prim == Pflag.POINT) {
+                        break;
+                    }
+                    GeomIterator git = map.new GeomIterator(feature.geom);
+                    git.nextComp();
+                    while (git.hasEdge()) {
+                        git.nextEdge();
+                        while (git.hasNode()) {
+                            Snode node = git.next();
+                            if (node == null)
+                                continue;
+                            if ((node.lat >= map.bounds.minlat) && (node.lat <= map.bounds.maxlat)
+                                    && (node.lon >= map.bounds.minlon) && (node.lon <= map.bounds.maxlon)) {
+                                return Symbols.Bwater;
+                            }
+                        }
+                    }
+                }
+                return Symbols.Yland;
+            } else {
+                if (map.features.containsKey(Obj.ROADWY) || map.features.containsKey(Obj.RAILWY)
+                        || map.features.containsKey(Obj.LAKARE) || map.features.containsKey(Obj.RIVERS) || map.features.containsKey(Obj.CANALS)) {
+                    return Symbols.Yland;
+                } else {
+                    return Symbols.Bwater;
+                }
+            }
+        }
+
+        @Override
+        public RuleSet ruleset() {
+            return RuleSet.ALL;
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        if (args.length < 4) {
+            System.err.println("Usage: java -jar jrender.jar <osm data file> <zoom> <scale> <output image file>");
+            System.exit(-1);
+        }
+        BufferedReader in = new BufferedReader(new FileReader(args[0]));
+        zoom = Integer.parseInt(args[1]);
+        map = new S57map(true);
+        S57osm.OSMmap(in, map, false);
+        in.close();
+        context = new Context();
+        Point2D size = context.getPoint(new Snode(map.bounds.minlat, map.bounds.maxlon));
+        BufferedImage img = new BufferedImage((int)size.getX(), (int)size.getY(), BufferedImage.TYPE_INT_ARGB);
+        String[] ext = args[3].split("\\.");
+        if (ext[1].equalsIgnoreCase("png")) {
+            Graphics2D g2 = img.createGraphics();
+            Renderer.reRender(g2, new Rectangle((int) size.getX(), (int) size.getY()), zoom, Double.parseDouble(args[2]), map, context);
+            try {
+                ImageIO.write(img, "png", new File(args[3]));
+            } catch (Exception e) {
+                System.err.println("PNG write Exception");
+            }
+        } else if (ext[1].equalsIgnoreCase("svg")) {
+            DOMImplementation domImpl = GenericDOMImplementation.getDOMImplementation();
+            String svgNS = "http://www.w3.org/2000/svg";
+            Document document = domImpl.createDocument(svgNS, "svg", null);
+            SVGGraphics2D svgGenerator = new SVGGraphics2D(document);
+            svgGenerator.setSVGCanvasSize(new Dimension((int) size.getX(), (int) size.getY()));
+            Renderer.reRender(svgGenerator, new Rectangle((int) size.getX(), (int) size.getY()), 16, Double.parseDouble(args[2]), map, context);
+            boolean useCSS = true;
+            Writer out = null;
+            try {
+                out = new OutputStreamWriter(new FileOutputStream(args[3]), "UTF-8");
+            } catch (IOException e1) {
+                System.err.println("SVG file Exception");
+            }
+            try {
+                svgGenerator.stream(out, useCSS);
+            } catch (SVGGraphics2DIOException e) {
+                System.err.println("SVG write Exception");
+            }
+        } else {
+            System.err.println("Output file not PNG nor SVG");
+        }
+        System.exit(0);
+    }
+}
Index: /applications/editors/josm/plugins/seachart/src/render/Rules.java
===================================================================
--- /applications/editors/josm/plugins/seachart/src/render/Rules.java	(revision 35387)
+++ /applications/editors/josm/plugins/seachart/src/render/Rules.java	(revision 35388)
@@ -356,4 +356,5 @@
 			return false;
 		} catch (Exception e) {
+		    e.printStackTrace();
 			return true;
 		}
@@ -979,6 +980,5 @@
 					fncSym = Landmarks.RadioTV;
 				Renderer.symbol(catSym);
-				if (catSym != Landmarks.Spire)
-					Renderer.symbol(fncSym);
+				Renderer.symbol(fncSym);
 				break;
 			case SILTNK:
@@ -1049,5 +1049,6 @@
 			ArrayList<CatSCF> scfs = (ArrayList<CatSCF>) getAttList(Obj.SMCFAC, Att.CATSCF);
 			for (CatSCF scf : scfs) {
-				symbols.add(Facilities.Cats.get(scf));
+			    Symbol sym = Facilities.Cats.get(scf);
+			    if (sym != null) symbols.add(sym);
 			}
 			Renderer.cluster(symbols);
@@ -1059,5 +1060,9 @@
 			switch ((CatMOR) getAttEnum(feature.type, Att.CATMOR)) {
 			case MOR_DLPN:
-				Renderer.symbol(Harbours.Dolphin);
+			    if (feature.geom.prim == Pflag.AREA) {
+			        Renderer.lineVector(new LineStyle(Color.black, 4, Symbols.Yland));
+			    } else {
+			        Renderer.symbol(Harbours.Dolphin);
+			    }
 	            Signals.addSignals();
 				break;
@@ -1079,4 +1084,5 @@
 			        Renderer.symbol(Topmarks.TopMooring, Topmarks.BuoyDeltas.get(shape));
 		            Signals.addSignals();
+		            addName(15, new Font("Arial", Font.BOLD, 40), new Delta(Handle.BL, AffineTransform.getTranslateInstance(60, -50)));
 			    }
 				break;
@@ -1542,4 +1548,5 @@
             }
         }
+        addName(15, new Font("Arial", Font.BOLD, 40), new Delta(Handle.BL, AffineTransform.getTranslateInstance(50, 0)));
         if (Renderer.zoom >= 15) {
                 Renderer.labelText("V-AIS", new Font("Arial", Font.PLAIN, 40), Symbols.Msymb, new Delta(Handle.BC, AffineTransform.getTranslateInstance(0, 70)));
Index: /applications/editors/josm/plugins/seachart/src/render/Signals.java
===================================================================
--- /applications/editors/josm/plugins/seachart/src/render/Signals.java	(revision 35387)
+++ /applications/editors/josm/plugins/seachart/src/render/Signals.java	(revision 35388)
@@ -469,5 +469,5 @@
                     str += "." + df.format(atts.get(Att.SIGPER).val) + "s";
                 }
-                if ((s1 <= 360) && (s2 <= 360) && (s1 != s2))
+                if ((s1 < 360) && (s2 < 360) && (s1 != s2))
                     Renderer.lightSector(LightColours.get(col1), LightColours.get(col2), radius, s1, s2, dir, (Renderer.zoom >= 15) ? str : "");
             }
Index: /applications/editors/josm/plugins/seachart/src/s57/S57osm.java
===================================================================
--- /applications/editors/josm/plugins/seachart/src/s57/S57osm.java	(revision 35387)
+++ /applications/editors/josm/plugins/seachart/src/s57/S57osm.java	(revision 35388)
@@ -112,17 +112,17 @@
                     for (String token : ln.split("[ ]+")) {
                         if (token.matches("^minlat=.+")) {
-                            map.bounds.minlat = Math.toRadians(Double.parseDouble(token.split("[\"\']")[1]));
+                            map.bounds.minlat = Math.toRadians(Double.parseDouble(token.split("[\"]")[1]));
                             map.nodes.get(2L).lat = map.bounds.minlat;
                             map.nodes.get(3L).lat = map.bounds.minlat;
                         } else if (token.matches("^minlon=.+")) {
-                            map.bounds.minlon = Math.toRadians(Double.parseDouble(token.split("[\"\']")[1]));
+                            map.bounds.minlon = Math.toRadians(Double.parseDouble(token.split("[\"]")[1]));
                             map.nodes.get(1L).lon = map.bounds.minlon;
                             map.nodes.get(2L).lon = map.bounds.minlon;
                         } else if (token.matches("^maxlat=.+")) {
-                            map.bounds.maxlat = Math.toRadians(Double.parseDouble(token.split("[\"\']")[1]));
+                            map.bounds.maxlat = Math.toRadians(Double.parseDouble(token.split("[\"]")[1]));
                             map.nodes.get(1L).lat = map.bounds.maxlat;
                             map.nodes.get(4L).lat = map.bounds.maxlat;
                         } else if (token.matches("^maxlon=.+")) {
-                            map.bounds.maxlon = Math.toRadians(Double.parseDouble(token.split("[\"\']")[1]));
+                            map.bounds.maxlon = Math.toRadians(Double.parseDouble(token.split("[\"]")[1]));
                             map.nodes.get(3L).lon = map.bounds.maxlon;
                             map.nodes.get(4L).lon = map.bounds.maxlon;
@@ -133,7 +133,7 @@
                         k = v = "";
                         String[] token = ln.split("k=");
-                        k = token[1].split("[\"\']")[1];
+                        k = token[1].split("[\"]")[1];
                         token = token[1].split("v=");
-                        v = token[1].split("[\"\']")[1];
+                        v = token[1].split("[\"]")[1];
                         if (!k.isEmpty() && !v.isEmpty()) {
                             map.addTag(k, v);
@@ -148,9 +148,9 @@
                         for (String token : ln.split("[ ]+")) {
                             if (token.matches("^id=.+")) {
-                                id = Long.parseLong(token.split("[\"\']")[1]);
+                                id = Long.parseLong(token.split("[\"]")[1]);
                             } else if (token.matches("^lat=.+")) {
-                                lat = Double.parseDouble(token.split("[\"\']")[1]);
+                                lat = Double.parseDouble(token.split("[\"]")[1]);
                             } else if (token.matches("^lon=.+")) {
-                                lon = Double.parseDouble(token.split("[\"\']")[1]);
+                                lon = Double.parseDouble(token.split("[\"]")[1]);
                             }
                         }
@@ -166,5 +166,5 @@
                             for (String token : ln.split("[ ]+")) {
                                 if (token.matches("^ref=.+")) {
-                                    ref = Long.parseLong(token.split("[\"\']")[1]);
+                                    ref = Long.parseLong(token.split("[\"]")[1]);
                                 }
                             }
@@ -182,5 +182,5 @@
                         for (String token : ln.split("[ ]+")) {
                             if (token.matches("^id=.+")) {
-                                id = Long.parseLong(token.split("[\"\']")[1]);
+                                id = Long.parseLong(token.split("[\"]")[1]);
                             }
                         }
@@ -202,11 +202,11 @@
                             for (String token : ln.split("[ ]+")) {
                                 if (token.matches("^ref=.+")) {
-                                    ref = Long.parseLong(token.split("[\"\']")[1]);
+                                    ref = Long.parseLong(token.split("[\"]")[1]);
                                 } else if (token.matches("^type=.+")) {
-                                    type = token.split("[\"\']")[1];
+                                    type = token.split("[\"]")[1];
                                 } else if (token.matches("^role=.+")) {
-                                    String[] str = token.split("[\"\']");
+                                    String[] str = token.split("[\"]");
                                     if (str.length > 1) {
-                                        role = token.split("[\"\']")[1];
+                                        role = token.split("[\"]")[1];
                                     }
                                 }
@@ -222,5 +222,5 @@
                         for (String token : ln.split("[ ]+")) {
                             if (token.matches("^id=.+")) {
-                                id = Long.parseLong(token.split("[\"\']")[1]);
+                                id = Long.parseLong(token.split("[\"]")[1]);
                             }
                         }
Index: /applications/editors/josm/plugins/seachart/src/s57/S57val.java
===================================================================
--- /applications/editors/josm/plugins/seachart/src/s57/S57val.java	(revision 35387)
+++ /applications/editors/josm/plugins/seachart/src/s57/S57val.java	(revision 35388)
@@ -661,5 +661,5 @@
         SPM_NOTC, SPM_TSS, SPM_NANC, SPM_NBRT, SPM_NOTK, SPM_NTWT, SPM_RWAK, SPM_SPDL, SPM_STOP, SPM_WRNG, SPM_SSSN, SPM_RVCL, SPM_MVDT, SPM_RHCL, SPM_SCNT, SPM_BRTH, SPM_OHPC, SPM_CHEG, SPM_TELE, SPM_FCRS,
         SPM_MTRL, SPM_PLIN, SPM_ANCH, SPM_CLRG, SPM_CTRL, SPM_DIVG, SPM_RBCN, SPM_FGND, SPM_YCHT, SPM_HPRT, SPM_GPS, SPM_SLDG, SPM_NENT, SPM_WRKP, SPM_UKPP, SPM_WELH, SPM_CHSP, SPM_MFRM, SPM_AREF,
-        SPM_ICE, SPM_NATR, SPM_FAD, SPM_WREK, SPM_CUST, SPM_CSWY, SPM_WAVR, }
+        SPM_ICE, SPM_NATR, SPM_FAD, SPM_WREK, SPM_CUST, SPM_CSWY, SPM_WAVR, SPM_NJET }
 
     private static final EnumMap<CatSPM, S57enum> Catspm = new EnumMap<>(CatSPM.class); static {
@@ -686,5 +686,5 @@
         Catspm.put(CatSPM.SPM_AREF, new S57enum(56, "artificial_reef")); Catspm.put(CatSPM.SPM_ICE, new S57enum(57, "ice")); Catspm.put(CatSPM.SPM_NATR, new S57enum(58, "nature_reserve"));
         Catspm.put(CatSPM.SPM_FAD, new S57enum(59, "fish_aggregator")); Catspm.put(CatSPM.SPM_WREK, new S57enum(60, "wreck")); Catspm.put(CatSPM.SPM_CUST, new S57enum(61, "customs"));
-        Catspm.put(CatSPM.SPM_CSWY, new S57enum(62, "causeway")); Catspm.put(CatSPM.SPM_WAVR, new S57enum(63, "wave_recorder"));
+        Catspm.put(CatSPM.SPM_CSWY, new S57enum(62, "causeway")); Catspm.put(CatSPM.SPM_WAVR, new S57enum(63, "wave_recorder")); Catspm.put(CatSPM.SPM_NJET, new S57enum(64, "no_jetski"));
     }
 
