source: osm/applications/editors/josm/plugins/NanoLog/src/nanolog/NanoLogLayer.java

Last change on this file was 36483, checked in by stoecker, 8 months ago

set eol-style, fix checkstyle issues, add ignores

  • Property svn:eol-style set to native
File size: 12.7 KB
RevLine 
[27939]1package nanolog;
2
[30737]3import static org.openstreetmap.josm.tools.I18n.tr;
4
5import java.awt.Color;
6import java.awt.Graphics2D;
7import java.awt.Point;
8import java.awt.event.ActionEvent;
9import java.awt.event.MouseAdapter;
10import java.awt.event.MouseEvent;
11import java.io.BufferedReader;
12import java.io.File;
13import java.io.FileInputStream;
14import java.io.IOException;
15import java.io.InputStreamReader;
[30491]16import java.text.ParseException;
17import java.text.SimpleDateFormat;
[30737]18import java.util.ArrayList;
19import java.util.Collections;
20import java.util.Date;
21import java.util.HashSet;
[27939]22import java.util.List;
[30737]23import java.util.Set;
24import java.util.regex.Matcher;
25import java.util.regex.Pattern;
26
27import javax.swing.Action;
28import javax.swing.Icon;
29import javax.swing.JOptionPane;
30
31import org.openstreetmap.josm.actions.JosmAction;
32import org.openstreetmap.josm.actions.RenameLayerAction;
[27939]33import org.openstreetmap.josm.data.Bounds;
[30491]34import org.openstreetmap.josm.data.coor.EastNorth;
35import org.openstreetmap.josm.data.coor.LatLon;
36import org.openstreetmap.josm.data.gpx.WayPoint;
[27939]37import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
[33788]38import org.openstreetmap.josm.gui.MainApplication;
[27939]39import org.openstreetmap.josm.gui.MapView;
[30737]40import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
41import org.openstreetmap.josm.gui.dialogs.LayerListPopup;
42import org.openstreetmap.josm.gui.layer.GpxLayer;
43import org.openstreetmap.josm.gui.layer.JumpToMarkerActions;
44import org.openstreetmap.josm.gui.layer.Layer;
[27939]45import org.openstreetmap.josm.tools.ImageProvider;
[33788]46import org.openstreetmap.josm.tools.Logging;
[27939]47
48/**
[30491]49 * NanoLog layer: a set of points that can be georeferenced.
[27939]50 *
51 * @author zverik
52 */
53public class NanoLogLayer extends Layer implements JumpToMarkerActions.JumpToMarkerLayer {
54
55 private List<NanoLogEntry> log;
56 private int selectedEntry;
[30737]57 private final Set<NanoLogLayerListener> listeners = new HashSet<>();
[30491]58 private NLLMouseAdapter mouseListener;
[30737]59
[32638]60 public NanoLogLayer(List<NanoLogEntry> entries) {
[27939]61 super(tr("NanoLog"));
[30737]62 log = new ArrayList<>(entries);
[27939]63 selectedEntry = -1;
[30491]64 mouseListener = new NLLMouseAdapter();
[31981]65 }
66
67 public void setupListeners() {
[33788]68 MainApplication.getMap().mapView.addMouseListener(mouseListener);
69 MainApplication.getMap().mapView.addMouseMotionListener(mouseListener);
[27939]70 }
[30491]71
72 @Override
[33788]73 public synchronized void destroy() {
74 MainApplication.getMap().mapView.removeMouseListener(mouseListener);
75 MainApplication.getMap().mapView.removeMouseMotionListener(mouseListener);
[30491]76 super.destroy();
77 }
[30737]78
[32638]79 public NanoLogLayer(File file) throws IOException {
[30491]80 this(readNanoLog(file));
81 }
82
[32638]83 public void addListener(NanoLogLayerListener listener) {
[30491]84 listeners.add(listener);
85 }
86
[32638]87 public void removeListener(NanoLogLayerListener listener) {
[30491]88 listeners.remove(listener);
89 }
90
91 protected void fireMarkersChanged() {
[32638]92 for (NanoLogLayerListener listener : listeners) {
[30491]93 listener.markersUpdated(this);
[32638]94 }
[30491]95 }
96
97 protected void fireMarkerSelected() {
[32638]98 for (NanoLogLayerListener listener : listeners) {
[30491]99 listener.markerActivated(this, selectedEntry < 0 ? null : log.get(selectedEntry));
[32638]100 }
[30491]101 }
102
103 public List<NanoLogEntry> getEntries() {
104 return Collections.unmodifiableList(log);
105 }
[30737]106
[32638]107 public static List<NanoLogEntry> readNanoLog(File file) throws IOException {
[30491]108 final Pattern NANOLOG_LINE = Pattern.compile("(.+?)\\t(.+?)(?:\\s*\\{\\{(-?\\d+\\.\\d+),\\s*(-?\\d+\\.\\d+)(?:,\\s*(\\d+))?\\}\\})?");
109 final SimpleDateFormat fmt = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss.SS");
[30737]110 List<NanoLogEntry> result = new ArrayList<>();
[30738]111 try (BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF8"))) {
[32638]112 while (r.ready()) {
113 String line = r.readLine();
114 if (line != null) {
115 Matcher m = NANOLOG_LINE.matcher(line);
116 if (m.matches()) {
117 String time = m.group(1);
118 String message = m.group(2);
119 String lat = m.group(3);
120 String lon = m.group(4);
121 String dir = m.group(5);
122 Date timeDate = null;
123 try {
124 timeDate = fmt.parse(time);
125 } catch (ParseException e) {
[33788]126 Logging.warn(e);
[32638]127 }
128 if (message == null || message.length() == 0 || timeDate == null)
129 continue;
130 LatLon pos = null;
131 Integer direction = null;
132 if (lat != null && lon != null) {
133 try {
134 pos = new LatLon(Double.parseDouble(lat), Double.parseDouble(lon));
[33788]135 direction = Integer.valueOf(dir);
[32638]136 } catch (NumberFormatException e) {
[33788]137 Logging.trace(e);
[32638]138 }
139 }
[33788]140 result.add(new NanoLogEntry(timeDate, message, pos, direction));
[32638]141 }
142 }
143 }
[27939]144 }
145 return result;
146 }
147
148 @Override
[32638]149 public void paint(Graphics2D g, MapView mv, Bounds box) {
[27939]150 // todo
[32638]151 for (int i = 0; i < log.size(); i++) {
[30491]152 NanoLogEntry entry = log.get(i);
153 int radius = 4;
[32638]154 if (entry.getPos() != null) {
[27939]155 Point p = mv.getPoint(entry.getPos());
[30491]156 g.setColor(selectedEntry == i ? Color.red : Color.yellow);
[27939]157 g.fillOval(p.x - radius, p.y - radius, radius * 2, radius * 2);
158 }
159 }
160 }
161
162 @Override
163 public Icon getIcon() {
164 return ImageProvider.get("nanolog.png");
165 }
166
167 @Override
168 public String getToolTipText() {
169 return tr("NanoLog of {0} entries", log.size());
170 }
171
172 @Override
[32638]173 public void mergeFrom(Layer from) {
[27939]174 // todo
175 }
176
177 @Override
[32638]178 public boolean isMergable(Layer other) {
[27939]179 return other instanceof NanoLogLayer;
180 }
181
182 @Override
[32638]183 public void visitBoundingBox(BoundingXYVisitor v) {
184 for (NanoLogEntry entry : log) {
[27939]185 v.visit(entry.getPos());
[32638]186 }
[27939]187 }
188
189 @Override
190 public Object getInfoComponent() {
191 StringBuilder b = new StringBuilder();
192 int cnt = 0;
[32638]193 for (NanoLogEntry e : log) {
194 if (e.getPos() != null)
[27939]195 cnt++;
[32638]196 }
[27939]197 b.append(tr("NanoLog of {0} lines, {1} of them with coordinates.", log.size(), cnt));
198 return b.toString();
199 }
200
201 @Override
202 public Action[] getMenuEntries() {
203 return new Action[] {
204 LayerListDialog.getInstance().createShowHideLayerAction(),
205 LayerListDialog.getInstance().createDeleteLayerAction(),
206 new RenameLayerAction(null, this),
207 SeparatorLayerAction.INSTANCE,
[30491]208 new CorrelateEntries(true),
209 new CorrelateEntries(false),
[27939]210 new SaveLayer(),
211 SeparatorLayerAction.INSTANCE,
212 new LayerListPopup.InfoAction(this)
213 };
214 }
215
[30491]216 @Override
[27939]217 public void jumpToNextMarker() {
218 selectedEntry++;
[32638]219 if (selectedEntry < 0)
[27939]220 selectedEntry = 0;
[32638]221 else if (selectedEntry >= log.size())
[27939]222 selectedEntry = log.size() - 1;
[33788]223 invalidate();
[27939]224 }
225
[30491]226 @Override
[27939]227 public void jumpToPreviousMarker() {
228 selectedEntry--;
[32638]229 if (selectedEntry < 0)
[27939]230 selectedEntry = 0;
[32638]231 else if (selectedEntry >= log.size())
[27939]232 selectedEntry = log.size() - 1;
[33788]233 invalidate();
[27939]234 }
[30491]235
[32638]236 protected void setSelected(int i) {
[30491]237 int newSelected = i >= 0 && i < log.size() ? i : -1;
[32638]238 if (newSelected != selectedEntry) {
[30491]239 selectedEntry = newSelected;
240 fireMarkerSelected();
[33788]241 invalidate();
[30491]242 }
243 }
244
[32638]245 public void setSelected(NanoLogEntry entry) {
246 if (entry == null)
[30491]247 setSelected(-1);
248 else {
[32638]249 for (int i = 0; i < log.size(); i++) {
250 if (entry.equals(log.get(i))) {
[30491]251 setSelected(i);
252 break;
253 }
254 }
255 }
256 }
257
258 private class NLLMouseAdapter extends MouseAdapter {
259 private int dragging;
260
[32638]261 public int nearestEntry(MouseEvent e) {
[33788]262 LatLon ll = MainApplication.getMap().mapView.getLatLon(e.getX(), e.getY());
[30491]263 int radius = 8;
[32638]264 if (ll != null) {
[33788]265 LatLon lld = MainApplication.getMap().mapView.getLatLon(e.getX() + radius, e.getY() + radius);
[30491]266 double distance = Math.max(lld.lat() - ll.lat(), lld.lon() - ll.lon());
267 boolean selectedIsSelected = false;
268 int newSelected = -1;
[32638]269 for (int i = 0; i < log.size(); i++) {
270 if (log.get(i).getPos() != null && log.get(i).getPos().distance(ll) < distance) {
[30491]271 newSelected = i;
[32638]272 if (i == selectedEntry)
[30491]273 selectedIsSelected = true;
274 }
275 }
[32638]276 if (newSelected >= 0)
[30491]277 return selectedIsSelected ? selectedEntry : newSelected;
278 }
279 return -1;
280 }
281
282 @Override
[32638]283 public void mouseMoved(MouseEvent e) {
[30491]284 int nearest = nearestEntry(e);
[32638]285 if (nearest > 0)
[30491]286 setSelected(nearest);
287 }
288
289 @Override
[32638]290 public void mouseDragged(MouseEvent e) {
[30491]291 doDrag(e);
292 }
293
294 @Override
[32638]295 public void mouseReleased(MouseEvent e) {
296 if (dragging > 0) {
[30491]297 dragging = 0;
298 }
299 }
300
301 @Override
[32638]302 public void mousePressed(MouseEvent e) {
[30491]303 int nearest = nearestEntry(e);
[33788]304 if (nearest > 0 && MainApplication.getLayerManager().getActiveLayer() == NanoLogLayer.this) {
[30491]305 dragging = nearest;
306 doDrag(e);
307 }
308 }
309
[32638]310 private void doDrag(MouseEvent e) {
311 if (dragging > 0)
[30491]312 dragTo(dragging, e.getX(), e.getY());
313 }
314 }
315
[32638]316 protected void dragTo(int entry, int x, int y) {
[30491]317 GpxLayer gpx = GPXChooser.topLayer();
[32638]318 if (gpx == null)
[30491]319 return;
[33788]320 EastNorth eastNorth = MainApplication.getMap().mapView.getEastNorth(x, y);
321 double tolerance = eastNorth.distance(MainApplication.getMap().mapView.getEastNorth(x + 300, y));
[30491]322 WayPoint wp = gpx.data.nearestPointOnTrack(eastNorth, tolerance);
[32638]323 if (wp == null)
[30491]324 return;
325 long newTime = Correlator.getGpxDate(gpx.data, wp.getCoor());
[32638]326 if (newTime <= 0)
[30491]327 return;
328 Correlator.revertPos(log);
329 Correlator.correlate(log, gpx.data, log.get(entry).getTime().getTime() - newTime);
[33788]330 MainApplication.getMap().mapView.repaint();
[30491]331 }
[30737]332
[30491]333 private class CorrelateEntries extends JosmAction {
334 private boolean toZero;
[27939]335
[32638]336 CorrelateEntries(boolean toZero) {
337 super(toZero ? tr("Correlate with GPX...") : tr("Put on GPX..."), "nanolog/correlate",
338 tr("Correlate entries with GPS trace"), null, false);
[30491]339 this.toZero = toZero;
340 }
341
342 @Override
[32638]343 public void actionPerformed(ActionEvent e) {
[30491]344 // 1. Select GPX trace or display message to load one
345 // (better yet, disable when no GPX traces)
346 GpxLayer layer = GPXChooser.chooseLayer();
347 // 2. Correlate by default, sticking by date
348 // (if does not match, shift so hours-minutes stay)
[32638]349 if (layer != null) {
[30491]350 long offset = toZero ? 0 : Correlator.crudeMatch(log, layer.data);
351 Correlator.revertPos(log);
352 Correlator.correlate(log, layer.data, offset);
353 fireMarkersChanged();
[33788]354 MainApplication.getMap().mapView.repaint();
[30491]355 }
356 // 3. Show non-modal (?) window with a slider and a text input
357 // (todo: better slider, like in blender)
[27939]358 }
359 }
[30737]360
[33788]361 private static class SaveLayer extends JosmAction {
[27939]362
[32638]363 SaveLayer() {
[30491]364 super(tr("Save layer..."), "nanolog/save", tr("Save NanoLog layer"), null, false);
365 }
366
367 @Override
[32638]368 public void actionPerformed(ActionEvent e) {
[27939]369 // todo
[34533]370 JOptionPane.showMessageDialog(MainApplication.getMainFrame(), "Sorry, no saving yet", "NanoLog", JOptionPane.ERROR_MESSAGE);
[27939]371 }
372 }
[30491]373
[32638]374 public interface NanoLogLayerListener {
375 void markersUpdated(NanoLogLayer layer);
376
377 void markerActivated(NanoLogLayer layer, NanoLogEntry entry);
[30491]378 }
[27939]379}
Note: See TracBrowser for help on using the repository browser.