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

Last change on this file was 34533, checked in by donvip, 6 years ago

update to JOSM 14153

File size: 12.7 KB
Line 
1package nanolog;
2
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;
16import java.text.ParseException;
17import java.text.SimpleDateFormat;
18import java.util.ArrayList;
19import java.util.Collections;
20import java.util.Date;
21import java.util.HashSet;
22import java.util.List;
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;
33import org.openstreetmap.josm.data.Bounds;
34import org.openstreetmap.josm.data.coor.EastNorth;
35import org.openstreetmap.josm.data.coor.LatLon;
36import org.openstreetmap.josm.data.gpx.WayPoint;
37import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
38import org.openstreetmap.josm.gui.MainApplication;
39import org.openstreetmap.josm.gui.MapView;
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;
45import org.openstreetmap.josm.tools.ImageProvider;
46import org.openstreetmap.josm.tools.Logging;
47
48/**
49 * NanoLog layer: a set of points that can be georeferenced.
50 *
51 * @author zverik
52 */
53public class NanoLogLayer extends Layer implements JumpToMarkerActions.JumpToMarkerLayer {
54
55 private List<NanoLogEntry> log;
56 private int selectedEntry;
57 private final Set<NanoLogLayerListener> listeners = new HashSet<>();
58 private NLLMouseAdapter mouseListener;
59
60 public NanoLogLayer(List<NanoLogEntry> entries) {
61 super(tr("NanoLog"));
62 log = new ArrayList<>(entries);
63 selectedEntry = -1;
64 mouseListener = new NLLMouseAdapter();
65 }
66
67 public void setupListeners() {
68 MainApplication.getMap().mapView.addMouseListener(mouseListener);
69 MainApplication.getMap().mapView.addMouseMotionListener(mouseListener);
70 }
71
72 @Override
73 public synchronized void destroy() {
74 MainApplication.getMap().mapView.removeMouseListener(mouseListener);
75 MainApplication.getMap().mapView.removeMouseMotionListener(mouseListener);
76 super.destroy();
77 }
78
79 public NanoLogLayer(File file) throws IOException {
80 this(readNanoLog(file));
81 }
82
83 public void addListener(NanoLogLayerListener listener) {
84 listeners.add(listener);
85 }
86
87 public void removeListener(NanoLogLayerListener listener) {
88 listeners.remove(listener);
89 }
90
91 protected void fireMarkersChanged() {
92 for (NanoLogLayerListener listener : listeners) {
93 listener.markersUpdated(this);
94 }
95 }
96
97 protected void fireMarkerSelected() {
98 for (NanoLogLayerListener listener : listeners) {
99 listener.markerActivated(this, selectedEntry < 0 ? null : log.get(selectedEntry));
100 }
101 }
102
103 public List<NanoLogEntry> getEntries() {
104 return Collections.unmodifiableList(log);
105 }
106
107 public static List<NanoLogEntry> readNanoLog(File file) throws IOException {
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");
110 List<NanoLogEntry> result = new ArrayList<>();
111 try (BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF8"))) {
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) {
126 Logging.warn(e);
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));
135 direction = Integer.valueOf(dir);
136 } catch (NumberFormatException e) {
137 Logging.trace(e);
138 }
139 }
140 result.add(new NanoLogEntry(timeDate, message, pos, direction));
141 }
142 }
143 }
144 }
145 return result;
146 }
147
148 @Override
149 public void paint(Graphics2D g, MapView mv, Bounds box) {
150 // todo
151 for (int i = 0; i < log.size(); i++) {
152 NanoLogEntry entry = log.get(i);
153 int radius = 4;
154 if (entry.getPos() != null) {
155 Point p = mv.getPoint(entry.getPos());
156 g.setColor(selectedEntry == i ? Color.red : Color.yellow);
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
173 public void mergeFrom(Layer from) {
174 // todo
175 }
176
177 @Override
178 public boolean isMergable(Layer other) {
179 return other instanceof NanoLogLayer;
180 }
181
182 @Override
183 public void visitBoundingBox(BoundingXYVisitor v) {
184 for (NanoLogEntry entry : log) {
185 v.visit(entry.getPos());
186 }
187 }
188
189 @Override
190 public Object getInfoComponent() {
191 StringBuilder b = new StringBuilder();
192 int cnt = 0;
193 for (NanoLogEntry e : log) {
194 if (e.getPos() != null)
195 cnt++;
196 }
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,
208 new CorrelateEntries(true),
209 new CorrelateEntries(false),
210 new SaveLayer(),
211 SeparatorLayerAction.INSTANCE,
212 new LayerListPopup.InfoAction(this)
213 };
214 }
215
216 @Override
217 public void jumpToNextMarker() {
218 selectedEntry++;
219 if (selectedEntry < 0)
220 selectedEntry = 0;
221 else if (selectedEntry >= log.size())
222 selectedEntry = log.size() - 1;
223 invalidate();
224 }
225
226 @Override
227 public void jumpToPreviousMarker() {
228 selectedEntry--;
229 if (selectedEntry < 0)
230 selectedEntry = 0;
231 else if (selectedEntry >= log.size())
232 selectedEntry = log.size() - 1;
233 invalidate();
234 }
235
236 protected void setSelected(int i) {
237 int newSelected = i >= 0 && i < log.size() ? i : -1;
238 if (newSelected != selectedEntry) {
239 selectedEntry = newSelected;
240 fireMarkerSelected();
241 invalidate();
242 }
243 }
244
245 public void setSelected(NanoLogEntry entry) {
246 if (entry == null)
247 setSelected(-1);
248 else {
249 for (int i = 0; i < log.size(); i++) {
250 if (entry.equals(log.get(i))) {
251 setSelected(i);
252 break;
253 }
254 }
255 }
256 }
257
258 private class NLLMouseAdapter extends MouseAdapter {
259 private int dragging;
260
261 public int nearestEntry(MouseEvent e) {
262 LatLon ll = MainApplication.getMap().mapView.getLatLon(e.getX(), e.getY());
263 int radius = 8;
264 if (ll != null) {
265 LatLon lld = MainApplication.getMap().mapView.getLatLon(e.getX() + radius, e.getY() + radius);
266 double distance = Math.max(lld.lat() - ll.lat(), lld.lon() - ll.lon());
267 boolean selectedIsSelected = false;
268 int newSelected = -1;
269 for (int i = 0; i < log.size(); i++) {
270 if (log.get(i).getPos() != null && log.get(i).getPos().distance(ll) < distance) {
271 newSelected = i;
272 if (i == selectedEntry)
273 selectedIsSelected = true;
274 }
275 }
276 if (newSelected >= 0)
277 return selectedIsSelected ? selectedEntry : newSelected;
278 }
279 return -1;
280 }
281
282 @Override
283 public void mouseMoved(MouseEvent e) {
284 int nearest = nearestEntry(e);
285 if (nearest > 0)
286 setSelected(nearest);
287 }
288
289 @Override
290 public void mouseDragged(MouseEvent e) {
291 doDrag(e);
292 }
293
294 @Override
295 public void mouseReleased(MouseEvent e) {
296 if (dragging > 0) {
297 dragging = 0;
298 }
299 }
300
301 @Override
302 public void mousePressed(MouseEvent e) {
303 int nearest = nearestEntry(e);
304 if (nearest > 0 && MainApplication.getLayerManager().getActiveLayer() == NanoLogLayer.this) {
305 dragging = nearest;
306 doDrag(e);
307 }
308 }
309
310 private void doDrag(MouseEvent e) {
311 if (dragging > 0)
312 dragTo(dragging, e.getX(), e.getY());
313 }
314 }
315
316 protected void dragTo(int entry, int x, int y) {
317 GpxLayer gpx = GPXChooser.topLayer();
318 if (gpx == null)
319 return;
320 EastNorth eastNorth = MainApplication.getMap().mapView.getEastNorth(x, y);
321 double tolerance = eastNorth.distance(MainApplication.getMap().mapView.getEastNorth(x + 300, y));
322 WayPoint wp = gpx.data.nearestPointOnTrack(eastNorth, tolerance);
323 if (wp == null)
324 return;
325 long newTime = Correlator.getGpxDate(gpx.data, wp.getCoor());
326 if (newTime <= 0)
327 return;
328 Correlator.revertPos(log);
329 Correlator.correlate(log, gpx.data, log.get(entry).getTime().getTime() - newTime);
330 MainApplication.getMap().mapView.repaint();
331 }
332
333 private class CorrelateEntries extends JosmAction {
334 private boolean toZero;
335
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);
339 this.toZero = toZero;
340 }
341
342 @Override
343 public void actionPerformed(ActionEvent e) {
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)
349 if (layer != null) {
350 long offset = toZero ? 0 : Correlator.crudeMatch(log, layer.data);
351 Correlator.revertPos(log);
352 Correlator.correlate(log, layer.data, offset);
353 fireMarkersChanged();
354 MainApplication.getMap().mapView.repaint();
355 }
356 // 3. Show non-modal (?) window with a slider and a text input
357 // (todo: better slider, like in blender)
358 }
359 }
360
361 private static class SaveLayer extends JosmAction {
362
363 SaveLayer() {
364 super(tr("Save layer..."), "nanolog/save", tr("Save NanoLog layer"), null, false);
365 }
366
367 @Override
368 public void actionPerformed(ActionEvent e) {
369 // todo
370 JOptionPane.showMessageDialog(MainApplication.getMainFrame(), "Sorry, no saving yet", "NanoLog", JOptionPane.ERROR_MESSAGE);
371 }
372 }
373
374 public interface NanoLogLayerListener {
375 void markersUpdated(NanoLogLayer layer);
376
377 void markerActivated(NanoLogLayer layer, NanoLogEntry entry);
378 }
379}
Note: See TracBrowser for help on using the repository browser.