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

Last change on this file since 31981 was 31981, checked in by donvip, 9 years ago

[josm_nanolog] update to JOSM 9383 + fix Eclipse warnings + fix NPE if nanolog layer is created first

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