Index: utils/josm/plugins/livegps/build.xml
===================================================================
--- utils/josm/plugins/livegps/build.xml	(revision 1955)
+++ utils/josm/plugins/livegps/build.xml	(revision 1955)
@@ -0,0 +1,30 @@
+<project name="livegps" default="dist" basedir=".">
+
+	<target name="dist" depends="compile">
+		<!-- images -->
+		<copy todir="build/images">
+			<fileset dir="images" />
+		</copy>
+
+		<!-- create josm-custom.jar -->
+		<jar destfile="livegps.jar" basedir="build">
+			<manifest>
+                <attribute name="Plugin-Class" value="livegps.LiveGpsPlugin" />
+                <attribute name="Plugin-Description" value="Allow live GPS feed from a gpsd server" />
+			</manifest>
+		</jar>
+	</target>
+
+	<target name="compile" depends="init">
+		<javac srcdir="livegps" classpath="../../josm/bin/" destdir="build" />
+	</target>
+
+	<target name="init">
+		<mkdir dir="build" />
+	</target>
+
+	<target name="clean">
+		<delete dir="build" />
+	</target>
+
+</project>
Index: utils/josm/plugins/livegps/livegps/LiveGpsAcquirer.java
===================================================================
--- utils/josm/plugins/livegps/livegps/LiveGpsAcquirer.java	(revision 1955)
+++ utils/josm/plugins/livegps/livegps/LiveGpsAcquirer.java	(revision 1955)
@@ -0,0 +1,129 @@
+package livegps;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PipedReader;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.util.StringTokenizer;
+
+public class LiveGpsAcquirer implements Runnable {
+	LiveGpsLayer view;
+	Socket gpsdSocket;
+	BufferedReader gpsdReader;
+	boolean connected = false;
+	String gpsdHost = "localhost";
+	int gpsdPort = 2947;
+	boolean shutdownFlag = false;
+	
+	public LiveGpsAcquirer() {
+		
+	}
+	
+	public void run() {	
+		while(!shutdownFlag) {
+			double lat = 0;
+			double lon = 0;
+			boolean haveFix = false;
+
+			try
+			{
+				if (!connected)
+				{
+					view.setStatus("connecting...");
+					InetAddress[] addrs = InetAddress.getAllByName(gpsdHost);
+					for (int i=0; i < addrs.length; i++) {
+						try {
+							gpsdSocket = new Socket(addrs[i], gpsdPort);
+							break;
+						} catch (Exception e) {
+							gpsdSocket = null;
+						}
+					}
+					
+					if (gpsdSocket != null)
+					{
+						gpsdReader = new BufferedReader(new InputStreamReader(gpsdSocket.getInputStream()));
+						gpsdSocket.getOutputStream().write(new byte[] { 'w', 13, 10 });
+					}
+					view.setStatus("connected");
+					connected = true;
+				}
+
+
+				String line = gpsdReader.readLine();
+				if (line == null) break;
+				String words[] = line.split(",");
+
+				if ((words.length == 0) || (!words[0].equals("GPSD"))) {
+					// unexpected response.
+					continue;
+				}
+
+				for (int i = 1; i < words.length; i++) {
+					
+					if ((words[i].length() < 2) || (words[i].charAt(1) != '=')) {
+						// unexpected response.
+						continue;
+					}
+					
+					char what = words[i].charAt(0);
+					String value = words[i].substring(2);
+
+					switch(what) {
+					case 'O':
+						// full report, tab delimited.
+						String[] status = value.split("\\s+");
+						if (status.length >= 5) {
+							lat = Double.parseDouble(status[3]);
+							lon = Double.parseDouble(status[4]);
+							try {
+								view.setSpeed(Float.parseFloat(status[9]));
+								view.setCourse(Float.parseFloat(status[8]));
+							} catch (NumberFormatException nex) {}
+							haveFix = true;
+						}
+						break;
+					case 'P':	
+						// position report, tab delimited.
+						String[] pos = value.split("\\s+");
+						if (pos.length >= 2) {
+							lat = Double.parseDouble(pos[0]);
+							lon = Double.parseDouble(pos[1]);
+							haveFix = true;
+						}
+					default:
+						// not interested
+					}
+
+				}
+
+				if (haveFix) {
+					view.setCurrentPosition(lat, lon);
+				}
+				
+			} catch(IOException iox) {
+				connected = false;
+				view.setStatus("connection failed");
+				try { Thread.sleep(1000); } catch (Exception x) {};
+				// send warning to layer
+
+			}
+
+		}
+		view.setStatus("disconnected");
+		if (gpsdSocket != null) try { gpsdSocket.close(); } catch (Exception ex) {};
+	}
+	
+	public void shutdown()
+	{
+		shutdownFlag = true;
+	}
+	
+	public void setOutputLayer(LiveGpsLayer o)
+	{
+		view = o;
+	}
+}
Index: utils/josm/plugins/livegps/livegps/LiveGpsLayer.java
===================================================================
--- utils/josm/plugins/livegps/livegps/LiveGpsLayer.java	(revision 1955)
+++ utils/josm/plugins/livegps/livegps/LiveGpsLayer.java	(revision 1955)
@@ -0,0 +1,111 @@
+package livegps;
+
+import java.awt.Color;
+import java.awt.Graphics;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+
+import javax.swing.JLabel;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.gui.MapView;
+import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
+import org.openstreetmap.josm.gui.layer.RawGpsLayer;
+import org.openstreetmap.josm.gui.layer.RawGpsLayer.GpsPoint;
+
+public class LiveGpsLayer extends RawGpsLayer {
+
+	LatLon lastPos;
+	GpsPoint lastPoint;
+	Collection<GpsPoint> trackBeingWritten;
+	float speed;
+	float course;
+	String status;
+	JLabel lbl;
+	boolean autocenter;
+	
+	public LiveGpsLayer(Collection<Collection<GpsPoint>> data)
+	{
+		super (data, "LiveGPS layer", null);
+		if (data.isEmpty())
+		{
+			data.add(new ArrayList<GpsPoint>());
+		}
+		for (Collection<GpsPoint> track : data) trackBeingWritten = track;
+		lbl = new JLabel();
+	}
+	
+	void setCurrentPosition(double lat, double lon)
+	{
+		LatLon thisPos = new LatLon(lat, lon);
+		if ((lastPos != null) && (thisPos.equalsEpsilon(lastPos))) {
+			// no change in position
+			// maybe show a "paused" cursor or some such
+			return;
+		}
+			
+		lastPos = thisPos;
+		lastPoint = new GpsPoint (thisPos, new Date().toString());
+		trackBeingWritten.add(lastPoint);
+		if (autocenter) center();
+		
+		Main.map.repaint();
+	}
+
+	public void center()
+	{
+		if (lastPoint != null) 
+			Main.map.mapView.zoomTo(lastPoint.eastNorth, Main.map.mapView.getScale());
+	}
+	
+	void setStatus(String status)
+	{
+		this.status = status;
+		Main.map.repaint();
+	}
+	
+	void setSpeed(float metresPerSecond)
+	{
+		speed = metresPerSecond;
+		Main.map.repaint();
+	}
+
+	void setCourse(float degrees)
+	{
+		course = degrees;
+		Main.map.repaint();
+	}
+	
+	void setAutoCenter(boolean ac)
+	{
+		autocenter = ac;
+	}
+
+	@Override public void paint(Graphics g, MapView mv)
+	{
+		super.paint(g, mv);
+		int statusHeight = 50;
+		Rectangle mvs = mv.getBounds();
+		mvs.y = mvs.y + mvs.height - statusHeight;
+		mvs.height = statusHeight;
+		g.setColor(new Color(1.0f, 1.0f, 1.0f, 0.8f)); 
+		g.fillRect(mvs.x, mvs.y, mvs.width, mvs.height);
+		
+		if (lastPoint != null)
+		{
+			Point screen = mv.getPoint(lastPoint.eastNorth);
+			g.setColor(Color.RED);
+			g.drawOval(screen.x-10, screen.y-10,20,20);
+			g.drawOval(screen.x-9, screen.y-9,18,18);
+		}
+		lbl.setText("gpsd: "+status+" Speed: " + speed + " Course: "+course);
+		lbl.setBounds(0, 0, mvs.width-10, mvs.height-10);
+		Graphics sub = g.create(mvs.x+5, mvs.y+5, mvs.width-10, mvs.height-10);
+		lbl.paint(sub);
+	}
+}
Index: utils/josm/plugins/livegps/livegps/LiveGpsPlugin.java
===================================================================
--- utils/josm/plugins/livegps/livegps/LiveGpsPlugin.java	(revision 1955)
+++ utils/josm/plugins/livegps/livegps/LiveGpsPlugin.java	(revision 1955)
@@ -0,0 +1,86 @@
+package livegps;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyEvent;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import javax.swing.JCheckBoxMenuItem;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JMenuItem;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.layer.MarkerLayer;
+import org.openstreetmap.josm.gui.layer.RawGpsLayer.GpsPoint;
+import org.openstreetmap.josm.plugins.Plugin;
+
+public class LiveGpsPlugin extends Plugin 
+{
+	private LiveGpsAcquirer acquirer = null;
+	private Thread acquirerThread = null;
+    private JMenu lgpsmenu;
+    private JCheckBoxMenuItem lgpscapture;
+    private JMenuItem lgpscenter;
+    private JCheckBoxMenuItem lgpsautocenter;
+    
+	private Collection<Collection<GpsPoint>> data = new ArrayList<Collection<GpsPoint>>();
+    private LiveGpsLayer lgpslayer;
+    
+    public LiveGpsPlugin() 
+    {
+        JMenuBar menu = Main.main.menu;
+        lgpsmenu = new JMenu("LiveGPS");
+        menu.add(lgpsmenu, 2);
+        lgpscapture = new JCheckBoxMenuItem("Capture GPS Track");
+        lgpscapture.setSelected(false);
+        lgpscapture.addChangeListener(new ChangeListener() {
+        	public void stateChanged(ChangeEvent ev) {
+        		if ((acquirer != null) && (!lgpscapture.isSelected()))
+        		{
+        			acquirer.shutdown();
+        			acquirer = null;
+        			acquirerThread = null;
+        		}
+        		else if ((acquirer == null) && (lgpscapture.isSelected()))
+        		{
+        			acquirer = new LiveGpsAcquirer();
+        			if (lgpslayer == null)
+        			{
+        		    	lgpslayer = new LiveGpsLayer(data);
+        				Main.main.addLayer(lgpslayer);
+        			}
+        			acquirer.setOutputLayer(lgpslayer);
+        			acquirerThread = new Thread(acquirer);
+        			acquirerThread.start();
+        		}
+        	}
+        });
+        lgpsmenu.add(lgpscapture);
+
+        lgpscenter = new JMenuItem("Center Once", KeyEvent.VK_C);
+        lgpscenter.addActionListener(new ActionListener() {
+        	public void actionPerformed(ActionEvent ev) {
+        		lgpslayer.center();
+        	}
+        });
+        lgpsmenu.add(lgpscenter);
+        
+        
+        lgpsautocenter = new JCheckBoxMenuItem("Auto-Center on current position");
+        lgpsautocenter.setSelected(false);
+        lgpsautocenter.addChangeListener(new ChangeListener() {
+        	public void stateChanged(ChangeEvent ev) {
+        		lgpslayer.setAutoCenter(lgpsautocenter.isSelected());
+        		if (lgpsautocenter.isSelected()) lgpslayer.center();
+        	}
+        });
+        lgpsmenu.add(lgpsautocenter);
+        
+    }
+}
