Index: applications/editors/josm/plugins/smed2/src/s57/S57dat.java
===================================================================
--- applications/editors/josm/plugins/smed2/src/s57/S57dat.java	(revision 30283)
+++ applications/editors/josm/plugins/smed2/src/s57/S57dat.java	(revision 30284)
@@ -134,4 +134,30 @@
 	}
 
+	private static final EnumMap<S57field, String> FldStr = new EnumMap<S57field, String>(S57field.class);
+	static {
+		FldStr.put(S57field.I8RI, "0001");
+		FldStr.put(S57field.DSID, "DSID"); FldStr.put(S57field.DSSI, "DSSI"); FldStr.put(S57field.DSPM, "DSPM"); FldStr.put(S57field.DSPR, "DSPR");
+		FldStr.put(S57field.DSRC, "DSRC"); FldStr.put(S57field.DSHT, "DSHT"); FldStr.put(S57field.DSAC, "DSAC"); FldStr.put(S57field.CATD, "CATD");
+		FldStr.put(S57field.CATX, "CATX"); FldStr.put(S57field.DDDF, "DDDF"); FldStr.put(S57field.DDDR, "DDDR"); FldStr.put(S57field.DDDI, "DDDI");
+		FldStr.put(S57field.DDOM, "DDOM"); FldStr.put(S57field.DDRF, "DDRF"); FldStr.put(S57field.DDSI, "DDSI"); FldStr.put(S57field.DDSC, "DDSC");
+		FldStr.put(S57field.FRID, "FRID"); FldStr.put(S57field.FOID, "FOID"); FldStr.put(S57field.ATTF, "ATTF"); FldStr.put(S57field.NATF, "NATF");
+		FldStr.put(S57field.FFPC, "FFPC"); FldStr.put(S57field.FFPT, "FFPT"); FldStr.put(S57field.FFPC, "FFPC"); FldStr.put(S57field.FSPT, "FSPT");
+		FldStr.put(S57field.VRID, "VRID"); FldStr.put(S57field.ATTV, "ATTV"); FldStr.put(S57field.VRPC, "VRPC"); FldStr.put(S57field.VRPT, "VRPT");
+		FldStr.put(S57field.SGCC, "SGCC"); FldStr.put(S57field.SG2D, "SG2D"); FldStr.put(S57field.SG3D, "SG3D"); FldStr.put(S57field.ARCC, "ARCC");
+		FldStr.put(S57field.AR2D, "AR2D"); FldStr.put(S57field.EL2D, "EL2D"); FldStr.put(S57field.CT2D, "CT2D"); 
+	}
+	
+	public static String stringField (S57field field) {
+		return FldStr.get(field);
+	}
+	
+	public static S57field enumField (String field) {
+		for (S57field fld : FldStr.keySet()) {
+			if (FldStr.get(fld).equals(field))
+				return fld;
+		}
+		return null;
+	}
+
 	private static byte[] buffer;
 	private static int offset;
