From 663996a3262499dc63b84cf62740cc4f7ffea441 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dirk=20St=C3=B6cker?= Date: Thu, 5 Sep 2024 08:36:28 +0000 Subject: [PATCH] add target display, patch by Pauline Thiele git-svn-id: https://josm.openstreetmap.de/osmsvn/applications/editors/josm/plugins@36330 b9d5c4c9-76e1-0310-9c85-f3177eceb1e4 --- livegps/src/livegps/CirclePanel.java | 92 +++++++++++++++++ livegps/src/livegps/DrawPoint.java | 86 +++++++++++++++ livegps/src/livegps/LiveGPSPreferences.java | 38 +++++++ livegps/src/livegps/LiveGpsData.java | 13 ++- livegps/src/livegps/LiveGpsDialog.java | 109 ++++++++++++++++---- livegps/src/livegps/LiveGpsPlugin.java | 9 ++ livegps/src/livegps/LiveGpsStatus.java | 3 + 7 files changed, 327 insertions(+), 23 deletions(-) create mode 100644 livegps/src/livegps/CirclePanel.java create mode 100644 livegps/src/livegps/DrawPoint.java diff --git a/livegps/src/livegps/CirclePanel.java b/livegps/src/livegps/CirclePanel.java new file mode 100644 index 000000000..b803b136a --- /dev/null +++ b/livegps/src/livegps/CirclePanel.java @@ -0,0 +1,92 @@ +// License: Public Domain. For details, see LICENSE file. +package livegps; + +import javax.swing.JPanel; +import javax.swing.JLabel; + +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.BasicStroke; +import java.awt.Color; + +import java.awt.geom.Rectangle2D; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Area; + +import org.openstreetmap.josm.spi.preferences.Config; + +/** + * Draw a target visualization + */ +public class CirclePanel extends JPanel { + + JLabel label = new JLabel(); + double x = 0; + + public CirclePanel(double x) { + add(label); + this.x = x; + } + + public void setOffset(double offs) { + this.x = offs; + } + + @Override + protected void paintComponent(Graphics g) { + boolean isVisible = Config.getPref().getBoolean(LiveGPSPreferences.C_DISTANCE_VISUALISATION, false); + + super.paintComponent(g); + if (isVisible == true) { + Graphics2D g2 = (Graphics2D) g; + + int w = getWidth(); + int h = getHeight(); + + double width = 0; + if (w > h) { + width = h*0.9; + } else { + width = w*0.9; + } + + double y_start = (h/2.0) - (width/2); // center circle vertical + double x_start = (w - width) / 2; // center circle horizontal + + // 3 rectangles + Shape rect1 = new Rectangle2D.Double(x_start, y_start, width*0.4, width); + Shape rect2 = new Rectangle2D.Double(x_start+width*0.4, y_start, width*0.2, width); + Shape rect3 = new Rectangle2D.Double(x_start+width*0.6, y_start, width*0.4, width); + + // 1 circle + Shape circle = new Ellipse2D.Double(x_start, y_start, width, width); + + // intersection + Area rect1Area = new Area(rect1); + Area rect2Area = new Area(rect2); + Area rect3Area = new Area(rect3); + + Area circleArea1 = new Area(circle); + Area circleArea2 = new Area(circle); + Area circleArea3 = new Area(circle); + + circleArea1.intersect(rect1Area); + circleArea2.intersect(rect2Area); + circleArea3.intersect(rect3Area); + + g2.setStroke(new BasicStroke(2, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER)); + + DrawPoint drawpoint = new DrawPoint(); + drawpoint.draw_point(x, g2, circleArea1, circleArea2, circleArea3, label, x_start, y_start, width); + + g2.draw(circleArea1); + g2.draw(circleArea2); + g2.draw(circleArea3); + + // red center line + g2.setColor(Color.red); + g2.drawLine((int) (x_start+(width/2)), (int) y_start, (int) (x_start+(width/2)), (int) (y_start+width)); + } + } +} diff --git a/livegps/src/livegps/DrawPoint.java b/livegps/src/livegps/DrawPoint.java new file mode 100644 index 000000000..1f2c5838f --- /dev/null +++ b/livegps/src/livegps/DrawPoint.java @@ -0,0 +1,86 @@ +// License: Public Domain. For details, see LICENSE file. +package livegps; + +import javax.swing.JPanel; +import javax.swing.JLabel; + +import java.awt.Graphics2D; +import java.awt.Color; +import java.awt.geom.Area; + +import org.openstreetmap.josm.spi.preferences.Config; + +class DrawPoint extends JPanel { + public double threshold = Config.getPref().getDouble(LiveGPSPreferences.C_OFFSET_THRESHOLD, LiveGPSPreferences.DEFAULT_THRESHOLD); + int x_p = 0; + int w = getWidth(); + int h = getHeight(); + + public void draw_point(double x, Graphics2D g2, Area circleArea1, Area circleArea2, + Area circleArea3, JLabel label, double x_start, double y_start, double width) { + + // x-value of black point + if (x < -threshold) { + x_p = (int) (x_start + (width*0.4) + (x*(width/100)) - ((width*0.15)/2)); + if (x_p < x_start) { + x_p = (int) x_start; + } + } else if (x > threshold) { + x_p = (int) (x_start + (x*(width/100)) + (width*0.6) - ((width*0.15)/2)); + if (x_p > x_start + width) { + x_p = (int) (x_start + width); + } + } else if (x >= -threshold && x <= threshold) { + x_p = (int) (x_start + (width/2) - ((width*0.15)/2)); + } + + // fill area + fill(x, x_p, g2, circleArea1, circleArea2, circleArea3, label, x_start, y_start, width); + + // black position point + g2.setColor(Color.black); + g2.fillOval(x_p, (int) (y_start+(width/2)-((width*0.15)/2)), (int) (width*0.15), (int) (width*0.15)); + } + + public void fill(double x, int x_p, Graphics2D g2, Area circleArea1, Area circleArea2, Area circleArea3, + JLabel label, double x_start, double y_start, double width) { + + Color yellow; + Color green; + yellow = new Color(255, 210, 50, 255); + green = new Color(30, 200, 50, 255); + + // Distance from point to center line, rounded to 2 decimals + double y = Math.round(x * 100.0) / 100.0; + + if (x < 0 && -x > threshold) { + // left yellow + text + g2.setColor(yellow); + g2.fill(circleArea1); + + if (width/3 < 60) { // if label larger than half circle, place label outside the circle + label.setBounds((int) (x_start - 60), (int) (y_start + width*0.3), 60, 10); + } else { + label.setBounds((int) (x_start + width*0.1), (int) (y_start + width*0.3), (int) (width/3), (int) (width/15)); + } + label.setText(y+" m"); + + } else if ((-x <= threshold && x <= 0) || (x <= threshold && x >= 0)) { + // center green + g2.setColor(green); + g2.fill(circleArea2); + label.setText(""); + } else if (x > 0 && x > threshold) { + // right yellow + text + g2.setColor(yellow); + g2.fill(circleArea3); + + if (width/3 < 60) { + label.setBounds((int) (x_start + width*1.1), (int) (y_start + width*0.3), 60, 10); + } else { + label.setBounds((int) (x_start + width*0.7), (int) (y_start + width*0.3), (int) (width/3), (int) (width/15)); + } + label.setText("+"+y+" m"); + } + } +} diff --git a/livegps/src/livegps/LiveGPSPreferences.java b/livegps/src/livegps/LiveGPSPreferences.java index ffd4977c2..8add2c59a 100644 --- a/livegps/src/livegps/LiveGPSPreferences.java +++ b/livegps/src/livegps/LiveGPSPreferences.java @@ -6,6 +6,9 @@ import java.awt.GridBagConstraints; import java.awt.GridBagLayout; +//import java.beans.PropertyChangeEvent; +//import java.beans.PropertyChangeListener; + import javax.swing.Box; import javax.swing.JCheckBox; import javax.swing.JLabel; @@ -33,6 +36,10 @@ public class LiveGPSPreferences extends DefaultTabPreferenceSetting { public static final String C_PORT = "livegps.gpsd.port"; /* option to use specify gpsd disabling */ public static final String C_DISABLED = "livegps.gpsd.disabled"; + /* option to use distance visualisation and set threshold */ + public static final String C_DISTANCE_VISUALISATION = "livegps.distance_visualisation"; + public static final String C_OFFSET_THRESHOLD = "livegps.offset_threshold"; + public static final double DEFAULT_THRESHOLD = 0.3; public static final String C_LIVEGPS_COLOR_POSITION = "color.livegps.position"; public static final String C_LIVEGPS_COLOR_POSITION_ESTIMATE = "color.livegps.position_estimate"; @@ -60,6 +67,9 @@ public class LiveGPSPreferences extends DefaultTabPreferenceSetting { private final JTextField serialDevice = new JTextField(30); private final JCheckBox disableGPSD = new JCheckBox(tr("Disable GPSD")); private final JCheckBox showOffset = new JCheckBox(tr("Show Distance to nearest way")); + private final JCheckBox showDistanceVisualisation = new JCheckBox(tr("Show distance visualisation")); + private final JTextField threshold = new JTextField(5); + private JLabel thresholdLabel; public LiveGPSPreferences() { super("dialogs/livegps", tr("LiveGPS settings"), tr("Here you can change some preferences of LiveGPS plugin")); @@ -95,10 +105,31 @@ public void addGui(PreferenceTabbedPane gui) { showOffset.setSelected(Config.getPref().getBoolean(C_WAYOFFSET, false)); panel.add(showOffset, GBC.eol().fill(GridBagConstraints.HORIZONTAL).insets(0, 0, 0, 5)); + showDistanceVisualisation.setSelected(Config.getPref().getBoolean(C_DISTANCE_VISUALISATION, false)); + panel.add(showDistanceVisualisation, GBC.eol().fill(GridBagConstraints.HORIZONTAL).insets(0, 0, 0, 5)); + + threshold.setText(String.valueOf(Config.getPref().getDouble(C_OFFSET_THRESHOLD, DEFAULT_THRESHOLD))); + threshold.setToolTipText(tr("Threshold, default is {0}", DEFAULT_THRESHOLD)); + thresholdLabel = new JLabel(tr("Threshold")); + panel.add(thresholdLabel, GBC.std()); + panel.add(threshold, GBC.eol().fill(GridBagConstraints.HORIZONTAL).insets(5, 0, 0, 5)); + threshold.setVisible(false); + thresholdLabel.setVisible(false); + + updateThreshold(); // beim Start + showDistanceVisualisation.addActionListener(e -> updateThreshold()); // wenn sich was ändert + panel.add(Box.createVerticalGlue(), GBC.eol().fill(GridBagConstraints.VERTICAL)); createPreferenceTabWithScrollPane(gui, panel); } + private void updateThreshold() { + boolean isVisible = showDistanceVisualisation.isSelected(); + + threshold.setVisible(isVisible); + thresholdLabel.setVisible(isVisible); + } + @Override public boolean ok() { Config.getPref().put(C_HOST, gpsdHost.getText()); @@ -106,6 +137,13 @@ public boolean ok() { Config.getPref().put(C_SERIAL, serialDevice.getText()); Config.getPref().putBoolean(C_DISABLED, disableGPSD.isSelected()); Config.getPref().putBoolean(C_WAYOFFSET, showOffset.isSelected()); + boolean oldVal = Config.getPref().getBoolean(C_DISTANCE_VISUALISATION, false); + boolean newVal = showDistanceVisualisation.isSelected(); + Config.getPref().putBoolean(C_DISTANCE_VISUALISATION, newVal); + Config.getPref().put(C_OFFSET_THRESHOLD, threshold.getText()); + if (oldVal != newVal) { + LiveGpsDialog.updateCirclePanelVisibility(); + } return false; } } diff --git a/livegps/src/livegps/LiveGpsData.java b/livegps/src/livegps/LiveGpsData.java index fb7abd859..5f592366b 100644 --- a/livegps/src/livegps/LiveGpsData.java +++ b/livegps/src/livegps/LiveGpsData.java @@ -31,6 +31,7 @@ public class LiveGpsData { private String wayString; private WayPoint waypoint; private static final DecimalFormat offsetFormat = new DecimalFormat("0.00"); + private double offs = 0; public LiveGpsData(double latitude, double longitude, float course, float speed) { this.fix = true; @@ -166,6 +167,14 @@ public String toString() { + ", long=" + latLon.lon() + ", speed=" + speed + ", course=" + course + ']'; } + public void setOffset(double offs) { + this.offs = offs; + } + + public double getOffset() { + return this.offs; + } + /** * Returns the name of the way that is closest to the current coordinates or an * empty string if no way is around. @@ -193,10 +202,10 @@ protected void decorateNameWithNodes(StringBuilder name, IWay way) { wayString = tr("no name"); } if (Config.getPref().getBoolean(LiveGPSPreferences.C_WAYOFFSET, false)) { - double offs = Geometry.getDistanceWayNode(way, n); + offs = Geometry.getDistanceWayNode(way, n); WaySegment ws = Geometry.getClosestWaySegment(way, n); if (!Geometry.angleIsClockwise(ws.getFirstNode(), ws.getSecondNode(), n)) - offs = -offs; + setOffset(-offs); /* I18N: side offset and way name for livegps way display with offset */ wayString = tr("{0} ({1})", offsetFormat.format(offs), wayString); } diff --git a/livegps/src/livegps/LiveGpsDialog.java b/livegps/src/livegps/LiveGpsDialog.java index fad1ac42a..7358d9c19 100644 --- a/livegps/src/livegps/LiveGpsDialog.java +++ b/livegps/src/livegps/LiveGpsDialog.java @@ -4,14 +4,19 @@ import static org.openstreetmap.josm.tools.I18n.tr; import java.awt.Color; -import java.awt.GridLayout; +import java.awt.BorderLayout; +import java.awt.GridBagLayout; +import java.awt.GridBagConstraints; + import java.awt.event.KeyEvent; + import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.SwingUtilities; +import javax.swing.UIManager; import org.openstreetmap.josm.data.coor.conversion.CoordinateFormatManager; import org.openstreetmap.josm.data.coor.conversion.ICoordinateFormat; @@ -42,30 +47,72 @@ public class LiveGpsDialog extends ToggleDialog implements PropertyChangeListene private JLabel speedLabel; private JLabel wayText; private JPanel panel; + private JPanel opanel; + private Color backgroundColor; private LiveGpsStatus status = new LiveGpsStatus(LiveGpsStatus.GpsStatus.CONNECTING, tr("Connecting")); private LiveGpsStatus nmeaStatus = new LiveGpsStatus(LiveGpsStatus.GpsStatus.CONNECTING, tr("Connecting")); private LiveGpsData data; + private CirclePanel circlePanel; + + private static volatile LiveGpsDialog dialog; public LiveGpsDialog(final MapFrame mapFrame) { super(tr("Live GPS"), "livegps", tr("Show GPS data."), Shortcut.registerShortcut("subwindow:livegps", tr("Toggle: {0}", tr("Live GPS")), KeyEvent.VK_G, Shortcut.ALT_CTRL_SHIFT), 100); + + dialog = this; + + backgroundColor = UIManager.getColor("Panel.background"); + + opanel = new JPanel(); + opanel.setLayout(new BorderLayout()); + panel = new JPanel(); - panel.setLayout(new GridLayout(7, 2)); - panel.add(statusText = new JLabel(tr("Status gpsd"))); - panel.add(statusLabel = new JLabel()); - panel.add(nmeaStatusText = new JLabel(tr("Status NMEA"))); - panel.add(nmeaStatusLabel = new JLabel()); - panel.add(wayText = new JLabel(tr("Way Info"))); - panel.add(wayLabel = new JLabel()); - panel.add(latText = new JLabel(tr("Latitude"))); - panel.add(latLabel = new JLabel()); - panel.add(longText = new JLabel(tr("Longitude"))); - panel.add(longLabel = new JLabel()); - panel.add(new JLabel(tr("Speed"))); - panel.add(speedLabel = new JLabel()); - panel.add(new JLabel(tr("Course"))); - panel.add(courseLabel = new JLabel()); + + GridBagLayout layout = new GridBagLayout(); + panel.setLayout(layout); + GridBagConstraints gbc = new GridBagConstraints(); + + // first column + gbc.weightx = 0.3; + gbc.gridx = 0; + gbc.anchor = GridBagConstraints.WEST; + gbc.fill = GridBagConstraints.HORIZONTAL; + + panel.add(statusText = new JLabel("Status gpsd "), gbc); + panel.add(nmeaStatusText = new JLabel("Status NMEA "), gbc); + panel.add(wayText = new JLabel("Way Info"), gbc); + panel.add(latText = new JLabel("Latitude"), gbc); + panel.add(longText = new JLabel("Longitude"), gbc); + panel.add(new JLabel(tr("Speed")), gbc); + panel.add(new JLabel(tr("Course")), gbc); + + // second column + gbc.weightx = 0.7; + gbc.gridx = 1; + gbc.fill = GridBagConstraints.HORIZONTAL; + + panel.add(statusLabel = new JLabel(), gbc); + panel.add(nmeaStatusLabel = new JLabel(), gbc); + panel.add(wayLabel = new JLabel(), gbc); + panel.add(latLabel = new JLabel(), gbc); + panel.add(longLabel = new JLabel(), gbc); + panel.add(speedLabel = new JLabel(), gbc); + panel.add(courseLabel = new JLabel(), gbc); + + opanel.add(panel, BorderLayout.NORTH); + + addPropertyChangeListener(new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + updateCirclePanelVisibility(); + } + }); + + circlePanel = new CirclePanel(0); + opanel.add(circlePanel, BorderLayout.CENTER); + setStatusVisibility(true); if (Config.getPref().getBoolean(LiveGPSPreferences.C_WAYOFFSET, false)) { /* I18N: way information with offset (in m) enabled */ @@ -73,7 +120,18 @@ public LiveGpsDialog(final MapFrame mapFrame) { } else { wayText.setText(tr("Way Info")); } - createLayout(panel, true, null); + createLayout(opanel, true, null); + } + + public static void updateCirclePanelVisibility() { + if (dialog != null) { + boolean vis = Config.getPref().getBoolean(LiveGPSPreferences.C_DISTANCE_VISUALISATION, false); + + dialog.circlePanel.setVisible(vis); + + dialog.circlePanel.revalidate(); + dialog.circlePanel.repaint(); + } } /** @@ -107,7 +165,7 @@ public void propertyChange(PropertyChangeEvent evt) { @Override public void run() { if (data.isFix()) { - panel.setBackground(Color.WHITE); + panel.setBackground(backgroundColor); ICoordinateFormat mCord = CoordinateFormatManager.getDefaultFormat(); if (ProjectedCoordinateFormat.INSTANCE.equals(mCord)) { latText.setText(tr("Northing")); @@ -125,7 +183,13 @@ public void run() { String wayString = data.getWayInfo(); if (!wayString.isEmpty()) { - wayLabel.setText(wayString); + wayLabel.setText(tr("{1}", (int) getWidth()*0.8, wayString)); + + circlePanel.setOffset(data.getOffset()); + circlePanel.setBackground(backgroundColor); + circlePanel.validate(); + circlePanel.repaint(); + } else { wayLabel.setText(tr("unknown")); } @@ -135,6 +199,7 @@ public void run() { } else { wayText.setText(tr("Way Info")); } + } else { latLabel.setText(""); longLabel.setText(""); @@ -142,7 +207,9 @@ public void run() { courseLabel.setText(""); panel.setBackground(Color.RED); } - } }); + } + + }); } else if ("gpsstatus".equals(evt.getPropertyName())) { LiveGpsStatus oldStatus = status; status = (LiveGpsStatus) evt.getNewValue(); @@ -161,7 +228,7 @@ public void run() { && nmeaStatus.getStatus() != LiveGpsStatus.GpsStatus.CONNECTED) { panel.setBackground(Color.RED); } else { - panel.setBackground(Color.WHITE); + panel.setBackground(backgroundColor); } } }); } else if ("nmeastatus".equals(evt.getPropertyName())) { diff --git a/livegps/src/livegps/LiveGpsPlugin.java b/livegps/src/livegps/LiveGpsPlugin.java index e9e8f96bc..645a9d1b0 100644 --- a/livegps/src/livegps/LiveGpsPlugin.java +++ b/livegps/src/livegps/LiveGpsPlugin.java @@ -47,6 +47,9 @@ public class LiveGpsPlugin extends Plugin implements LayerChangeListener { private GpxData data = new GpxData(); private LiveGpsLayer lgpslayer = null; + /** + * Main action to start data capture + */ public class CaptureAction extends JosmAction { public CaptureAction() { super( @@ -64,6 +67,9 @@ public void actionPerformed(ActionEvent e) { } } + /** + * Center view to newest data + */ public class CenterAction extends JosmAction { public CenterAction() { super(tr("Center Once"), "centermenu", @@ -81,6 +87,9 @@ public void actionPerformed(ActionEvent e) { } } + /** + * Automatically center view to newest data + */ public class AutoCenterAction extends JosmAction { public AutoCenterAction() { super( diff --git a/livegps/src/livegps/LiveGpsStatus.java b/livegps/src/livegps/LiveGpsStatus.java index 0c59d08f9..171e88165 100644 --- a/livegps/src/livegps/LiveGpsStatus.java +++ b/livegps/src/livegps/LiveGpsStatus.java @@ -6,6 +6,9 @@ * @author cdaller */ public class LiveGpsStatus { + /** + * Possible status of LiveGPS data input + */ public enum GpsStatus { CONNECTING, CONNECTED, DISCONNECTED, CONNECTION_FAILED }