source: josm/trunk/src/org/openstreetmap/josm/gui/layer/GpxLayer.java@ 512

Last change on this file since 512 was 512, checked in by gebner, 16 years ago

Add support for two non-standard GPX variants:

  • <link>URL</link> (gets silently translated to <link href="URL"/>)
  • <... lat="nan" lon="nan"> (we parse "nan" as Double.NaN and skip these points when rendering)
File size: 15.0 KB
Line 
1//License: GPL. Copyright 2007 by Immanuel Scholz, Raphael Mack and others
2
3package org.openstreetmap.josm.gui.layer;
4
5import static org.openstreetmap.josm.tools.I18n.tr;
6import static org.openstreetmap.josm.tools.I18n.trn;
7
8import java.awt.Color;
9import java.awt.Component;
10import java.awt.Graphics;
11import java.awt.GridBagLayout;
12import java.awt.Point;
13import java.util.Collection;
14import java.util.LinkedList;
15import java.awt.event.ActionEvent;
16import java.awt.event.ActionListener;
17import java.io.File;
18import java.io.FileInputStream;
19import java.io.FileOutputStream;
20import java.io.BufferedReader;
21import java.io.InputStreamReader;
22import java.net.URL;
23import java.net.URLConnection;
24import java.net.UnknownHostException;
25
26import javax.swing.AbstractAction;
27import javax.swing.Box;
28import javax.swing.ButtonGroup;
29import javax.swing.Icon;
30import javax.swing.JColorChooser;
31import javax.swing.JFileChooser;
32import javax.swing.JLabel;
33import javax.swing.JMenuItem;
34import javax.swing.JOptionPane;
35import javax.swing.JPanel;
36import javax.swing.JSeparator;
37import javax.swing.JRadioButton;
38import javax.swing.JTextField;
39import javax.swing.JCheckBox;
40import javax.swing.filechooser.FileFilter;
41
42import org.openstreetmap.josm.Main;
43import org.openstreetmap.josm.actions.SaveAction;
44import org.openstreetmap.josm.actions.SaveAsAction;
45import org.openstreetmap.josm.actions.RenameLayerAction;
46import org.openstreetmap.josm.data.gpx.GpxData;
47import org.openstreetmap.josm.data.gpx.GpxTrack;
48import org.openstreetmap.josm.data.gpx.GpxRoute;
49import org.openstreetmap.josm.data.gpx.WayPoint;
50import org.openstreetmap.josm.data.osm.DataSet;
51import org.openstreetmap.josm.data.osm.Node;
52import org.openstreetmap.josm.data.osm.Way;
53import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
54import org.openstreetmap.josm.io.MultiPartFormOutputStream;
55import org.openstreetmap.josm.io.GpxWriter;
56import org.openstreetmap.josm.gui.MapView;
57import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
58import org.openstreetmap.josm.gui.dialogs.LayerListPopup;
59import org.openstreetmap.josm.tools.ColorHelper;
60import org.openstreetmap.josm.tools.DontShowAgainInfo;
61import org.openstreetmap.josm.tools.GBC;
62import org.openstreetmap.josm.tools.ImageProvider;
63import org.openstreetmap.josm.tools.UrlLabel;
64
65public class GpxLayer extends Layer {
66 public GpxData data;
67
68 public GpxLayer(GpxData d) {
69 super((String) d.attr.get("name"));
70 data = d;
71 }
72
73 public GpxLayer(GpxData d, String name) {
74 this(d);
75 this.name = name;
76 }
77
78 @Override public Icon getIcon() {
79 return ImageProvider.get("layer", "gpx_small");
80 }
81
82 @Override public Object getInfoComponent() {
83 return getToolTipText();
84 }
85
86 @Override public Component[] getMenuEntries() {
87 JMenuItem line = new JMenuItem(tr("Customize line drawing"), ImageProvider.get("mapmode/addsegment"));
88 line.addActionListener(new ActionListener() {
89 public void actionPerformed(ActionEvent e) {
90 JRadioButton[] r = new JRadioButton[3];
91 r[0] = new JRadioButton(tr("Use global settings."));
92 r[1] = new JRadioButton(tr("Draw lines between points for this layer."));
93 r[2] = new JRadioButton(tr("Do not draw lines between points for this layer."));
94 ButtonGroup group = new ButtonGroup();
95 Box panel = Box.createVerticalBox();
96 for (JRadioButton b : r) {
97 group.add(b);
98 panel.add(b);
99 }
100 String propName = "draw.rawgps.lines.layer "+name;
101 if (Main.pref.hasKey(propName))
102 group.setSelected(r[Main.pref.getBoolean(propName) ? 1:2].getModel(), true);
103 else
104 group.setSelected(r[0].getModel(), true);
105 int answer = JOptionPane.showConfirmDialog(Main.parent, panel, tr("Select line drawing options"), JOptionPane.OK_CANCEL_OPTION);
106 if (answer == JOptionPane.CANCEL_OPTION)
107 return;
108 if (group.getSelection() == r[0].getModel())
109 Main.pref.put(propName, null);
110 else
111 Main.pref.put(propName, group.getSelection() == r[1].getModel());
112 Main.map.repaint();
113 }
114 });
115
116 JMenuItem color = new JMenuItem(tr("Customize Color"), ImageProvider.get("colorchooser"));
117 color.addActionListener(new ActionListener() {
118 public void actionPerformed(ActionEvent e) {
119 String col = Main.pref.get("color.layer "+name, Main.pref.get("color.gps point", ColorHelper.color2html(Color.gray)));
120 JColorChooser c = new JColorChooser(ColorHelper.html2color(col));
121 Object[] options = new Object[]{tr("OK"), tr("Cancel"), tr("Default")};
122 int answer = JOptionPane.showOptionDialog(Main.parent, c, tr("Choose a color"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE, null, options, options[0]);
123 switch (answer) {
124 case 0:
125 Main.pref.put("color.layer "+name, ColorHelper.color2html(c.getColor()));
126 break;
127 case 1:
128 return;
129 case 2:
130 Main.pref.put("color.layer "+name, null);
131 break;
132 }
133 Main.map.repaint();
134 }
135 });
136
137 JMenuItem tagimage = new JMenuItem(tr("Import images"), ImageProvider.get("tagimages"));
138 tagimage.addActionListener(new ActionListener() {
139 public void actionPerformed(ActionEvent e) {
140 JFileChooser fc = new JFileChooser(Main.pref.get("tagimages.lastdirectory"));
141 fc.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
142 fc.setMultiSelectionEnabled(true);
143 fc.setAcceptAllFileFilterUsed(false);
144 fc.setFileFilter(new FileFilter() {
145 @Override public boolean accept(File f) {
146 return f.isDirectory() || f.getName().toLowerCase().endsWith(".jpg");
147 }
148 @Override public String getDescription() {
149 return tr("JPEG images (*.jpg)");
150 }
151 });
152 fc.showOpenDialog(Main.parent);
153 File[] sel = fc.getSelectedFiles();
154 if (sel == null || sel.length == 0)
155 return;
156 LinkedList<File> files = new LinkedList<File>();
157 addRecursiveFiles(files, sel);
158 Main.pref.put("tagimages.lastdirectory", fc.getCurrentDirectory().getPath());
159 GeoImageLayer.create(files, GpxLayer.this);
160 }
161
162 private void addRecursiveFiles(LinkedList<File> files, File[] sel) {
163 for (File f : sel) {
164 if (f.isDirectory())
165 addRecursiveFiles(files, f.listFiles());
166 else if (f.getName().toLowerCase().endsWith(".jpg"))
167 files.add(f);
168 }
169 }
170 });
171
172 if (Main.applet)
173 return new Component[] {
174 new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)),
175 new JMenuItem(new LayerListDialog.DeleteLayerAction(this)),
176 new JSeparator(),
177 color,
178 line,
179 new JMenuItem(new ConvertToDataLayerAction()),
180 new JSeparator(),
181 new JMenuItem(new RenameLayerAction(associatedFile, this)),
182 new JSeparator(),
183 new JMenuItem(new LayerListPopup.InfoAction(this))};
184 return new Component[] {
185 new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)),
186 new JMenuItem(new LayerListDialog.DeleteLayerAction(this)),
187 new JSeparator(),
188 new JMenuItem(new SaveAction(this)),
189 new JMenuItem(new SaveAsAction(this)),
190 // new JMenuItem(new UploadTraceAction()),
191 color,
192 line,
193 tagimage,
194 new JMenuItem(new ConvertToDataLayerAction()),
195 new JSeparator(),
196 new JMenuItem(new RenameLayerAction(associatedFile, this)),
197 new JSeparator(),
198 new JMenuItem(new LayerListPopup.InfoAction(this))};
199 }
200
201 @Override public String getToolTipText() {
202 StringBuilder info = new StringBuilder().append("<html>");
203
204 info.append(trn("{0} track, ", "{0} tracks, ",
205 data.tracks.size(), data.tracks.size()))
206 .append(trn("{0} route, ", "{0} routes, ",
207 data.routes.size(), data.routes.size()))
208 .append(trn("{0} waypoint", "{0} waypoints",
209 data.waypoints.size(), data.waypoints.size()))
210 .append("<br />");
211
212 if (data.attr.containsKey("name"))
213 info.append(tr("Name: {0}", data.attr.get("name")))
214 .append("<br />");
215
216 if (data.attr.containsKey("desc"))
217 info.append(tr("Description: {0}", data.attr.get("desc")))
218 .append("<br />");
219
220 return info.append("</html>").toString();
221 }
222
223 @Override public boolean isMergable(Layer other) {
224 return other instanceof GpxLayer;
225 }
226
227 @Override public void mergeFrom(Layer from) {
228 data.mergeFrom(((GpxLayer)from).data);
229 }
230
231 @Override public void paint(Graphics g, MapView mv) {
232 String gpsCol = Main.pref.get("color.gps point");
233 String gpsColSpecial = Main.pref.get("color.layer "+name);
234 if (!gpsColSpecial.equals("")) {
235 g.setColor(ColorHelper.html2color(gpsColSpecial));
236 } else if (!gpsCol.equals("")) {
237 g.setColor(ColorHelper.html2color(gpsCol));
238 } else{
239 g.setColor(Color.GRAY);
240 }
241
242 boolean forceLines = Main.pref.getBoolean("draw.rawgps.lines.force");
243 boolean lines = Main.pref.getBoolean("draw.rawgps.lines");
244 String linesKey = "draw.rawgps.lines.layer "+name;
245 if (Main.pref.hasKey(linesKey))
246 lines = Main.pref.getBoolean(linesKey);
247 boolean large = Main.pref.getBoolean("draw.rawgps.large");
248
249 Point old = null;
250 for (GpxTrack trk : data.tracks) {
251 if (!forceLines) {
252 old = null;
253 }
254 for (Collection<WayPoint> segment : trk.trackSegs) {
255 for (WayPoint trkPnt : segment) {
256 if (Double.isNaN(trkPnt.latlon.lat()) || Double.isNaN(trkPnt.latlon.lon()))
257 continue;
258 Point screen = mv.getPoint(trkPnt.eastNorth);
259 if (lines && old != null) {
260 g.drawLine(old.x, old.y, screen.x, screen.y);
261 } else if (!large) {
262 g.drawRect(screen.x, screen.y, 0, 0);
263 }
264 if (large)
265 g.fillRect(screen.x-1, screen.y-1, 3, 3);
266 old = screen;
267 }
268 }
269 }
270 }
271
272 @Override public void visitBoundingBox(BoundingXYVisitor v) {
273 for (WayPoint p : data.waypoints)
274 v.visit(p.eastNorth);
275
276 for (GpxRoute rte : data.routes) {
277 Collection<WayPoint> r = rte.routePoints;
278 for (WayPoint p : r) {
279 v.visit(p.eastNorth);
280 }
281 }
282
283 for (GpxTrack trk : data.tracks) {
284 for (Collection<WayPoint> seg : trk.trackSegs) {
285 for (WayPoint p : seg) {
286 v.visit(p.eastNorth);
287 }
288 }
289 }
290 }
291
292 public class UploadTraceAction extends AbstractAction {
293 public UploadTraceAction() {
294 super(tr("Upload this trace..."), ImageProvider.get("uploadtrace"));
295 }
296 public void actionPerformed(ActionEvent e) {
297 JPanel msg = new JPanel(new GridBagLayout());
298 msg.add(new JLabel(tr("<html>This functionality has been added only recently. Please<br>"+
299 "use with care and check if it works as expected.</html>")), GBC.eop());
300 ButtonGroup bg = new ButtonGroup();
301 JRadioButton c1 = null;
302 JRadioButton c2 = null;
303
304 //TODO
305 //check whether data comes from server
306 //check whether data changed sind last save/open
307
308 c1 = new JRadioButton(tr("Upload track filtered by JOSM"), true);
309 c2 = new JRadioButton(tr("Upload raw file: "), false);
310 c2.setEnabled(false);
311 c1.setEnabled(false);
312 bg.add(c1);
313 bg.add(c2);
314
315 msg.add(c1, GBC.eol());
316 msg.add(c2, GBC.eop());
317
318
319 JLabel description = new JLabel((String) data.attr.get("desc"));
320 JTextField tags = new JTextField();
321 tags.setText((String) data.attr.get("keywords"));
322 msg.add(new JLabel(tr("Description:")), GBC.std());
323 msg.add(description, GBC.eol().fill(GBC.HORIZONTAL));
324 msg.add(new JLabel(tr("Tags (keywords in GPX):")), GBC.std());
325 msg.add(tags, GBC.eol().fill(GBC.HORIZONTAL));
326 JCheckBox c3 = new JCheckBox("public");
327 msg.add(c3, GBC.eop());
328 msg.add(new JLabel("Please ensure that you don't upload your traces twice."), GBC.eop());
329
330 int answer = JOptionPane.showConfirmDialog(Main.parent, msg, tr("GPX-Upload"), JOptionPane.OK_CANCEL_OPTION);
331 if (answer == JOptionPane.OK_OPTION)
332 {
333 try {
334 String version = Main.pref.get("osm-server.version", "0.5");
335 URL url = new URL(Main.pref.get("osm-server.url") +
336 "/" + version + "/gpx/create");
337
338 // create a boundary string
339 String boundary = MultiPartFormOutputStream.createBoundary();
340 URLConnection urlConn = MultiPartFormOutputStream.createConnection(url);
341 urlConn.setRequestProperty("Accept", "*/*");
342 urlConn.setRequestProperty("Content-Type",
343 MultiPartFormOutputStream.getContentType(boundary));
344 // set some other request headers...
345 urlConn.setRequestProperty("Connection", "Keep-Alive");
346 urlConn.setRequestProperty("Cache-Control", "no-cache");
347 // no need to connect cuz getOutputStream() does it
348 MultiPartFormOutputStream out =
349 new MultiPartFormOutputStream(urlConn.getOutputStream(), boundary);
350 out.writeField("description", description.getText());
351 out.writeField("tags", tags.getText());
352 out.writeField("public", (c3.getSelectedObjects() != null) ? "1" : "0");
353 // upload a file
354 // out.writeFile("gpx_file", "text/xml", associatedFile);
355 // can also write bytes directly
356 // out.writeFile("myFile", "text/plain", "C:\\test.txt",
357 // "This is some file text.".getBytes("ASCII"));
358 File tmp = File.createTempFile("josm", "tmp.gpx");
359 FileOutputStream outs = new FileOutputStream(tmp);
360 new GpxWriter(outs).write(data);
361 outs.close();
362 FileInputStream ins = new FileInputStream(tmp);
363 new GpxWriter(System.out).write(data);
364 out.writeFile("gpx_file", "text/xml", data.storageFile.getName(), ins);
365 out.close();
366 tmp.delete();
367 // read response from server
368 BufferedReader in = new BufferedReader(
369 new InputStreamReader(urlConn.getInputStream()));
370 String line = "";
371 while((line = in.readLine()) != null) {
372 System.out.println(line);
373 }
374 in.close();
375
376 //TODO check response
377 /* int retCode = urlConn.getResponseCode();
378 System.out.println("got return: " + retCode);
379 String retMsg = urlConn.getResponseMessage();
380 urlConn.disconnect();
381 if (retCode != 200) {
382 // Look for a detailed error message from the server
383 if (urlConn.getHeaderField("Error") != null)
384 retMsg += "\n" + urlConn.getHeaderField("Error");
385
386 // Report our error
387 ByteArrayOutputStream o = new ByteArrayOutputStream();
388 System.out.println(new String(o.toByteArray(), "UTF-8").toString());
389 throw new RuntimeException(retCode+" "+retMsg);
390 }
391 */
392 } catch (UnknownHostException ex) {
393 throw new RuntimeException(tr("Unknown host")+": "+ex.getMessage(), ex);
394 } catch (Exception ex) {
395 //if (cancel)
396 // return; // assume cancel
397 if (ex instanceof RuntimeException)
398 throw (RuntimeException)ex;
399 throw new RuntimeException(ex.getMessage(), ex);
400 }
401 }
402 }
403 }
404
405
406 public class ConvertToDataLayerAction extends AbstractAction {
407 public ConvertToDataLayerAction() {
408 super(tr("Convert to data layer"), ImageProvider.get("converttoosm"));
409 }
410 public void actionPerformed(ActionEvent e) {
411 JPanel msg = new JPanel(new GridBagLayout());
412 msg.add(new JLabel(tr("<html>Upload of unprocessed GPS data as map data is considered harmful.<br>If you want to upload traces, look here:")), GBC.eol());
413 msg.add(new UrlLabel(tr("http://www.openstreetmap.org/traces")), GBC.eop());
414 if (!DontShowAgainInfo.show("convert_to_data", msg))
415 return;
416 DataSet ds = new DataSet();
417 for (GpxTrack trk : data.tracks) {
418 for (Collection<WayPoint> segment : trk.trackSegs) {
419 Way w = new Way();
420 for (WayPoint p : segment) {
421 Node n = new Node(p.latlon);
422 ds.nodes.add(n);
423 w.nodes.add(n);
424 }
425 ds.ways.add(w);
426 }
427 }
428 Main.main.addLayer(new OsmDataLayer(ds, tr("Converted from: {0}", GpxLayer.this.name), null));
429 Main.main.removeLayer(GpxLayer.this);
430 }
431 }
432
433}
Note: See TracBrowser for help on using the repository browser.