Index: applications/editors/josm/plugins/smed2/src/s57/S57dec.java
===================================================================
--- applications/editors/josm/plugins/smed2/src/s57/S57dec.java	(revision 30284)
+++ applications/editors/josm/plugins/smed2/src/s57/S57dec.java	(revision 30284)
@@ -0,0 +1,202 @@
+/* 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.io.FileInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+
+import s57.S57dat.*;
+import s57.S57map.*;
+import s57.S57obj.*;
+
+public class S57dec {
+
+	public static MapBounds decodeFile(FileInputStream in, ArrayList<Obj> types, S57map map) throws IOException {
+		S57dat.rnum = 0;
+		byte[] leader = new byte[24];
+		boolean ddr = false;
+		int length;
+		int fields;
+		int mapfl, mapfp, mapts, entry;
+		String tag;
+		int len;
+		int pos;
+		boolean inFeature = false;
+
+		double comf = 1;
+		double somf = 1;
+		long name = 0;
+		S57map.Nflag nflag = Nflag.ANON;
+		S57map.Pflag pflag = S57map.Pflag.NOSP;
+		long objl = 0;
+		MapBounds bounds = map.new MapBounds();
+		
+		while (in.read(leader) == 24) {
+			length = Integer.parseInt(new String(leader, 0, 5)) - 24;
+			ddr = (leader[6] == 'L');
+			fields = Integer.parseInt(new String(leader, 12, 5)) - 24;
+			mapfl = leader[20] - '0';
+			mapfp = leader[21] - '0';
+			mapts = leader[23] - '0';
+			entry = mapfl + mapfp + mapts;
+			byte[] record = new byte[length];
+			if (in.read(record) != length)
+				break;
+			for (int idx = 0; idx < fields-1; idx += entry) {
+				tag = new String(record, idx, mapts);
+				len = Integer.parseInt(new String(record, idx+mapts, mapfl));
+				pos = Integer.parseInt(new String(record, idx+mapts+mapfl, mapfp));
+				if (!ddr) {
+					switch (S57dat.enumField(tag)) {
+					case I8RI:
+						int i8rn = ((Long) S57dat.getSubf(record, fields + pos, S57field.I8RI, S57subf.I8RN)).intValue();
+						if (i8rn != ++S57dat.rnum) {
+							System.err.println("Out of order record ID");
+							in.close();
+							System.exit(-1);
+						}
+						break;
+					case DSPM:
+						comf = (double) (Long) S57dat.getSubf(record, fields + pos, S57field.DSPM, S57subf.COMF);
+						somf = (double) (Long) S57dat.getSubf(S57subf.SOMF);
+						break;
+					case FRID:
+						inFeature = true;
+						switch (((Long)S57dat.getSubf(record, fields + pos, S57field.FRID, S57subf.PRIM)).intValue()) {
+						case 1:
+							pflag = S57map.Pflag.POINT;
+							break;
+						case 2:
+							pflag = S57map.Pflag.LINE;
+							break;
+						case 3:
+							pflag = S57map.Pflag.AREA;
+							break;
+						default:
+							pflag = S57map.Pflag.NOSP;
+						}
+						objl = (Long)S57dat.getSubf(S57subf.OBJL);
+						break;
+					case FOID:
+						name = (Long) S57dat.getSubf(record, fields + pos, S57field.FOID, S57subf.LNAM);
+						map.newFeature(name, pflag, objl);
+						break;
+					case ATTF:
+						S57dat.setField(record, fields + pos, S57field.ATTF, len);
+						do {
+							long attl = (Long) S57dat.getSubf(S57subf.ATTL);
+							String atvl = ((String) S57dat.getSubf(S57subf.ATVL)).trim();
+							if (!atvl.isEmpty()) {
+								map.newAtt(attl, atvl);
+							}
+						} while (S57dat.more());
+						break;
+					case FFPT:
+						S57dat.setField(record, fields + pos, S57field.FFPT, len);
+						do {
+							name = (Long) S57dat.getSubf(S57subf.LNAM);
+							int rind = ((Long) S57dat.getSubf(S57subf.RIND)).intValue();
+							S57dat.getSubf(S57subf.COMT);
+							map.newObj(name, rind);
+						} while (S57dat.more());
+						break;
+					case FSPT:
+						S57dat.setField(record, fields + pos, S57field.FSPT, len);
+						do {
+							name = (Long) S57dat.getSubf(S57subf.NAME) << 16;
+							map.newPrim(name, (Long) S57dat.getSubf(S57subf.ORNT), (Long) S57dat.getSubf(S57subf.USAG));
+							S57dat.getSubf(S57subf.MASK);
+						} while (S57dat.more());
+						break;
+					case VRID:
+						inFeature = false;
+						name = (long) (Long)S57dat.getSubf(record, fields + pos, S57field.VRID, S57subf.RCNM);
+						switch ((int) name) {
+						case 110:
+							nflag = Nflag.ISOL;
+							break;
+						case 120:
+							nflag = Nflag.CONN;
+							break;
+						default:
+							nflag = Nflag.ANON;
+							break;
+						}
+						name <<= 32;
+						name += (Long) S57dat.getSubf(S57subf.RCID);
+						name <<= 16;
+						if (nflag == Nflag.ANON) {
+							map.newEdge(name);
+						}
+						break;
+					case VRPT:
+						S57dat.setField(record, fields + pos, S57field.VRPT, len);
+						do {
+							long conn = (Long) S57dat.getSubf(S57subf.NAME) << 16;
+							int topi = ((Long) S57dat.getSubf(S57subf.TOPI)).intValue();
+							map.addConn(conn, topi);
+							S57dat.getSubf(S57subf.MASK);
+						} while (S57dat.more());
+						break;
+					case SG2D:
+						S57dat.setField(record, fields + pos, S57field.SG2D, len);
+						do {
+							double lat = (double) ((Long) S57dat.getSubf(S57subf.YCOO)) / comf;
+							double lon = (double) ((Long) S57dat.getSubf(S57subf.XCOO)) / comf;
+							if (nflag == Nflag.ANON) {
+								map.newNode(++name, lat, lon, nflag);
+							} else {
+								map.newNode(name, lat, lon, nflag);
+							}
+							if (lat < bounds.minlat)
+								bounds.minlat = lat;
+							if (lat > bounds.maxlat)
+								bounds.maxlat = lat;
+							if (lon < bounds.minlon)
+								bounds.minlon = lon;
+							if (lon > bounds.maxlon)
+								bounds.maxlon = lon;
+						} while (S57dat.more());
+						break;
+					case SG3D:
+						S57dat.setField(record, fields + pos, S57field.SG3D, len);
+						do {
+							double lat = (double) ((Long) S57dat.getSubf(S57subf.YCOO)) / comf;
+							double lon = (double) ((Long) S57dat.getSubf(S57subf.XCOO)) / comf;
+							double depth = (double) ((Long) S57dat.getSubf(S57subf.VE3D)) / somf;
+							map.newNode(name++, lat, lon, depth);
+							if (lat < bounds.minlat)
+								bounds.minlat = lat;
+							if (lat > bounds.maxlat)
+								bounds.maxlat = lat;
+							if (lon < bounds.minlon)
+								bounds.minlon = lon;
+							if (lon > bounds.maxlon)
+								bounds.maxlon = lon;
+						} while (S57dat.more());
+						break;
+					default:
+						break;
+					}
+				}
+				if (inFeature) {
+					map.endFeature();
+					inFeature = false;
+				}
+			}
+		}
+		map.endFile();
+		in.close();
+		
+		return bounds;
+	}
+	
+}
Index: applications/editors/josm/plugins/smed2/src/s57/S57map.java
===================================================================
--- applications/editors/josm/plugins/smed2/src/s57/S57map.java	(revision 30283)
+++ applications/editors/josm/plugins/smed2/src/s57/S57map.java	(revision 30284)
@@ -20,4 +20,17 @@
 
 public class S57map {
+	
+	public class MapBounds {
+		public double minlat;
+		public double minlon;
+		public double maxlat;
+		public double maxlon;
+		public MapBounds() {
+			minlat = 90;
+			minlon = 180;
+			maxlat = -90;
+			maxlon = -180;
+		}
+	}
 
 	public enum Nflag {
@@ -526,4 +539,135 @@
 	// Utility methods
 	
+	public void sortGeom() {
+		for (long id : index.keySet()) {
+			feature = index.get(id);
+			Geom sort = new Geom(feature.geom.prim);
+			long first = 0;
+			long last = 0;
+			Comp comp = null;
+			boolean next = true;
+			if ((feature.geom.prim == Pflag.LINE) || (feature.geom.prim == Pflag.AREA)) {
+				int sweep = feature.geom.elems.size();
+				while (!feature.geom.elems.isEmpty()) {
+					Prim prim = feature.geom.elems.remove(0);
+					Edge edge = edges.get(prim.id);
+					if (next == true) {
+						next = false;
+						if (prim.forward) {
+							first = edge.first;
+							last = edge.last;
+						} else {
+							first = edge.last;
+							last = edge.first;
+						}
+						sort.elems.add(prim);
+						if (prim.outer) {
+							sort.outers++;
+						} else {
+							sort.inners++;
+						}
+						comp = new Comp(ref++, 1);
+						sort.refs.add(comp);
+					} else {
+						if (prim.forward) {
+							if (edge.first == last) {
+								sort.elems.add(prim);
+								last = edge.last;
+								comp.size++;
+							} else if (edge.last == first) {
+								sort.elems.add(0, prim);
+								first = edge.first;
+								comp.size++;
+							} else {
+								feature.geom.elems.add(prim);
+							}
+						} else {
+							if (edge.last == last) {
+								sort.elems.add(prim);
+								last = edge.first;
+								comp.size++;
+							} else if (edge.first == first) {
+								sort.elems.add(0, prim);
+								first = edge.last;
+								comp.size++;
+							} else {
+								feature.geom.elems.add(prim);
+							}
+						}
+					}
+					if (--sweep == 0) {
+						next = true;
+						sweep = feature.geom.elems.size();
+					}
+				}
+				if ((sort.prim == Pflag.LINE) && (sort.outers == 1) && (sort.inners == 0) && (first == last)) {
+					sort.prim = Pflag.AREA;
+				}
+				feature.geom = sort;
+			} 
+			if (feature.geom.prim == Pflag.AREA) {
+				ArrayList<Prim> outers = new ArrayList<Prim>();
+				ArrayList<Prim> inners = new ArrayList<Prim>();
+				for (Prim prim : feature.geom.elems) {
+					if (prim.outer) {
+						outers.add(prim);
+					} else {
+						inners.add(prim);
+					}
+				}
+				ArrayList<Prim> sorting = outers;
+				ArrayList<Prim> closed = null;
+				sort = new Geom(feature.geom.prim);
+				sort.outers = feature.geom.outers;
+				sort.inners = feature.geom.inners;
+				sort.refs = feature.geom.refs;
+				next = true;
+				while (!sorting.isEmpty()) {
+					Prim prim = sorting.remove(0);
+					Edge edge = edges.get(prim.id);
+					if (next == true) {
+						next = false;
+						closed = new ArrayList<Prim>();
+						closed.add(prim);
+						if (prim.forward) {
+							first = edge.first;
+							last = edge.last;
+						} else {
+							first = edge.last;
+							last = edge.first;
+						}
+					} else {
+						if (prim.forward) {
+							if (edge.first == last) {
+								last = edge.last;
+								closed.add(prim);
+							} else {
+								sorting.add(0, prim);
+								next = true;
+							}
+						} else {
+							if (edge.last == last) {
+								last = edge.first;
+								closed.add(prim);
+							} else {
+								sorting.add(0, prim);
+								next = true;
+							}
+						}
+					}
+					if (first == last) {
+						sort.elems.addAll(closed);
+						next = true;
+					}
+					if (sorting.isEmpty() && sorting == outers) {
+						sorting = inners;
+						next = true;
+					}
+				}
+				feature.geom = sort;
+			}
+		}
+	}
+	
 	public boolean cmpGeoms (Geom g1, Geom g2) {
 		return ((g1.prim == g2.prim) && (g1.outers == g2.outers) && (g1.inners == g2.inners) && (g1.elems.size() == g2.elems.size()));
