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

Last change on this file since 33197 was 32638, checked in by donvip, 10 years ago

checkstyle

File size: 12.6 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.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;
46
47/**
48 * NanoLog layer: a set of points that can be georeferenced.
49 *
50 * @author zverik
51 */
52public class NanoLogLayer extends Layer implements JumpToMarkerActions.JumpToMarkerLayer {
53
54 private List<NanoLogEntry> log;
55 private int selectedEntry;
56 private final Set<NanoLogLayerListener> listeners = new HashSet<>();
57 private NLLMouseAdapter mouseListener;
58
59 public NanoLogLayer(List<NanoLogEntry> entries) {
60 super(tr("NanoLog"));
61 log = new ArrayList<>(entries);
62 selectedEntry = -1;
63 mouseListener = new NLLMouseAdapter();
64 }
65
66 public void setupListeners() {
67 Main.map.mapView.addMouseListener(mouseListener);
68 Main.map.mapView.addMouseMotionListener(mouseListener);
69 }
70
71 @Override
72 public void destroy() {
73 Main.map.mapView.removeMouseListener(mouseListener);
74 Main.map.mapView.removeMouseMotionListener(mouseListener);
75 super.destroy();
76 }
77
78 public NanoLogLayer(File file) throws IOException {
79 this(readNanoLog(file));
80 }
81
82 public void addListener(NanoLogLayerListener listener) {
83 listeners.add(listener);
84 }
85
86 public void removeListener(NanoLogLayerListener listener) {
87 listeners.remove(listener);
88 }
89
90 protected void fireMarkersChanged() {
91 for (NanoLogLayerListener listener : listeners) {
92 listener.markersUpdated(this);
93 }
94 }
95
96 protected void fireMarkerSelected() {
97 for (NanoLogLayerListener listener : listeners) {
98 listener.markerActivated(this, selectedEntry < 0 ? null : log.get(selectedEntry));
99 }
100 }
101
102 public List<NanoLogEntry> getEntries() {
103 return Collections.unmodifiableList(log);
104 }
105
106 public static List<NanoLogEntry> readNanoLog(File file) throws IOException {
107 final Pattern NANOLOG_LINE = Pattern.compile("(.+?)\\t(.+?)(?:\\s*\\{\\{(-?\\d+\\.\\d+),\\s*(-?\\d+\\.\\d+)(?:,\\s*(\\d+))?\\}\\})?");
108 final SimpleDateFormat fmt = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss.SS");
109 List<NanoLogEntry> result = new ArrayList<>();
110 try (BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF8"))) {
111 while (r.ready()) {
112 String line = r.readLine();
113 if (line != null) {
114 Matcher m = NANOLOG_LINE.matcher(line);
115 if (m.matches()) {
116 String time = m.group(1);
117 String message = m.group(2);
118 String lat = m.group(3);
119 String lon = m.group(4);
120 String dir = m.group(5);
121 Date timeDate = null;
122 try {
123 timeDate = fmt.parse(time);
124 } catch (ParseException e) {
125 Main.warn(e);
126 }
127 if (message == null || message.length() == 0 || timeDate == null)
128 continue;
129 LatLon pos = null;
130 Integer direction = null;
131 if (lat != null && lon != null) {
132 try {
133 pos = new LatLon(Double.parseDouble(lat), Double.parseDouble(lon));
134 direction = new Integer(dir);
135 } catch (NumberFormatException e) {
136 Main.trace(e);
137 }
138 }
139 NanoLogEntry entry = new NanoLogEntry(timeDate, message, pos, direction);
140 result.add(entry);
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 Main.map.repaint();
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 Main.map.repaint();
234 }
235
236 protected void setSelected(int i) {
237 int newSelected = i >= 0 && i < log.size() ? i : -1;
238 if (newSelected != selectedEntry) {
239// System.out.println("selected: " + log.get(newSelected).getMessage());
240 selectedEntry = newSelected;
241 fireMarkerSelected();
242 Main.map.mapView.repaint();
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 = Main.map.mapView.getLatLon(e.getX(), e.getY());
264 int radius = 8;
265 if (ll != null) {
266 LatLon lld = Main.map.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 && Main.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 = Main.map.mapView.getEastNorth(x, y);
322 double tolerance = eastNorth.distance(Main.map.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 Main.map.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 Main.map.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 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.