Changeset 1169 in josm for trunk/src/org/openstreetmap/josm/gui/layer/GpxLayer.java
- Timestamp:
- 2008-12-23T15:07:05+01:00 (17 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/gui/layer/GpxLayer.java
r1165 r1169 80 80 81 81 public class GpxLayer extends Layer { 82 public GpxData data;83 private final GpxLayer me;84 protected static final double PHI = Math.toRadians(15);85 private boolean computeCacheInSync;86 private int computeCacheMaxLineLengthUsed;87 private Color computeCacheColorUsed;88 private boolean computeCacheColored;89 90 public GpxLayer(GpxData d) {91 super((String) d.attr.get("name"));92 data = d;93 me = this;94 computeCacheInSync = false;95 }96 97 public GpxLayer(GpxData d, String name) {98 this(d);99 this.name = name;100 }101 102 @Override public Icon getIcon() {103 return ImageProvider.get("layer", "gpx_small");104 }105 106 @Override public Object getInfoComponent() {107 return getToolTipText();108 }109 110 @Override public Component[] getMenuEntries() {111 JMenuItem line = new JMenuItem(tr("Customize line drawing"), ImageProvider.get("mapmode/addsegment"));112 line.addActionListener(new ActionListener() {113 public void actionPerformed(ActionEvent e) {114 JRadioButton[] r = new JRadioButton[3];115 r[0] = new JRadioButton(tr("Use global settings."));116 r[1] = new JRadioButton(tr("Draw lines between points for this layer."));117 r[2] = new JRadioButton(tr("Do not draw lines between points for this layer."));118 ButtonGroup group = new ButtonGroup();119 Box panel = Box.createVerticalBox();120 for (JRadioButton b : r) {121 group.add(b);122 panel.add(b);123 }124 String propName = "draw.rawgps.lines.layer "+name;125 if (Main.pref.hasKey(propName))126 group.setSelected(r[Main.pref.getBoolean(propName) ? 1:2].getModel(), true);127 else128 group.setSelected(r[0].getModel(), true);129 int answer = JOptionPane.showConfirmDialog(Main.parent, panel, tr("Select line drawing options"), JOptionPane.OK_CANCEL_OPTION);130 if (answer == JOptionPane.CANCEL_OPTION)131 return;132 if (group.getSelection() == r[0].getModel())133 Main.pref.put(propName, null);134 else135 Main.pref.put(propName, group.getSelection() == r[1].getModel());136 Main.map.repaint();137 }138 });139 140 JMenuItem color = new JMenuItem(tr("Customize Color"), ImageProvider.get("colorchooser"));141 color.putClientProperty("help", "Action/LayerCustomizeColor");142 color.addActionListener(new ActionListener() {143 public void actionPerformed(ActionEvent e) {144 JColorChooser c = new JColorChooser(Main.pref.getColor(marktr("gps point"), "layer "+name, Color.gray));145 Object[] options = new Object[]{tr("OK"), tr("Cancel"), tr("Default")};146 int answer = JOptionPane.showOptionDialog(Main.parent, c, tr("Choose a color"), JOptionPane.OK_CANCEL_OPTION,147 JOptionPane.PLAIN_MESSAGE, null, options, options[0]);148 switch (answer) {149 case 0:150 Main.pref.putColor("layer "+name, c.getColor());151 break;152 case 1:153 return;154 case 2:155 Main.pref.putColor("layer "+name, null);156 break;157 }158 Main.map.repaint();159 }160 });161 162 JMenuItem markersFromNamedTrackpoints = new JMenuItem(tr("Markers From Named Points"), ImageProvider.get("addmarkers"));163 markersFromNamedTrackpoints.putClientProperty("help", "Action/MarkersFromNamedPoints");164 markersFromNamedTrackpoints.addActionListener(new ActionListener() {165 public void actionPerformed(ActionEvent e) {166 GpxData namedTrackPoints = new GpxData();167 for (GpxTrack track : data.tracks)168 for (Collection<WayPoint> seg : track.trackSegs)169 for (WayPoint point : seg)170 if (point.attr.containsKey("name") || point.attr.containsKey("desc"))171 namedTrackPoints.waypoints.add(point);172 173 MarkerLayer ml = new MarkerLayer(namedTrackPoints, tr("Named Trackpoints from {0}", name), associatedFile, me);174 if (ml.data.size() > 0) {175 Main.main.addLayer(ml);176 }177 }178 });179 180 JMenuItem importAudio = new JMenuItem(tr("Import Audio"), ImageProvider.get("importaudio"));181 importAudio.putClientProperty("help", "ImportAudio");182 importAudio.addActionListener(new ActionListener() {183 public void actionPerformed(ActionEvent e) {184 String dir = Main.pref.get("markers.lastaudiodirectory");185 JFileChooser fc = new JFileChooser(dir);186 fc.setFileSelectionMode(JFileChooser.FILES_ONLY);187 fc.setAcceptAllFileFilterUsed(false);188 fc.setFileFilter(new FileFilter(){189 @Override public boolean accept(File f) {190 return f.isDirectory() || f.getName().toLowerCase().endsWith(".wav");191 }192 @Override public String getDescription() {193 return tr("Wave Audio files (*.wav)");194 }195 });196 fc.setMultiSelectionEnabled(true);197 if(fc.showOpenDialog(Main.parent) == JFileChooser.APPROVE_OPTION) {198 if (!fc.getCurrentDirectory().getAbsolutePath().equals(dir))199 Main.pref.put("markers.lastaudiodirectory", fc.getCurrentDirectory().getAbsolutePath());200 201 // FIXME: properly support multi-selection here.202 // Calling importAudio several times just creates N maker layers, which203 // is sub-optimal.204 File sel[] = fc.getSelectedFiles();205 if(sel != null)206 for (int i = 0; i < sel.length; i++)207 importAudio(sel[i]);208 209 Main.map.repaint();210 }211 }212 });213 214 JMenuItem tagimage = new JMenuItem(tr("Import images"), ImageProvider.get("tagimages"));215 tagimage.putClientProperty("help", "Action/ImportImages");216 tagimage.addActionListener(new ActionListener() {217 public void actionPerformed(ActionEvent e) {218 JFileChooser fc = new JFileChooser(Main.pref.get("tagimages.lastdirectory"));219 fc.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);220 fc.setMultiSelectionEnabled(true);221 fc.setAcceptAllFileFilterUsed(false);222 fc.setFileFilter(new FileFilter() {223 @Override public boolean accept(File f) {224 return f.isDirectory() || f.getName().toLowerCase().endsWith(".jpg");225 }226 @Override public String getDescription() {227 return tr("JPEG images (*.jpg)");228 }229 });230 fc.showOpenDialog(Main.parent);231 File[] sel = fc.getSelectedFiles();232 if (sel == null || sel.length == 0)233 return;234 LinkedList<File> files = new LinkedList<File>();235 addRecursiveFiles(files, sel);236 Main.pref.put("tagimages.lastdirectory", fc.getCurrentDirectory().getPath());237 GeoImageLayer.create(files, GpxLayer.this);238 }239 240 private void addRecursiveFiles(LinkedList<File> files, File[] sel) {241 for (File f : sel) {242 if (f.isDirectory())243 addRecursiveFiles(files, f.listFiles());244 else if (f.getName().toLowerCase().endsWith(".jpg"))245 files.add(f);246 }247 }248 });249 250 if (Main.applet)251 return new Component[] {252 new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)),253 new JMenuItem(new LayerListDialog.DeleteLayerAction(this)),254 new JSeparator(),255 color,256 line,257 new JMenuItem(new ConvertToDataLayerAction()),258 new JSeparator(),259 new JMenuItem(new RenameLayerAction(associatedFile, this)),260 new JSeparator(),261 new JMenuItem(new LayerListPopup.InfoAction(this))};262 return new Component[] {263 new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)),264 new JMenuItem(new LayerListDialog.DeleteLayerAction(this)),265 new JSeparator(),266 new JMenuItem(new SaveAction(this)),267 new JMenuItem(new SaveAsAction(this)),268 // new JMenuItem(new UploadTraceAction()),269 color,270 line,271 tagimage,272 importAudio,273 markersFromNamedTrackpoints,274 new JMenuItem(new ConvertToDataLayerAction()),82 public GpxData data; 83 private final GpxLayer me; 84 protected static final double PHI = Math.toRadians(15); 85 private boolean computeCacheInSync; 86 private int computeCacheMaxLineLengthUsed; 87 private Color computeCacheColorUsed; 88 private boolean computeCacheColored; 89 90 public GpxLayer(GpxData d) { 91 super((String) d.attr.get("name")); 92 data = d; 93 me = this; 94 computeCacheInSync = false; 95 } 96 97 public GpxLayer(GpxData d, String name) { 98 this(d); 99 this.name = name; 100 } 101 102 @Override public Icon getIcon() { 103 return ImageProvider.get("layer", "gpx_small"); 104 } 105 106 @Override public Object getInfoComponent() { 107 return getToolTipText(); 108 } 109 110 @Override public Component[] getMenuEntries() { 111 JMenuItem line = new JMenuItem(tr("Customize line drawing"), ImageProvider.get("mapmode/addsegment")); 112 line.addActionListener(new ActionListener() { 113 public void actionPerformed(ActionEvent e) { 114 JRadioButton[] r = new JRadioButton[3]; 115 r[0] = new JRadioButton(tr("Use global settings.")); 116 r[1] = new JRadioButton(tr("Draw lines between points for this layer.")); 117 r[2] = new JRadioButton(tr("Do not draw lines between points for this layer.")); 118 ButtonGroup group = new ButtonGroup(); 119 Box panel = Box.createVerticalBox(); 120 for (JRadioButton b : r) { 121 group.add(b); 122 panel.add(b); 123 } 124 String propName = "draw.rawgps.lines.layer "+name; 125 if (Main.pref.hasKey(propName)) 126 group.setSelected(r[Main.pref.getBoolean(propName) ? 1:2].getModel(), true); 127 else 128 group.setSelected(r[0].getModel(), true); 129 int answer = JOptionPane.showConfirmDialog(Main.parent, panel, tr("Select line drawing options"), JOptionPane.OK_CANCEL_OPTION); 130 if (answer == JOptionPane.CANCEL_OPTION) 131 return; 132 if (group.getSelection() == r[0].getModel()) 133 Main.pref.put(propName, null); 134 else 135 Main.pref.put(propName, group.getSelection() == r[1].getModel()); 136 Main.map.repaint(); 137 } 138 }); 139 140 JMenuItem color = new JMenuItem(tr("Customize Color"), ImageProvider.get("colorchooser")); 141 color.putClientProperty("help", "Action/LayerCustomizeColor"); 142 color.addActionListener(new ActionListener() { 143 public void actionPerformed(ActionEvent e) { 144 JColorChooser c = new JColorChooser(Main.pref.getColor(marktr("gps point"), "layer "+name, Color.gray)); 145 Object[] options = new Object[]{tr("OK"), tr("Cancel"), tr("Default")}; 146 int answer = JOptionPane.showOptionDialog(Main.parent, c, tr("Choose a color"), JOptionPane.OK_CANCEL_OPTION, 147 JOptionPane.PLAIN_MESSAGE, null, options, options[0]); 148 switch (answer) { 149 case 0: 150 Main.pref.putColor("layer "+name, c.getColor()); 151 break; 152 case 1: 153 return; 154 case 2: 155 Main.pref.putColor("layer "+name, null); 156 break; 157 } 158 Main.map.repaint(); 159 } 160 }); 161 162 JMenuItem markersFromNamedTrackpoints = new JMenuItem(tr("Markers From Named Points"), ImageProvider.get("addmarkers")); 163 markersFromNamedTrackpoints.putClientProperty("help", "Action/MarkersFromNamedPoints"); 164 markersFromNamedTrackpoints.addActionListener(new ActionListener() { 165 public void actionPerformed(ActionEvent e) { 166 GpxData namedTrackPoints = new GpxData(); 167 for (GpxTrack track : data.tracks) 168 for (Collection<WayPoint> seg : track.trackSegs) 169 for (WayPoint point : seg) 170 if (point.attr.containsKey("name") || point.attr.containsKey("desc")) 171 namedTrackPoints.waypoints.add(point); 172 173 MarkerLayer ml = new MarkerLayer(namedTrackPoints, tr("Named Trackpoints from {0}", name), associatedFile, me); 174 if (ml.data.size() > 0) { 175 Main.main.addLayer(ml); 176 } 177 } 178 }); 179 180 JMenuItem importAudio = new JMenuItem(tr("Import Audio"), ImageProvider.get("importaudio")); 181 importAudio.putClientProperty("help", "ImportAudio"); 182 importAudio.addActionListener(new ActionListener() { 183 public void actionPerformed(ActionEvent e) { 184 String dir = Main.pref.get("markers.lastaudiodirectory"); 185 JFileChooser fc = new JFileChooser(dir); 186 fc.setFileSelectionMode(JFileChooser.FILES_ONLY); 187 fc.setAcceptAllFileFilterUsed(false); 188 fc.setFileFilter(new FileFilter(){ 189 @Override public boolean accept(File f) { 190 return f.isDirectory() || f.getName().toLowerCase().endsWith(".wav"); 191 } 192 @Override public String getDescription() { 193 return tr("Wave Audio files (*.wav)"); 194 } 195 }); 196 fc.setMultiSelectionEnabled(true); 197 if(fc.showOpenDialog(Main.parent) == JFileChooser.APPROVE_OPTION) { 198 if (!fc.getCurrentDirectory().getAbsolutePath().equals(dir)) 199 Main.pref.put("markers.lastaudiodirectory", fc.getCurrentDirectory().getAbsolutePath()); 200 201 // FIXME: properly support multi-selection here. 202 // Calling importAudio several times just creates N maker layers, which 203 // is sub-optimal. 204 File sel[] = fc.getSelectedFiles(); 205 if(sel != null) 206 for (int i = 0; i < sel.length; i++) 207 importAudio(sel[i]); 208 209 Main.map.repaint(); 210 } 211 } 212 }); 213 214 JMenuItem tagimage = new JMenuItem(tr("Import images"), ImageProvider.get("tagimages")); 215 tagimage.putClientProperty("help", "Action/ImportImages"); 216 tagimage.addActionListener(new ActionListener() { 217 public void actionPerformed(ActionEvent e) { 218 JFileChooser fc = new JFileChooser(Main.pref.get("tagimages.lastdirectory")); 219 fc.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); 220 fc.setMultiSelectionEnabled(true); 221 fc.setAcceptAllFileFilterUsed(false); 222 fc.setFileFilter(new FileFilter() { 223 @Override public boolean accept(File f) { 224 return f.isDirectory() || f.getName().toLowerCase().endsWith(".jpg"); 225 } 226 @Override public String getDescription() { 227 return tr("JPEG images (*.jpg)"); 228 } 229 }); 230 fc.showOpenDialog(Main.parent); 231 File[] sel = fc.getSelectedFiles(); 232 if (sel == null || sel.length == 0) 233 return; 234 LinkedList<File> files = new LinkedList<File>(); 235 addRecursiveFiles(files, sel); 236 Main.pref.put("tagimages.lastdirectory", fc.getCurrentDirectory().getPath()); 237 GeoImageLayer.create(files, GpxLayer.this); 238 } 239 240 private void addRecursiveFiles(LinkedList<File> files, File[] sel) { 241 for (File f : sel) { 242 if (f.isDirectory()) 243 addRecursiveFiles(files, f.listFiles()); 244 else if (f.getName().toLowerCase().endsWith(".jpg")) 245 files.add(f); 246 } 247 } 248 }); 249 250 if (Main.applet) 251 return new Component[] { 252 new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)), 253 new JMenuItem(new LayerListDialog.DeleteLayerAction(this)), 254 new JSeparator(), 255 color, 256 line, 257 new JMenuItem(new ConvertToDataLayerAction()), 258 new JSeparator(), 259 new JMenuItem(new RenameLayerAction(associatedFile, this)), 260 new JSeparator(), 261 new JMenuItem(new LayerListPopup.InfoAction(this))}; 262 return new Component[] { 263 new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)), 264 new JMenuItem(new LayerListDialog.DeleteLayerAction(this)), 265 new JSeparator(), 266 new JMenuItem(new SaveAction(this)), 267 new JMenuItem(new SaveAsAction(this)), 268 // new JMenuItem(new UploadTraceAction()), 269 color, 270 line, 271 tagimage, 272 importAudio, 273 markersFromNamedTrackpoints, 274 new JMenuItem(new ConvertToDataLayerAction()), 275 275 new JMenuItem(new DownloadAlongTrackAction()), 276 new JSeparator(),277 new JMenuItem(new RenameLayerAction(associatedFile, this)),278 new JSeparator(),279 new JMenuItem(new LayerListPopup.InfoAction(this))};280 }281 282 @Override public String getToolTipText() {283 StringBuilder info = new StringBuilder().append("<html>");284 285 info.append(trn("{0} track, ", "{0} tracks, ",286 data.tracks.size(), data.tracks.size())).append(trn("{0} route, ", "{0} routes, ",287 data.routes.size(), data.routes.size())).append(trn("{0} waypoint", "{0} waypoints",288 data.waypoints.size(), data.waypoints.size())).append("<br>");289 290 if (data.attr.containsKey("name"))291 info.append(tr("Name: {0}", data.attr.get("name"))).append("<br>");292 293 if (data.attr.containsKey("desc"))294 info.append(tr("Description: {0}", data.attr.get("desc"))).append("<br>");295 296 if(data.tracks.size() > 0){297 boolean first = true;298 WayPoint earliest = null, latest = null;299 300 for(GpxTrack trk: data.tracks){301 for(Collection<WayPoint> seg:trk.trackSegs){302 for(WayPoint pnt:seg){303 if(first){304 latest = earliest = pnt;305 first = false;306 }else{307 if(pnt.compareTo(earliest) < 0){308 earliest = pnt;309 }else{310 latest = pnt;311 }312 }313 }314 }315 }316 if (earliest != null && latest != null) {317 DateFormat df = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT);318 info.append(tr("Timespan: ") + df.format(new Date((long)(earliest.time * 1000))) + " - "319 + df.format(new Date((long)(latest.time * 1000))));320 int diff = (int)(latest.time - earliest.time);321 info.append(" (" + (diff / 3600) + ":" + ((diff % 3600)/60) + ")");322 info.append("<br>");323 }324 }325 info.append(tr("Length: ") + new DecimalFormat("#0.00").format(data.length() / 1000) + "km");326 info.append("<br>");327 328 return info.append("</html>").toString();329 }330 331 @Override public boolean isMergable(Layer other) {332 return other instanceof GpxLayer;333 }334 335 @Override public void mergeFrom(Layer from) {336 data.mergeFrom(((GpxLayer)from).data);337 computeCacheInSync = false;338 }339 340 private static Color[] colors = new Color[256];341 static {342 for (int i = 0; i < colors.length; i++) {343 colors[i] = Color.getHSBColor(i/300.0f, 1, 1);344 }345 }346 347 // lookup array to draw arrows without doing any math348 private static int ll0 = 9;349 private static int sl4 = 5;350 private static int sl9 = 3;351 private static int[][] dir = {352 {+sl4,+ll0,+ll0,+sl4},353 {-sl9,+ll0,+sl9,+ll0},354 {-ll0,+sl4,-sl4,+ll0},355 {-ll0,-sl9,-ll0,+sl9},356 {-sl4,-ll0,-ll0,-sl4},357 {+sl9,-ll0,-sl9,-ll0},358 {+ll0,-sl4,+sl4,-ll0},359 {+ll0,+sl9,+ll0,-sl9},360 {+sl4,+ll0,+ll0,+sl4},361 {-sl9,+ll0,+sl9,+ll0},362 {-ll0,+sl4,-sl4,+ll0},363 {-ll0,-sl9,-ll0,+sl9}364 };365 366 @Override public void paint(Graphics g, MapView mv) {367 368 /****************************************************************369 ********** STEP 1 - GET CONFIG VALUES **************************370 ****************************************************************/371 // Long startTime = System.currentTimeMillis();372 Color neutralColor = Main.pref.getColor(marktr("gps point"), "layer "+name, Color.GRAY);373 boolean forceLines = Main.pref.getBoolean("draw.rawgps.lines.force"); // also draw lines between points belonging to different segments374 boolean direction = Main.pref.getBoolean("draw.rawgps.direction"); // draw direction arrows on the lines375 int maxLineLength = -1;376 try {377 maxLineLength = Integer.parseInt(Main.pref.get("draw.rawgps.max-line-length", "-1")); // don't draw lines if longer than x meters378 } catch (java.lang.NumberFormatException e) {379 Main.pref.put("draw.rawgps.max-line-length", "-1");380 }381 boolean lines = Main.pref.getBoolean("draw.rawgps.lines"); // draw line between points, global setting382 String linesKey = "draw.rawgps.lines.layer "+name;383 if (Main.pref.hasKey(linesKey))384 lines = Main.pref.getBoolean(linesKey); // draw lines, per-layer setting385 boolean large = Main.pref.getBoolean("draw.rawgps.large"); // paint large dots for points386 boolean colored = Main.pref.getBoolean("draw.rawgps.colors"); // color the lines387 boolean alternatedirection = Main.pref.getBoolean("draw.rawgps.alternatedirection"); // paint direction arrow with alternate math. may be faster388 int delta = 0;389 try {390 delta = Integer.parseInt(Main.pref.get("draw.rawgps.min-arrow-distance", "0")); // don't draw arrows nearer to each other than this391 } catch (java.lang.NumberFormatException e) {392 Main.pref.put("draw.rawgps.min-arrow-distance", "0");393 }394 395 /****************************************************************396 ********** STEP 2a - CHECK CACHE VALIDITY **********************397 ****************************************************************/398 if (computeCacheInSync && ((computeCacheMaxLineLengthUsed != maxLineLength) ||399 (!neutralColor.equals(computeCacheColorUsed)) ||400 (computeCacheColored != colored))) {276 new JSeparator(), 277 new JMenuItem(new RenameLayerAction(associatedFile, this)), 278 new JSeparator(), 279 new JMenuItem(new LayerListPopup.InfoAction(this))}; 280 } 281 282 @Override public String getToolTipText() { 283 StringBuilder info = new StringBuilder().append("<html>"); 284 285 info.append(trn("{0} track, ", "{0} tracks, ", 286 data.tracks.size(), data.tracks.size())).append(trn("{0} route, ", "{0} routes, ", 287 data.routes.size(), data.routes.size())).append(trn("{0} waypoint", "{0} waypoints", 288 data.waypoints.size(), data.waypoints.size())).append("<br>"); 289 290 if (data.attr.containsKey("name")) 291 info.append(tr("Name: {0}", data.attr.get("name"))).append("<br>"); 292 293 if (data.attr.containsKey("desc")) 294 info.append(tr("Description: {0}", data.attr.get("desc"))).append("<br>"); 295 296 if(data.tracks.size() > 0){ 297 boolean first = true; 298 WayPoint earliest = null, latest = null; 299 300 for(GpxTrack trk: data.tracks){ 301 for(Collection<WayPoint> seg:trk.trackSegs){ 302 for(WayPoint pnt:seg){ 303 if(first){ 304 latest = earliest = pnt; 305 first = false; 306 }else{ 307 if(pnt.compareTo(earliest) < 0){ 308 earliest = pnt; 309 }else{ 310 latest = pnt; 311 } 312 } 313 } 314 } 315 } 316 if (earliest != null && latest != null) { 317 DateFormat df = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT); 318 info.append(tr("Timespan: ") + df.format(new Date((long)(earliest.time * 1000))) + " - " 319 + df.format(new Date((long)(latest.time * 1000)))); 320 int diff = (int)(latest.time - earliest.time); 321 info.append(" (" + (diff / 3600) + ":" + ((diff % 3600)/60) + ")"); 322 info.append("<br>"); 323 } 324 } 325 info.append(tr("Length: ") + new DecimalFormat("#0.00").format(data.length() / 1000) + "km"); 326 info.append("<br>"); 327 328 return info.append("</html>").toString(); 329 } 330 331 @Override public boolean isMergable(Layer other) { 332 return other instanceof GpxLayer; 333 } 334 335 @Override public void mergeFrom(Layer from) { 336 data.mergeFrom(((GpxLayer)from).data); 337 computeCacheInSync = false; 338 } 339 340 private static Color[] colors = new Color[256]; 341 static { 342 for (int i = 0; i < colors.length; i++) { 343 colors[i] = Color.getHSBColor(i/300.0f, 1, 1); 344 } 345 } 346 347 // lookup array to draw arrows without doing any math 348 private static int ll0 = 9; 349 private static int sl4 = 5; 350 private static int sl9 = 3; 351 private static int[][] dir = { 352 {+sl4,+ll0,+ll0,+sl4}, 353 {-sl9,+ll0,+sl9,+ll0}, 354 {-ll0,+sl4,-sl4,+ll0}, 355 {-ll0,-sl9,-ll0,+sl9}, 356 {-sl4,-ll0,-ll0,-sl4}, 357 {+sl9,-ll0,-sl9,-ll0}, 358 {+ll0,-sl4,+sl4,-ll0}, 359 {+ll0,+sl9,+ll0,-sl9}, 360 {+sl4,+ll0,+ll0,+sl4}, 361 {-sl9,+ll0,+sl9,+ll0}, 362 {-ll0,+sl4,-sl4,+ll0}, 363 {-ll0,-sl9,-ll0,+sl9} 364 }; 365 366 @Override public void paint(Graphics g, MapView mv) { 367 368 /**************************************************************** 369 ********** STEP 1 - GET CONFIG VALUES ************************** 370 ****************************************************************/ 371 // Long startTime = System.currentTimeMillis(); 372 Color neutralColor = Main.pref.getColor(marktr("gps point"), "layer "+name, Color.GRAY); 373 boolean forceLines = Main.pref.getBoolean("draw.rawgps.lines.force"); // also draw lines between points belonging to different segments 374 boolean direction = Main.pref.getBoolean("draw.rawgps.direction"); // draw direction arrows on the lines 375 int maxLineLength = -1; 376 try { 377 maxLineLength = Integer.parseInt(Main.pref.get("draw.rawgps.max-line-length", "-1")); // don't draw lines if longer than x meters 378 } catch (java.lang.NumberFormatException e) { 379 Main.pref.put("draw.rawgps.max-line-length", "-1"); 380 } 381 boolean lines = Main.pref.getBoolean("draw.rawgps.lines"); // draw line between points, global setting 382 String linesKey = "draw.rawgps.lines.layer "+name; 383 if (Main.pref.hasKey(linesKey)) 384 lines = Main.pref.getBoolean(linesKey); // draw lines, per-layer setting 385 boolean large = Main.pref.getBoolean("draw.rawgps.large"); // paint large dots for points 386 boolean colored = Main.pref.getBoolean("draw.rawgps.colors"); // color the lines 387 boolean alternatedirection = Main.pref.getBoolean("draw.rawgps.alternatedirection"); // paint direction arrow with alternate math. may be faster 388 int delta = 0; 389 try { 390 delta = Integer.parseInt(Main.pref.get("draw.rawgps.min-arrow-distance", "0")); // don't draw arrows nearer to each other than this 391 } catch (java.lang.NumberFormatException e) { 392 Main.pref.put("draw.rawgps.min-arrow-distance", "0"); 393 } 394 395 /**************************************************************** 396 ********** STEP 2a - CHECK CACHE VALIDITY ********************** 397 ****************************************************************/ 398 if (computeCacheInSync && ((computeCacheMaxLineLengthUsed != maxLineLength) || 399 (!neutralColor.equals(computeCacheColorUsed)) || 400 (computeCacheColored != colored))) { 401 401 // System.out.println("(re-)computing gpx line styles, reason: CCIS=" + computeCacheInSync + " CCMLLU=" + (computeCacheMaxLineLengthUsed != maxLineLength) + " CCCU=" + (!neutralColor.equals(computeCacheColorUsed)) + " CCC=" + (computeCacheColored != colored)); 402 computeCacheMaxLineLengthUsed = maxLineLength;403 computeCacheInSync = false;404 computeCacheColorUsed = neutralColor;405 computeCacheColored = colored;406 }407 408 /****************************************************************409 ********** STEP 2b - RE-COMPUTE CACHE DATA *********************410 ****************************************************************/411 if (!computeCacheInSync) { // don't compute if the cache is good412 WayPoint oldWp = null;413 for (GpxTrack trk : data.tracks) {414 if (!forceLines) { // don't draw lines between segments, unless forced to415 oldWp = null;416 }417 for (Collection<WayPoint> segment : trk.trackSegs) {418 for (WayPoint trkPnt : segment) {419 if (Double.isNaN(trkPnt.latlon.lat()) || Double.isNaN(trkPnt.latlon.lon())) {420 continue;421 }422 if (oldWp != null) {423 double dist = trkPnt.latlon.greatCircleDistance(oldWp.latlon);424 double dtime = trkPnt.time - oldWp.time;425 double vel = dist/dtime;426 427 if (!colored) {428 trkPnt.speedLineColor = neutralColor;429 } else if (dtime <= 0 || vel < 0 || vel > 36) { // attn: bad case first430 trkPnt.speedLineColor = colors[255];431 } else {432 trkPnt.speedLineColor = colors[(int) (7*vel)];433 }434 if (maxLineLength == -1 || dist <= maxLineLength) {435 trkPnt.drawLine = true;436 trkPnt.dir = (int)(Math.atan2(-trkPnt.eastNorth.north()+oldWp.eastNorth.north(), trkPnt.eastNorth.east()-oldWp.eastNorth.east()) / Math.PI * 4 + 3.5); // crude but works437 } else {438 trkPnt.drawLine = false;439 }440 } else { // make sure we reset outdated data441 trkPnt.speedLineColor = colors[255];442 trkPnt.drawLine = false;443 }444 oldWp = trkPnt;445 }446 }447 }448 computeCacheInSync = true;449 }450 451 /****************************************************************452 ********** STEP 3a - DRAW LINES ********************************453 ****************************************************************/454 if (lines) {455 Point old = null;456 for (GpxTrack trk : data.tracks) {457 for (Collection<WayPoint> segment : trk.trackSegs) {458 for (WayPoint trkPnt : segment) {459 if (Double.isNaN(trkPnt.latlon.lat()) || Double.isNaN(trkPnt.latlon.lon()))460 continue;461 Point screen = mv.getPoint(trkPnt.eastNorth);462 if (trkPnt.drawLine) {463 // skip points that are on the same screenposition464 if (old != null && ((old.x != screen.x) || (old.y != screen.y))) {465 g.setColor(trkPnt.speedLineColor);466 g.drawLine(old.x, old.y, screen.x, screen.y);467 }468 }469 old = screen;470 } // end for trkpnt471 } // end for segment472 } // end for trk473 } // end if lines474 475 /****************************************************************476 ********** STEP 3b - DRAW NICE ARROWS **************************477 ****************************************************************/478 if (lines && direction && !alternatedirection) {479 Point old = null;480 Point oldA = null; // last arrow painted481 for (GpxTrack trk : data.tracks) {482 for (Collection<WayPoint> segment : trk.trackSegs) {483 for (WayPoint trkPnt : segment) {484 if (Double.isNaN(trkPnt.latlon.lat()) || Double.isNaN(trkPnt.latlon.lon()))485 continue;486 if (trkPnt.drawLine) {487 Point screen = mv.getPoint(trkPnt.eastNorth);488 // skip points that are on the same screenposition489 if (old != null && (oldA == null || screen.x < oldA.x-delta || screen.x > oldA.x+delta || screen.y < oldA.y-delta || screen.y > oldA.y+delta)) {490 g.setColor(trkPnt.speedLineColor);491 double t = Math.atan2(screen.y-old.y, screen.x-old.x) + Math.PI;492 g.drawLine(screen.x,screen.y, (int)(screen.x + 10*Math.cos(t-PHI)), (int)(screen.y493 + 10*Math.sin(t-PHI)));494 g.drawLine(screen.x,screen.y, (int)(screen.x + 10*Math.cos(t+PHI)), (int)(screen.y495 + 10*Math.sin(t+PHI)));496 oldA = screen;497 }498 old = screen;499 }500 } // end for trkpnt501 } // end for segment502 } // end for trk503 } // end if lines504 505 /****************************************************************506 ********** STEP 3c - DRAW FAST ARROWS **************************507 ****************************************************************/508 if (lines && direction && alternatedirection) {509 Point old = null;510 Point oldA = null; // last arrow painted511 for (GpxTrack trk : data.tracks) {512 for (Collection<WayPoint> segment : trk.trackSegs) {513 for (WayPoint trkPnt : segment) {514 if (Double.isNaN(trkPnt.latlon.lat()) || Double.isNaN(trkPnt.latlon.lon()))515 continue;516 if (trkPnt.drawLine) {517 Point screen = mv.getPoint(trkPnt.eastNorth);518 // skip points that are on the same screenposition519 if (old != null && (oldA == null || screen.x < oldA.x-delta || screen.x > oldA.x+delta || screen.y < oldA.y-delta || screen.y > oldA.y+delta)) {520 g.setColor(trkPnt.speedLineColor);521 g.drawLine(screen.x, screen.y, screen.x + dir[trkPnt.dir][0], screen.y + dir[trkPnt.dir][1]);522 g.drawLine(screen.x, screen.y, screen.x + dir[trkPnt.dir][2], screen.y + dir[trkPnt.dir][3]);523 oldA = screen;524 }525 old = screen;526 }527 } // end for trkpnt528 } // end for segment529 } // end for trk530 } // end if lines531 532 /****************************************************************533 ********** STEP 3d - DRAW LARGE POINTS *************************534 ****************************************************************/535 if (large) {536 g.setColor(neutralColor);537 for (GpxTrack trk : data.tracks) {538 for (Collection<WayPoint> segment : trk.trackSegs) {539 for (WayPoint trkPnt : segment) {540 if (Double.isNaN(trkPnt.latlon.lat()) || Double.isNaN(trkPnt.latlon.lon()))541 continue;542 Point screen = mv.getPoint(trkPnt.eastNorth);543 g.fillRect(screen.x-1, screen.y-1, 3, 3);544 } // end for trkpnt545 } // end for segment546 } // end for trk547 } // end if large548 549 /****************************************************************550 ********** STEP 3e - DRAW SMALL POINTS FOR LINES ***************551 ****************************************************************/552 if (!large && lines){553 g.setColor(neutralColor);554 for (GpxTrack trk : data.tracks) {555 for (Collection<WayPoint> segment : trk.trackSegs) {556 for (WayPoint trkPnt : segment) {557 if (Double.isNaN(trkPnt.latlon.lat()) || Double.isNaN(trkPnt.latlon.lon()))558 continue;559 if (!trkPnt.drawLine) {560 Point screen = mv.getPoint(trkPnt.eastNorth);561 g.drawRect(screen.x, screen.y, 0, 0);562 }563 } // end for trkpnt564 } // end for segment565 } // end for trk566 } // end if large567 568 /****************************************************************569 ********** STEP 3f - DRAW SMALL POINTS INSTEAD OF LINES ********570 ****************************************************************/571 if (!large && !lines){572 g.setColor(neutralColor);573 for (GpxTrack trk : data.tracks) {574 for (Collection<WayPoint> segment : trk.trackSegs) {575 for (WayPoint trkPnt : segment) {576 if (Double.isNaN(trkPnt.latlon.lat()) || Double.isNaN(trkPnt.latlon.lon()))577 continue;578 Point screen = mv.getPoint(trkPnt.eastNorth);579 g.drawRect(screen.x, screen.y, 0, 0);580 } // end for trkpnt581 } // end for segment582 } // end for trk583 } // end if large584 585 //Long duration = System.currentTimeMillis() - startTime;586 //System.out.println(duration);587 } // end paint588 589 @Override public void visitBoundingBox(BoundingXYVisitor v) {590 for (WayPoint p : data.waypoints)591 v.visit(p.eastNorth);592 593 for (GpxRoute rte : data.routes) {594 Collection<WayPoint> r = rte.routePoints;595 for (WayPoint p : r) {596 v.visit(p.eastNorth);597 }598 }599 600 for (GpxTrack trk : data.tracks) {601 for (Collection<WayPoint> seg : trk.trackSegs) {602 for (WayPoint p : seg) {603 v.visit(p.eastNorth);604 }605 }606 }607 }608 609 public class UploadTraceAction extends AbstractAction {610 public UploadTraceAction() {611 super(tr("Upload this trace..."), ImageProvider.get("uploadtrace"));612 }613 public void actionPerformed(ActionEvent e) {614 JPanel msg = new JPanel(new GridBagLayout());615 msg.add(new JLabel(tr("<html>This functionality has been added only recently. Please<br>"+616 "use with care and check if it works as expected.</html>")), GBC.eop());617 ButtonGroup bg = new ButtonGroup();618 JRadioButton c1 = null;619 JRadioButton c2 = null;620 621 //TODO622 //check whether data comes from server623 //check whether data changed sind last save/open624 625 c1 = new JRadioButton(tr("Upload track filtered by JOSM"), true);626 c2 = new JRadioButton(tr("Upload raw file: "), false);627 c2.setEnabled(false);628 c1.setEnabled(false);629 bg.add(c1);630 bg.add(c2);631 632 msg.add(c1, GBC.eol());633 msg.add(c2, GBC.eop());634 635 636 JLabel description = new JLabel((String) data.attr.get("desc"));637 JTextField tags = new JTextField();638 tags.setText((String) data.attr.get("keywords"));639 msg.add(new JLabel(tr("Description:")), GBC.std());640 msg.add(description, GBC.eol().fill(GBC.HORIZONTAL));641 msg.add(new JLabel(tr("Tags (keywords in GPX):")), GBC.std());642 msg.add(tags, GBC.eol().fill(GBC.HORIZONTAL));643 JCheckBox c3 = new JCheckBox("public");644 msg.add(c3, GBC.eop());645 msg.add(new JLabel("Please ensure that you don't upload your traces twice."), GBC.eop());646 647 int answer = JOptionPane.showConfirmDialog(Main.parent, msg, tr("GPX-Upload"), JOptionPane.OK_CANCEL_OPTION);648 if (answer == JOptionPane.OK_OPTION)649 {650 try {651 String version = Main.pref.get("osm-server.version", "0.5");652 URL url = new URL(Main.pref.get("osm-server.url") + "/" + version + "/gpx/create");653 654 // create a boundary string655 String boundary = MultiPartFormOutputStream.createBoundary();656 URLConnection urlConn = MultiPartFormOutputStream.createConnection(url);657 urlConn.setRequestProperty("Accept", "*/*");658 urlConn.setRequestProperty("Content-Type", MultiPartFormOutputStream.getContentType(boundary));659 // set some other request headers...660 urlConn.setRequestProperty("Connection", "Keep-Alive");661 urlConn.setRequestProperty("Cache-Control", "no-cache");662 // no need to connect cuz getOutputStream() does it663 MultiPartFormOutputStream out = new MultiPartFormOutputStream(urlConn.getOutputStream(), boundary);664 out.writeField("description", description.getText());665 out.writeField("tags", tags.getText());666 out.writeField("public", (c3.getSelectedObjects() != null) ? "1" : "0");667 // upload a file668 // out.writeFile("gpx_file", "text/xml", associatedFile);669 // can also write bytes directly670 // out.writeFile("myFile", "text/plain", "C:\\test.txt",671 // "This is some file text.".getBytes("ASCII"));672 File tmp = File.createTempFile("josm", "tmp.gpx");673 FileOutputStream outs = new FileOutputStream(tmp);674 new GpxWriter(outs).write(data);675 outs.close();676 FileInputStream ins = new FileInputStream(tmp);677 new GpxWriter(System.out).write(data);678 out.writeFile("gpx_file", "text/xml", data.storageFile.getName(), ins);679 out.close();680 tmp.delete();681 // read response from server682 BufferedReader in = new BufferedReader(new InputStreamReader(urlConn.getInputStream()));683 String line = "";684 while((line = in.readLine()) != null) {685 System.out.println(line);686 }687 in.close();688 689 //TODO check response690 /* int retCode = urlConn.getResponseCode();691 System.out.println("got return: " + retCode);692 String retMsg = urlConn.getResponseMessage();693 urlConn.disconnect();694 if (retCode != 200) {695 // Look for a detailed error message from the server696 if (urlConn.getHeaderField("Error") != null)697 retMsg += "\n" + urlConn.getHeaderField("Error");698 699 // Report our error700 ByteArrayOutputStream o = new ByteArrayOutputStream();701 System.out.println(new String(o.toByteArray(), "UTF-8").toString());702 throw new RuntimeException(retCode+" "+retMsg);703 }704 */705 } catch (UnknownHostException ex) {706 throw new RuntimeException(tr("Unknown host")+": "+ex.getMessage(), ex);707 } catch (Exception ex) {708 //if (cancel)709 // return; // assume cancel710 if (ex instanceof RuntimeException)711 throw (RuntimeException)ex;712 throw new RuntimeException(ex.getMessage(), ex);713 }714 }715 }716 }717 718 public class ConvertToDataLayerAction extends AbstractAction {719 public ConvertToDataLayerAction() {720 super(tr("Convert to data layer"), ImageProvider.get("converttoosm"));721 }722 public void actionPerformed(ActionEvent e) {723 JPanel msg = new JPanel(new GridBagLayout());724 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());725 msg.add(new UrlLabel(tr("http://www.openstreetmap.org/traces")), GBC.eop());726 if (!DontShowAgainInfo.show("convert_to_data", msg))727 return;728 DataSet ds = new DataSet();729 for (GpxTrack trk : data.tracks) {730 for (Collection<WayPoint> segment : trk.trackSegs) {731 Way w = new Way();732 for (WayPoint p : segment) {733 Node n = new Node(p.latlon);734 String timestr = p.getString("time");735 if(timestr != null)736 {737 timestr = timestr.replace("Z","+00:00");738 n.timestamp = timestr;739 }740 ds.nodes.add(n);741 w.nodes.add(n);742 }743 ds.ways.add(w);744 }745 }746 Main.main.addLayer(new OsmDataLayer(ds, tr("Converted from: {0}", GpxLayer.this.name), null));747 Main.main.removeLayer(GpxLayer.this);748 }749 }402 computeCacheMaxLineLengthUsed = maxLineLength; 403 computeCacheInSync = false; 404 computeCacheColorUsed = neutralColor; 405 computeCacheColored = colored; 406 } 407 408 /**************************************************************** 409 ********** STEP 2b - RE-COMPUTE CACHE DATA ********************* 410 ****************************************************************/ 411 if (!computeCacheInSync) { // don't compute if the cache is good 412 WayPoint oldWp = null; 413 for (GpxTrack trk : data.tracks) { 414 if (!forceLines) { // don't draw lines between segments, unless forced to 415 oldWp = null; 416 } 417 for (Collection<WayPoint> segment : trk.trackSegs) { 418 for (WayPoint trkPnt : segment) { 419 if (Double.isNaN(trkPnt.latlon.lat()) || Double.isNaN(trkPnt.latlon.lon())) { 420 continue; 421 } 422 if (oldWp != null) { 423 double dist = trkPnt.latlon.greatCircleDistance(oldWp.latlon); 424 double dtime = trkPnt.time - oldWp.time; 425 double vel = dist/dtime; 426 427 if (!colored) { 428 trkPnt.speedLineColor = neutralColor; 429 } else if (dtime <= 0 || vel < 0 || vel > 36) { // attn: bad case first 430 trkPnt.speedLineColor = colors[255]; 431 } else { 432 trkPnt.speedLineColor = colors[(int) (7*vel)]; 433 } 434 if (maxLineLength == -1 || dist <= maxLineLength) { 435 trkPnt.drawLine = true; 436 trkPnt.dir = (int)(Math.atan2(-trkPnt.eastNorth.north()+oldWp.eastNorth.north(), trkPnt.eastNorth.east()-oldWp.eastNorth.east()) / Math.PI * 4 + 3.5); // crude but works 437 } else { 438 trkPnt.drawLine = false; 439 } 440 } else { // make sure we reset outdated data 441 trkPnt.speedLineColor = colors[255]; 442 trkPnt.drawLine = false; 443 } 444 oldWp = trkPnt; 445 } 446 } 447 } 448 computeCacheInSync = true; 449 } 450 451 /**************************************************************** 452 ********** STEP 3a - DRAW LINES ******************************** 453 ****************************************************************/ 454 if (lines) { 455 Point old = null; 456 for (GpxTrack trk : data.tracks) { 457 for (Collection<WayPoint> segment : trk.trackSegs) { 458 for (WayPoint trkPnt : segment) { 459 if (Double.isNaN(trkPnt.latlon.lat()) || Double.isNaN(trkPnt.latlon.lon())) 460 continue; 461 Point screen = mv.getPoint(trkPnt.eastNorth); 462 if (trkPnt.drawLine) { 463 // skip points that are on the same screenposition 464 if (old != null && ((old.x != screen.x) || (old.y != screen.y))) { 465 g.setColor(trkPnt.speedLineColor); 466 g.drawLine(old.x, old.y, screen.x, screen.y); 467 } 468 } 469 old = screen; 470 } // end for trkpnt 471 } // end for segment 472 } // end for trk 473 } // end if lines 474 475 /**************************************************************** 476 ********** STEP 3b - DRAW NICE ARROWS ************************** 477 ****************************************************************/ 478 if (lines && direction && !alternatedirection) { 479 Point old = null; 480 Point oldA = null; // last arrow painted 481 for (GpxTrack trk : data.tracks) { 482 for (Collection<WayPoint> segment : trk.trackSegs) { 483 for (WayPoint trkPnt : segment) { 484 if (Double.isNaN(trkPnt.latlon.lat()) || Double.isNaN(trkPnt.latlon.lon())) 485 continue; 486 if (trkPnt.drawLine) { 487 Point screen = mv.getPoint(trkPnt.eastNorth); 488 // skip points that are on the same screenposition 489 if (old != null && (oldA == null || screen.x < oldA.x-delta || screen.x > oldA.x+delta || screen.y < oldA.y-delta || screen.y > oldA.y+delta)) { 490 g.setColor(trkPnt.speedLineColor); 491 double t = Math.atan2(screen.y-old.y, screen.x-old.x) + Math.PI; 492 g.drawLine(screen.x,screen.y, (int)(screen.x + 10*Math.cos(t-PHI)), (int)(screen.y 493 + 10*Math.sin(t-PHI))); 494 g.drawLine(screen.x,screen.y, (int)(screen.x + 10*Math.cos(t+PHI)), (int)(screen.y 495 + 10*Math.sin(t+PHI))); 496 oldA = screen; 497 } 498 old = screen; 499 } 500 } // end for trkpnt 501 } // end for segment 502 } // end for trk 503 } // end if lines 504 505 /**************************************************************** 506 ********** STEP 3c - DRAW FAST ARROWS ************************** 507 ****************************************************************/ 508 if (lines && direction && alternatedirection) { 509 Point old = null; 510 Point oldA = null; // last arrow painted 511 for (GpxTrack trk : data.tracks) { 512 for (Collection<WayPoint> segment : trk.trackSegs) { 513 for (WayPoint trkPnt : segment) { 514 if (Double.isNaN(trkPnt.latlon.lat()) || Double.isNaN(trkPnt.latlon.lon())) 515 continue; 516 if (trkPnt.drawLine) { 517 Point screen = mv.getPoint(trkPnt.eastNorth); 518 // skip points that are on the same screenposition 519 if (old != null && (oldA == null || screen.x < oldA.x-delta || screen.x > oldA.x+delta || screen.y < oldA.y-delta || screen.y > oldA.y+delta)) { 520 g.setColor(trkPnt.speedLineColor); 521 g.drawLine(screen.x, screen.y, screen.x + dir[trkPnt.dir][0], screen.y + dir[trkPnt.dir][1]); 522 g.drawLine(screen.x, screen.y, screen.x + dir[trkPnt.dir][2], screen.y + dir[trkPnt.dir][3]); 523 oldA = screen; 524 } 525 old = screen; 526 } 527 } // end for trkpnt 528 } // end for segment 529 } // end for trk 530 } // end if lines 531 532 /**************************************************************** 533 ********** STEP 3d - DRAW LARGE POINTS ************************* 534 ****************************************************************/ 535 if (large) { 536 g.setColor(neutralColor); 537 for (GpxTrack trk : data.tracks) { 538 for (Collection<WayPoint> segment : trk.trackSegs) { 539 for (WayPoint trkPnt : segment) { 540 if (Double.isNaN(trkPnt.latlon.lat()) || Double.isNaN(trkPnt.latlon.lon())) 541 continue; 542 Point screen = mv.getPoint(trkPnt.eastNorth); 543 g.fillRect(screen.x-1, screen.y-1, 3, 3); 544 } // end for trkpnt 545 } // end for segment 546 } // end for trk 547 } // end if large 548 549 /**************************************************************** 550 ********** STEP 3e - DRAW SMALL POINTS FOR LINES *************** 551 ****************************************************************/ 552 if (!large && lines){ 553 g.setColor(neutralColor); 554 for (GpxTrack trk : data.tracks) { 555 for (Collection<WayPoint> segment : trk.trackSegs) { 556 for (WayPoint trkPnt : segment) { 557 if (Double.isNaN(trkPnt.latlon.lat()) || Double.isNaN(trkPnt.latlon.lon())) 558 continue; 559 if (!trkPnt.drawLine) { 560 Point screen = mv.getPoint(trkPnt.eastNorth); 561 g.drawRect(screen.x, screen.y, 0, 0); 562 } 563 } // end for trkpnt 564 } // end for segment 565 } // end for trk 566 } // end if large 567 568 /**************************************************************** 569 ********** STEP 3f - DRAW SMALL POINTS INSTEAD OF LINES ******** 570 ****************************************************************/ 571 if (!large && !lines){ 572 g.setColor(neutralColor); 573 for (GpxTrack trk : data.tracks) { 574 for (Collection<WayPoint> segment : trk.trackSegs) { 575 for (WayPoint trkPnt : segment) { 576 if (Double.isNaN(trkPnt.latlon.lat()) || Double.isNaN(trkPnt.latlon.lon())) 577 continue; 578 Point screen = mv.getPoint(trkPnt.eastNorth); 579 g.drawRect(screen.x, screen.y, 0, 0); 580 } // end for trkpnt 581 } // end for segment 582 } // end for trk 583 } // end if large 584 585 //Long duration = System.currentTimeMillis() - startTime; 586 //System.out.println(duration); 587 } // end paint 588 589 @Override public void visitBoundingBox(BoundingXYVisitor v) { 590 for (WayPoint p : data.waypoints) 591 v.visit(p.eastNorth); 592 593 for (GpxRoute rte : data.routes) { 594 Collection<WayPoint> r = rte.routePoints; 595 for (WayPoint p : r) { 596 v.visit(p.eastNorth); 597 } 598 } 599 600 for (GpxTrack trk : data.tracks) { 601 for (Collection<WayPoint> seg : trk.trackSegs) { 602 for (WayPoint p : seg) { 603 v.visit(p.eastNorth); 604 } 605 } 606 } 607 } 608 609 public class UploadTraceAction extends AbstractAction { 610 public UploadTraceAction() { 611 super(tr("Upload this trace..."), ImageProvider.get("uploadtrace")); 612 } 613 public void actionPerformed(ActionEvent e) { 614 JPanel msg = new JPanel(new GridBagLayout()); 615 msg.add(new JLabel(tr("<html>This functionality has been added only recently. Please<br>"+ 616 "use with care and check if it works as expected.</html>")), GBC.eop()); 617 ButtonGroup bg = new ButtonGroup(); 618 JRadioButton c1 = null; 619 JRadioButton c2 = null; 620 621 //TODO 622 //check whether data comes from server 623 //check whether data changed sind last save/open 624 625 c1 = new JRadioButton(tr("Upload track filtered by JOSM"), true); 626 c2 = new JRadioButton(tr("Upload raw file: "), false); 627 c2.setEnabled(false); 628 c1.setEnabled(false); 629 bg.add(c1); 630 bg.add(c2); 631 632 msg.add(c1, GBC.eol()); 633 msg.add(c2, GBC.eop()); 634 635 636 JLabel description = new JLabel((String) data.attr.get("desc")); 637 JTextField tags = new JTextField(); 638 tags.setText((String) data.attr.get("keywords")); 639 msg.add(new JLabel(tr("Description:")), GBC.std()); 640 msg.add(description, GBC.eol().fill(GBC.HORIZONTAL)); 641 msg.add(new JLabel(tr("Tags (keywords in GPX):")), GBC.std()); 642 msg.add(tags, GBC.eol().fill(GBC.HORIZONTAL)); 643 JCheckBox c3 = new JCheckBox("public"); 644 msg.add(c3, GBC.eop()); 645 msg.add(new JLabel("Please ensure that you don't upload your traces twice."), GBC.eop()); 646 647 int answer = JOptionPane.showConfirmDialog(Main.parent, msg, tr("GPX-Upload"), JOptionPane.OK_CANCEL_OPTION); 648 if (answer == JOptionPane.OK_OPTION) 649 { 650 try { 651 String version = Main.pref.get("osm-server.version", "0.5"); 652 URL url = new URL(Main.pref.get("osm-server.url") + "/" + version + "/gpx/create"); 653 654 // create a boundary string 655 String boundary = MultiPartFormOutputStream.createBoundary(); 656 URLConnection urlConn = MultiPartFormOutputStream.createConnection(url); 657 urlConn.setRequestProperty("Accept", "*/*"); 658 urlConn.setRequestProperty("Content-Type", MultiPartFormOutputStream.getContentType(boundary)); 659 // set some other request headers... 660 urlConn.setRequestProperty("Connection", "Keep-Alive"); 661 urlConn.setRequestProperty("Cache-Control", "no-cache"); 662 // no need to connect cuz getOutputStream() does it 663 MultiPartFormOutputStream out = new MultiPartFormOutputStream(urlConn.getOutputStream(), boundary); 664 out.writeField("description", description.getText()); 665 out.writeField("tags", tags.getText()); 666 out.writeField("public", (c3.getSelectedObjects() != null) ? "1" : "0"); 667 // upload a file 668 // out.writeFile("gpx_file", "text/xml", associatedFile); 669 // can also write bytes directly 670 // out.writeFile("myFile", "text/plain", "C:\\test.txt", 671 // "This is some file text.".getBytes("ASCII")); 672 File tmp = File.createTempFile("josm", "tmp.gpx"); 673 FileOutputStream outs = new FileOutputStream(tmp); 674 new GpxWriter(outs).write(data); 675 outs.close(); 676 FileInputStream ins = new FileInputStream(tmp); 677 new GpxWriter(System.out).write(data); 678 out.writeFile("gpx_file", "text/xml", data.storageFile.getName(), ins); 679 out.close(); 680 tmp.delete(); 681 // read response from server 682 BufferedReader in = new BufferedReader(new InputStreamReader(urlConn.getInputStream())); 683 String line = ""; 684 while((line = in.readLine()) != null) { 685 System.out.println(line); 686 } 687 in.close(); 688 689 //TODO check response 690 /* int retCode = urlConn.getResponseCode(); 691 System.out.println("got return: " + retCode); 692 String retMsg = urlConn.getResponseMessage(); 693 urlConn.disconnect(); 694 if (retCode != 200) { 695 // Look for a detailed error message from the server 696 if (urlConn.getHeaderField("Error") != null) 697 retMsg += "\n" + urlConn.getHeaderField("Error"); 698 699 // Report our error 700 ByteArrayOutputStream o = new ByteArrayOutputStream(); 701 System.out.println(new String(o.toByteArray(), "UTF-8").toString()); 702 throw new RuntimeException(retCode+" "+retMsg); 703 } 704 */ 705 } catch (UnknownHostException ex) { 706 throw new RuntimeException(tr("Unknown host")+": "+ex.getMessage(), ex); 707 } catch (Exception ex) { 708 //if (cancel) 709 // return; // assume cancel 710 if (ex instanceof RuntimeException) 711 throw (RuntimeException)ex; 712 throw new RuntimeException(ex.getMessage(), ex); 713 } 714 } 715 } 716 } 717 718 public class ConvertToDataLayerAction extends AbstractAction { 719 public ConvertToDataLayerAction() { 720 super(tr("Convert to data layer"), ImageProvider.get("converttoosm")); 721 } 722 public void actionPerformed(ActionEvent e) { 723 JPanel msg = new JPanel(new GridBagLayout()); 724 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()); 725 msg.add(new UrlLabel(tr("http://www.openstreetmap.org/traces")), GBC.eop()); 726 if (!DontShowAgainInfo.show("convert_to_data", msg)) 727 return; 728 DataSet ds = new DataSet(); 729 for (GpxTrack trk : data.tracks) { 730 for (Collection<WayPoint> segment : trk.trackSegs) { 731 Way w = new Way(); 732 for (WayPoint p : segment) { 733 Node n = new Node(p.latlon); 734 String timestr = p.getString("time"); 735 if(timestr != null) 736 { 737 timestr = timestr.replace("Z","+00:00"); 738 n.timestamp = timestr; 739 } 740 ds.nodes.add(n); 741 w.nodes.add(n); 742 } 743 ds.ways.add(w); 744 } 745 } 746 Main.main.addLayer(new OsmDataLayer(ds, tr("Converted from: {0}", GpxLayer.this.name), null)); 747 Main.main.removeLayer(GpxLayer.this); 748 } 749 } 750 750 751 751 /** 752 752 * Action that issues a series of download requests to the API, following the GPX track. 753 * 753 * 754 754 * @author fred 755 755 */ … … 762 762 JList buffer = new JList(new String[] { "50 metres", "500 metres", "5000 metres" }); 763 763 JList maxRect = new JList(new String[] { "1 sq km", "5 sq km", "10 sq km", "20 sq km" }); 764 764 765 765 msg.add(new JLabel(tr("Download everything within:")), GBC.eol()); 766 766 msg.add(buffer, GBC.eol()); 767 767 msg.add(new JLabel(tr("Maximum area per request:")), GBC.eol()); 768 768 msg.add(maxRect, GBC.eol()); 769 770 if (JOptionPane.showConfirmDialog(Main.parent, msg, 771 tr("Download from OSM along this track"), 769 770 if (JOptionPane.showConfirmDialog(Main.parent, msg, 771 tr("Download from OSM along this track"), 772 772 JOptionPane.OK_CANCEL_OPTION) == JOptionPane.CANCEL_OPTION) { 773 773 return; 774 774 775 775 } 776 776 … … 781 781 double latsum = 0; 782 782 int latcnt = 0; 783 783 784 784 for (GpxTrack trk : data.tracks) { 785 785 for (Collection<WayPoint> segment : trk.trackSegs) { … … 790 790 } 791 791 } 792 792 793 793 double avglat = latsum / latcnt; 794 794 double scale = Math.cos(Math.toRadians(avglat)); … … 797 797 * Compute buffer zone extents and maximum bounding box size. Note how the 798 798 * maximum we ever offer is a bbox area of 0.002, while the API theoretically 799 * supports 0.25, but as soon as you touch any built-up area, that kind of 800 * bounding box will download forever and then stop because it has more than 799 * supports 0.25, but as soon as you touch any built-up area, that kind of 800 * bounding box will download forever and then stop because it has more than 801 801 * 50k nodes. 802 802 */ … … 809 809 } 810 810 buffer_x = buffer_y / scale; 811 811 812 812 double max_area; 813 813 switch(maxRect.getSelectedIndex()) { … … 820 820 Area a = new Area(); 821 821 Rectangle2D r = new Rectangle2D.Double(); 822 822 823 823 /* 824 824 * Collect the combined area of all gpx points plus buffer zones around them. … … 836 836 } 837 837 } 838 838 839 839 /* 840 840 * Area "a" now contains the hull that we would like to download data for. … … 858 858 * actually has something in it. 859 859 */ 860 860 861 861 List<Rectangle2D> toDownload = new ArrayList<Rectangle2D>(); 862 862 863 863 addToDownload(a, a.getBounds(), toDownload, max_area); 864 864 865 865 msg = new JPanel(new GridBagLayout()); 866 866 867 867 msg.add(new JLabel(tr("<html>This action will require {0} individual<br>download requests. Do you wish<br>to continue?</html>", 868 868 toDownload.size())), GBC.eol()); 869 870 if (JOptionPane.showConfirmDialog(Main.parent, msg, 871 tr("Download from OSM along this track"), 869 870 if (JOptionPane.showConfirmDialog(Main.parent, msg, 871 tr("Download from OSM along this track"), 872 872 JOptionPane.OK_CANCEL_OPTION) == JOptionPane.CANCEL_OPTION) { 873 873 return; 874 874 } 875 875 876 876 // FIXME: DownloadTask's "please wait" dialog should display the number of 877 877 // downloads left, and "cancel" needs to be honoured. An error along the way … … 883 883 } 884 884 } 885 885 886 886 private static void addToDownload(Area a, Rectangle2D r, Collection<Rectangle2D> results, double max_area) { 887 887 Area tmp = new Area(r); … … 909 909 } 910 910 } 911 912 /**913 * Makes a new marker layer derived from this GpxLayer containing at least one914 * audio marker which the given audio file is associated with.915 * Markers are derived from the following916 * (a) explict waypoints in the GPX layer, or917 * (b) named trackpoints in the GPX layer, or918 * (c) (in future) voice recognised markers in the sound recording919 * (d) a single marker at the beginning of the track920 * @param wavFile : the file to be associated with the markers in the new marker layer921 */922 private void importAudio(File wavFile) {923 String uri = "file:".concat(wavFile.getAbsolutePath());924 MarkerLayer ml = new MarkerLayer(new GpxData(), tr("Audio markers from {0}", name), associatedFile, me);925 926 Collection<WayPoint> waypoints = new ArrayList<WayPoint>();927 boolean timedMarkersOmitted = false;928 boolean untimedMarkersOmitted = false;929 double snapDistance = Main.pref.getDouble("marker.audiofromuntimedwaypoints.distance", 1.0e-3); /* about 25m */930 931 // determine time of first point in track932 double firstTime = -1.0;933 if (data.tracks != null && ! data.tracks.isEmpty()) {934 for (GpxTrack track : data.tracks) {935 if (track.trackSegs == null) continue;936 for (Collection<WayPoint> seg : track.trackSegs) {937 for (WayPoint w : seg) {938 firstTime = w.time;939 break;940 }941 if (firstTime >= 0.0) break;942 }943 if (firstTime >= 0.0) break;944 }945 }946 if (firstTime < 0.0) {947 JOptionPane.showMessageDialog(Main.parent, tr("No GPX track available in layer to associate audio with."));948 return;949 }950 951 // (a) try explicit timestamped waypoints - unless suppressed952 if (Main.pref.getBoolean("marker.audiofromexplicitwaypoints", true) &&953 data.waypoints != null && ! data.waypoints.isEmpty())954 {955 for (WayPoint w : data.waypoints) {956 if (w.time > firstTime) {957 waypoints.add(w);958 } else if (w.time > 0.0) {959 timedMarkersOmitted = true;960 }961 }962 }963 964 // (b) try explicit waypoints without timestamps - unless suppressed965 if (Main.pref.getBoolean("marker.audiofromuntimedwaypoints", true) &&966 data.waypoints != null && ! data.waypoints.isEmpty())967 {968 for (WayPoint w : data.waypoints) {969 if (waypoints.contains(w)) { continue; }970 WayPoint wNear = nearestPointOnTrack(w.eastNorth, snapDistance);971 if (wNear != null) {972 WayPoint wc = new WayPoint(w.latlon);973 wc.time = wNear.time;974 if (w.attr.containsKey("name")) wc.attr.put("name", w.getString("name"));975 waypoints.add(wc);976 } else {977 untimedMarkersOmitted = true;978 }979 }980 }981 982 // (c) use explicitly named track points, again unless suppressed983 if ((Main.pref.getBoolean("marker.audiofromnamedtrackpoints", false)) &&984 data.tracks != null && ! data.tracks.isEmpty())985 {986 for (GpxTrack track : data.tracks) {987 if (track.trackSegs == null) continue;988 for (Collection<WayPoint> seg : track.trackSegs) {989 for (WayPoint w : seg) {990 if (w.attr.containsKey("name") || w.attr.containsKey("desc")) {991 waypoints.add(w);992 }993 }994 }995 }996 }997 998 // (d) analyse audio for spoken markers here, in due course999 1000 // (e) simply add a single marker at the start of the track1001 if ((Main.pref.getBoolean("marker.audiofromstart") || waypoints.isEmpty()) &&1002 data.tracks != null && ! data.tracks.isEmpty())1003 {1004 boolean gotOne = false;1005 for (GpxTrack track : data.tracks) {1006 if (track.trackSegs == null) continue;1007 for (Collection<WayPoint> seg : track.trackSegs) {1008 for (WayPoint w : seg) {1009 WayPoint wStart = new WayPoint(w.latlon);1010 wStart.attr.put("name", "start");1011 wStart.time = w.time;1012 waypoints.add(wStart);1013 gotOne = true;1014 break;1015 }1016 if (gotOne) break;1017 }1018 if (gotOne) break;1019 }1020 }1021 1022 /* we must have got at least one waypoint now */1023 1024 Collections.sort((ArrayList<WayPoint>) waypoints, new Comparator<WayPoint>() {1025 public int compare(WayPoint a, WayPoint b) {1026 return a.time <= b.time ? -1 : 1;1027 }1028 });1029 1030 firstTime = -1.0; /* this time of the first waypoint, not first trackpoint */1031 for (WayPoint w : waypoints) {1032 if (firstTime < 0.0) firstTime = w.time;1033 double offset = w.time - firstTime;1034 String name;1035 if (w.attr.containsKey("name"))1036 name = w.getString("name");1037 else if (w.attr.containsKey("desc"))1038 name = w.getString("desc");1039 else1040 name = AudioMarker.inventName(offset);1041 AudioMarker am = AudioMarker.create(w.latlon,1042 name, uri, ml, w.time, offset);1043 ml.data.add(am);1044 }1045 Main.main.addLayer(ml);1046 1047 if (timedMarkersOmitted) {1048 JOptionPane.showMessageDialog(Main.parent,1049 tr("Some waypoints with timestamps from before the start of the track were omitted."));1050 }1051 if (untimedMarkersOmitted) {1052 JOptionPane.showMessageDialog(Main.parent,1053 tr("Some waypoints which were too far from the track to sensibly estimate their time were omitted."));1054 }1055 }1056 1057 /**1058 * Makes a WayPoint at the projection of point P onto the track providing P is1059 * less than tolerance away from the track1060 1061 * @param P : the point to determine the projection for1062 * @param tolerance : must be no further than this from the track1063 * @return the closest point on the track to P, which may be the1064 * first or last point if off the end of a segment, or may be null if1065 * nothing close enough1066 */1067 public WayPoint nearestPointOnTrack(EastNorth P, double tolerance) {1068 /*1069 * assume the coordinates of P are xp,yp, and those of a section of track1070 * between two trackpoints are R=xr,yr and S=xs,ys. Let N be the projected point.1071 *1072 * The equation of RS is Ax + By + C = 0 where1073 * A = ys - yr1074 * B = xr - xs1075 * C = - Axr - Byr1076 *1077 * Also, note that the distance RS^2 is A^2 + B^21078 *1079 * If RS^2 == 0.0 ignore the degenerate section of track1080 *1081 * PN^2 = (Axp + Byp + C)^2 / RS^21082 * that is the distance from P to the line1083 *1084 * so if PN^2 is less than PNmin^2 (initialized to tolerance) we can reject1085 * the line; otherwise...1086 * determine if the projected poijnt lies within the bounds of the line:1087 * PR^2 - PN^2 <= RS^2 and PS^2 - PN^2 <= RS^21088 *1089 * where PR^2 = (xp - xr)^2 + (yp-yr)^21090 * and PS^2 = (xp - xs)^2 + (yp-ys)^21091 *1092 * If so, calculate N as1093 * xn = xr + (RN/RS) B1094 * yn = y1 + (RN/RS) A1095 *1096 * where RN = sqrt(PR^2 - PN^2)1097 */1098 1099 double PNminsq = tolerance * tolerance;1100 EastNorth bestEN = null;1101 double bestTime = 0.0;1102 double px = P.east();1103 double py = P.north();1104 double rx = 0.0, ry = 0.0, sx, sy, x, y;1105 if (data.tracks == null) return null;1106 for (GpxTrack track : data.tracks) {1107 if (track.trackSegs == null) continue;1108 for (Collection<WayPoint> seg : track.trackSegs) {1109 WayPoint R = null;1110 for (WayPoint S : seg) {1111 if (R == null) {1112 R = S;1113 rx = R.eastNorth.east();1114 ry = R.eastNorth.north();1115 x = px - rx;1116 y = py - ry;1117 double PRsq = x * x + y * y;1118 if (PRsq < PNminsq) {1119 PNminsq = PRsq;1120 bestEN = R.eastNorth;1121 bestTime = R.time;1122 }1123 } else {1124 sx = S.eastNorth.east();1125 sy = S.eastNorth.north();1126 double A = sy - ry;1127 double B = rx - sx;1128 double C = - A * rx - B * ry;1129 double RSsq = A * A + B * B;1130 if (RSsq == 0.0) continue;1131 double PNsq = A * px + B * py + C;1132 PNsq = PNsq * PNsq / RSsq;1133 if (PNsq < PNminsq) {1134 x = px - rx;1135 y = py - ry;1136 double PRsq = x * x + y * y;1137 x = px - sx;1138 y = py - sy;1139 double PSsq = x * x + y * y;1140 if (PRsq - PNsq <= RSsq && PSsq - PNsq <= RSsq) {1141 double RNoverRS = Math.sqrt((PRsq - PNsq)/RSsq);1142 double nx = rx - RNoverRS * B;1143 double ny = ry + RNoverRS * A;1144 bestEN = new EastNorth(nx, ny);1145 bestTime = R.time + RNoverRS * (S.time - R.time);1146 PNminsq = PNsq;1147 }1148 }1149 R = S;1150 rx = sx;1151 ry = sy;1152 }1153 }1154 if (R != null) {1155 /* if there is only one point in the seg, it will do this twice, but no matter */1156 rx = R.eastNorth.east();1157 ry = R.eastNorth.north();1158 x = px - rx;1159 y = py - ry;1160 double PRsq = x * x + y * y;1161 if (PRsq < PNminsq) {1162 PNminsq = PRsq;1163 bestEN = R.eastNorth;1164 bestTime = R.time;1165 }1166 }1167 }1168 }1169 if (bestEN == null) return null;1170 WayPoint best = new WayPoint(Main.proj.eastNorth2latlon(bestEN));1171 best.time = bestTime;1172 return best;1173 }911 912 /** 913 * Makes a new marker layer derived from this GpxLayer containing at least one 914 * audio marker which the given audio file is associated with. 915 * Markers are derived from the following 916 * (a) explict waypoints in the GPX layer, or 917 * (b) named trackpoints in the GPX layer, or 918 * (c) (in future) voice recognised markers in the sound recording 919 * (d) a single marker at the beginning of the track 920 * @param wavFile : the file to be associated with the markers in the new marker layer 921 */ 922 private void importAudio(File wavFile) { 923 String uri = "file:".concat(wavFile.getAbsolutePath()); 924 MarkerLayer ml = new MarkerLayer(new GpxData(), tr("Audio markers from {0}", name), associatedFile, me); 925 926 Collection<WayPoint> waypoints = new ArrayList<WayPoint>(); 927 boolean timedMarkersOmitted = false; 928 boolean untimedMarkersOmitted = false; 929 double snapDistance = Main.pref.getDouble("marker.audiofromuntimedwaypoints.distance", 1.0e-3); /* about 25m */ 930 931 // determine time of first point in track 932 double firstTime = -1.0; 933 if (data.tracks != null && ! data.tracks.isEmpty()) { 934 for (GpxTrack track : data.tracks) { 935 if (track.trackSegs == null) continue; 936 for (Collection<WayPoint> seg : track.trackSegs) { 937 for (WayPoint w : seg) { 938 firstTime = w.time; 939 break; 940 } 941 if (firstTime >= 0.0) break; 942 } 943 if (firstTime >= 0.0) break; 944 } 945 } 946 if (firstTime < 0.0) { 947 JOptionPane.showMessageDialog(Main.parent, tr("No GPX track available in layer to associate audio with.")); 948 return; 949 } 950 951 // (a) try explicit timestamped waypoints - unless suppressed 952 if (Main.pref.getBoolean("marker.audiofromexplicitwaypoints", true) && 953 data.waypoints != null && ! data.waypoints.isEmpty()) 954 { 955 for (WayPoint w : data.waypoints) { 956 if (w.time > firstTime) { 957 waypoints.add(w); 958 } else if (w.time > 0.0) { 959 timedMarkersOmitted = true; 960 } 961 } 962 } 963 964 // (b) try explicit waypoints without timestamps - unless suppressed 965 if (Main.pref.getBoolean("marker.audiofromuntimedwaypoints", true) && 966 data.waypoints != null && ! data.waypoints.isEmpty()) 967 { 968 for (WayPoint w : data.waypoints) { 969 if (waypoints.contains(w)) { continue; } 970 WayPoint wNear = nearestPointOnTrack(w.eastNorth, snapDistance); 971 if (wNear != null) { 972 WayPoint wc = new WayPoint(w.latlon); 973 wc.time = wNear.time; 974 if (w.attr.containsKey("name")) wc.attr.put("name", w.getString("name")); 975 waypoints.add(wc); 976 } else { 977 untimedMarkersOmitted = true; 978 } 979 } 980 } 981 982 // (c) use explicitly named track points, again unless suppressed 983 if ((Main.pref.getBoolean("marker.audiofromnamedtrackpoints", false)) && 984 data.tracks != null && ! data.tracks.isEmpty()) 985 { 986 for (GpxTrack track : data.tracks) { 987 if (track.trackSegs == null) continue; 988 for (Collection<WayPoint> seg : track.trackSegs) { 989 for (WayPoint w : seg) { 990 if (w.attr.containsKey("name") || w.attr.containsKey("desc")) { 991 waypoints.add(w); 992 } 993 } 994 } 995 } 996 } 997 998 // (d) analyse audio for spoken markers here, in due course 999 1000 // (e) simply add a single marker at the start of the track 1001 if ((Main.pref.getBoolean("marker.audiofromstart") || waypoints.isEmpty()) && 1002 data.tracks != null && ! data.tracks.isEmpty()) 1003 { 1004 boolean gotOne = false; 1005 for (GpxTrack track : data.tracks) { 1006 if (track.trackSegs == null) continue; 1007 for (Collection<WayPoint> seg : track.trackSegs) { 1008 for (WayPoint w : seg) { 1009 WayPoint wStart = new WayPoint(w.latlon); 1010 wStart.attr.put("name", "start"); 1011 wStart.time = w.time; 1012 waypoints.add(wStart); 1013 gotOne = true; 1014 break; 1015 } 1016 if (gotOne) break; 1017 } 1018 if (gotOne) break; 1019 } 1020 } 1021 1022 /* we must have got at least one waypoint now */ 1023 1024 Collections.sort((ArrayList<WayPoint>) waypoints, new Comparator<WayPoint>() { 1025 public int compare(WayPoint a, WayPoint b) { 1026 return a.time <= b.time ? -1 : 1; 1027 } 1028 }); 1029 1030 firstTime = -1.0; /* this time of the first waypoint, not first trackpoint */ 1031 for (WayPoint w : waypoints) { 1032 if (firstTime < 0.0) firstTime = w.time; 1033 double offset = w.time - firstTime; 1034 String name; 1035 if (w.attr.containsKey("name")) 1036 name = w.getString("name"); 1037 else if (w.attr.containsKey("desc")) 1038 name = w.getString("desc"); 1039 else 1040 name = AudioMarker.inventName(offset); 1041 AudioMarker am = AudioMarker.create(w.latlon, 1042 name, uri, ml, w.time, offset); 1043 ml.data.add(am); 1044 } 1045 Main.main.addLayer(ml); 1046 1047 if (timedMarkersOmitted) { 1048 JOptionPane.showMessageDialog(Main.parent, 1049 tr("Some waypoints with timestamps from before the start of the track were omitted.")); 1050 } 1051 if (untimedMarkersOmitted) { 1052 JOptionPane.showMessageDialog(Main.parent, 1053 tr("Some waypoints which were too far from the track to sensibly estimate their time were omitted.")); 1054 } 1055 } 1056 1057 /** 1058 * Makes a WayPoint at the projection of point P onto the track providing P is 1059 * less than tolerance away from the track 1060 1061 * @param P : the point to determine the projection for 1062 * @param tolerance : must be no further than this from the track 1063 * @return the closest point on the track to P, which may be the 1064 * first or last point if off the end of a segment, or may be null if 1065 * nothing close enough 1066 */ 1067 public WayPoint nearestPointOnTrack(EastNorth P, double tolerance) { 1068 /* 1069 * assume the coordinates of P are xp,yp, and those of a section of track 1070 * between two trackpoints are R=xr,yr and S=xs,ys. Let N be the projected point. 1071 * 1072 * The equation of RS is Ax + By + C = 0 where 1073 * A = ys - yr 1074 * B = xr - xs 1075 * C = - Axr - Byr 1076 * 1077 * Also, note that the distance RS^2 is A^2 + B^2 1078 * 1079 * If RS^2 == 0.0 ignore the degenerate section of track 1080 * 1081 * PN^2 = (Axp + Byp + C)^2 / RS^2 1082 * that is the distance from P to the line 1083 * 1084 * so if PN^2 is less than PNmin^2 (initialized to tolerance) we can reject 1085 * the line; otherwise... 1086 * determine if the projected poijnt lies within the bounds of the line: 1087 * PR^2 - PN^2 <= RS^2 and PS^2 - PN^2 <= RS^2 1088 * 1089 * where PR^2 = (xp - xr)^2 + (yp-yr)^2 1090 * and PS^2 = (xp - xs)^2 + (yp-ys)^2 1091 * 1092 * If so, calculate N as 1093 * xn = xr + (RN/RS) B 1094 * yn = y1 + (RN/RS) A 1095 * 1096 * where RN = sqrt(PR^2 - PN^2) 1097 */ 1098 1099 double PNminsq = tolerance * tolerance; 1100 EastNorth bestEN = null; 1101 double bestTime = 0.0; 1102 double px = P.east(); 1103 double py = P.north(); 1104 double rx = 0.0, ry = 0.0, sx, sy, x, y; 1105 if (data.tracks == null) return null; 1106 for (GpxTrack track : data.tracks) { 1107 if (track.trackSegs == null) continue; 1108 for (Collection<WayPoint> seg : track.trackSegs) { 1109 WayPoint R = null; 1110 for (WayPoint S : seg) { 1111 if (R == null) { 1112 R = S; 1113 rx = R.eastNorth.east(); 1114 ry = R.eastNorth.north(); 1115 x = px - rx; 1116 y = py - ry; 1117 double PRsq = x * x + y * y; 1118 if (PRsq < PNminsq) { 1119 PNminsq = PRsq; 1120 bestEN = R.eastNorth; 1121 bestTime = R.time; 1122 } 1123 } else { 1124 sx = S.eastNorth.east(); 1125 sy = S.eastNorth.north(); 1126 double A = sy - ry; 1127 double B = rx - sx; 1128 double C = - A * rx - B * ry; 1129 double RSsq = A * A + B * B; 1130 if (RSsq == 0.0) continue; 1131 double PNsq = A * px + B * py + C; 1132 PNsq = PNsq * PNsq / RSsq; 1133 if (PNsq < PNminsq) { 1134 x = px - rx; 1135 y = py - ry; 1136 double PRsq = x * x + y * y; 1137 x = px - sx; 1138 y = py - sy; 1139 double PSsq = x * x + y * y; 1140 if (PRsq - PNsq <= RSsq && PSsq - PNsq <= RSsq) { 1141 double RNoverRS = Math.sqrt((PRsq - PNsq)/RSsq); 1142 double nx = rx - RNoverRS * B; 1143 double ny = ry + RNoverRS * A; 1144 bestEN = new EastNorth(nx, ny); 1145 bestTime = R.time + RNoverRS * (S.time - R.time); 1146 PNminsq = PNsq; 1147 } 1148 } 1149 R = S; 1150 rx = sx; 1151 ry = sy; 1152 } 1153 } 1154 if (R != null) { 1155 /* if there is only one point in the seg, it will do this twice, but no matter */ 1156 rx = R.eastNorth.east(); 1157 ry = R.eastNorth.north(); 1158 x = px - rx; 1159 y = py - ry; 1160 double PRsq = x * x + y * y; 1161 if (PRsq < PNminsq) { 1162 PNminsq = PRsq; 1163 bestEN = R.eastNorth; 1164 bestTime = R.time; 1165 } 1166 } 1167 } 1168 } 1169 if (bestEN == null) return null; 1170 WayPoint best = new WayPoint(Main.proj.eastNorth2latlon(bestEN)); 1171 best.time = bestTime; 1172 return best; 1173 } 1174 1174 }
Note:
See TracChangeset
for help on using the changeset viewer.
