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

Last change on this file since 34489 was 33788, checked in by donvip, 7 years ago

update to JOSM 12643

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