Changeset 27760 in osm for applications
- Timestamp:
- 2012-02-14T21:43:28+01:00 (13 years ago)
- Location:
- applications/editors/josm/plugins
- Files:
-
- 1 deleted
- 5 edited
Legend:
- Unmodified
- Added
- Removed
-
applications/editors/josm/plugins/gpxfilter/src/gpxfilter/EGpxLayer.java
r26509 r27760 3 3 package gpxfilter; 4 4 5 import static org.openstreetmap.josm.gui.help.HelpUtil.ht;6 import static org.openstreetmap.josm.tools.I18n.marktr;7 5 import static org.openstreetmap.josm.tools.I18n.tr; 8 import static org.openstreetmap.josm.tools.I18n.trn;9 10 import java.awt.BasicStroke;11 import java.awt.Color;12 import java.awt.Dimension;13 import java.awt.Graphics2D;14 import java.awt.GridBagLayout;15 import java.awt.Point;16 import java.awt.event.ActionEvent;17 import java.awt.geom.Area;18 import java.awt.geom.Rectangle2D;19 import java.io.File;20 import java.net.MalformedURLException;21 import java.net.URL;22 import java.text.DateFormat;23 import java.util.ArrayList;24 import java.util.Arrays;25 import java.util.Collection;26 import java.util.Collections;27 import java.util.Comparator;28 import java.util.LinkedList;29 import java.util.List;30 import java.util.concurrent.Future;31 32 import javax.swing.AbstractAction;33 import javax.swing.Action;34 import javax.swing.Box;35 import javax.swing.ButtonGroup;36 import javax.swing.Icon;37 import javax.swing.JColorChooser;38 import javax.swing.JFileChooser;39 import javax.swing.JLabel;40 import javax.swing.JList;41 import javax.swing.JOptionPane;42 import javax.swing.JPanel;43 import javax.swing.JRadioButton;44 import javax.swing.JScrollPane;45 import javax.swing.SwingUtilities;46 import javax.swing.filechooser.FileFilter;47 6 48 7 import org.openstreetmap.josm.Main; 49 import org.openstreetmap.josm.actions.RenameLayerAction;50 import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmTaskList;51 8 import org.openstreetmap.josm.data.Bounds; 52 import org.openstreetmap.josm.data.coor.EastNorth;53 import org.openstreetmap.josm.data.coor.LatLon;54 9 import org.openstreetmap.josm.data.gpx.GpxData; 55 import org.openstreetmap.josm.data.gpx.GpxTrack; 56 import org.openstreetmap.josm.data.gpx.GpxTrackSegment; 57 import org.openstreetmap.josm.data.gpx.WayPoint; 58 import org.openstreetmap.josm.data.osm.DataSet; 59 import org.openstreetmap.josm.data.osm.Node; 60 import org.openstreetmap.josm.data.osm.Way; 61 import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor; 62 import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil; 63 import org.openstreetmap.josm.gui.HelpAwareOptionPane; 64 import org.openstreetmap.josm.gui.MapView; 65 import org.openstreetmap.josm.gui.NavigatableComponent; 66 import org.openstreetmap.josm.gui.dialogs.LayerListDialog; 67 import org.openstreetmap.josm.gui.dialogs.LayerListPopup; 68 import org.openstreetmap.josm.gui.layer.Layer; 69 import org.openstreetmap.josm.gui.layer.OsmDataLayer; 70 import org.openstreetmap.josm.gui.layer.markerlayer.AudioMarker; 71 import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer; 72 import org.openstreetmap.josm.gui.progress.NullProgressMonitor; 73 import org.openstreetmap.josm.gui.progress.PleaseWaitProgressMonitor; 74 import org.openstreetmap.josm.gui.widgets.HtmlPanel; 75 import org.openstreetmap.josm.io.JpgImporter; 76 import org.openstreetmap.josm.tools.AudioUtil; 77 import org.openstreetmap.josm.tools.DateUtils; 78 import org.openstreetmap.josm.tools.GBC; 79 import org.openstreetmap.josm.tools.ImageProvider; 80 import org.openstreetmap.josm.tools.UrlLabel; 10 import org.openstreetmap.josm.gui.layer.GpxLayer; 81 11 82 public class EGpxLayer extends Layer { 83 84 private static final String PREF_DOWNLOAD_ALONG_TRACK_DISTANCE = "gpxLayer.downloadAlongTrack.distance"; 85 private static final String PREF_DOWNLOAD_ALONG_TRACK_AREA = "gpxLayer.downloadAlongTrack.area"; 86 private static final String PREF_DOWNLOAD_ALONG_TRACK_NEAR = "gpxLayer.downloadAlongTrack.near"; 87 88 public final GpxData data = new GpxData(); 89 protected static final double PHI = Math.toRadians(15); 90 private boolean computeCacheInSync; 91 private int computeCacheMaxLineLengthUsed; 92 private Color computeCacheColorUsed; 93 private colorModes computeCacheColored; 94 private int computeCacheColorTracksTune; 95 private boolean isLocalFile; 96 97 private final List<GpxTrack> lastTracks = new ArrayList<GpxTrack>(); // List of tracks at last paint 98 private int lastUpdateCount; 99 private final Bounds bounds; 100 private volatile boolean painting; 101 102 private static class Markers { 103 public boolean timedMarkersOmitted = false; 104 public boolean untimedMarkersOmitted = false; 105 } 106 107 public EGpxLayer(Bounds b) { 108 super("GPX Data"); 109 this.bounds = b; 12 public class EGpxLayer extends GpxLayer { 13 public EGpxLayer(final Bounds b) { 14 super(new GpxData(), tr("GPX Data")); 110 15 Thread t = new Thread(new Runnable() 111 16 { 112 17 @Override 113 18 public void run() { 114 GpxGrabber grabber = new GpxGrabber( EGpxLayer.this.bounds);19 GpxGrabber grabber = new GpxGrabber(b); 115 20 while (true) { 116 21 GpxData newData; … … 126 31 } 127 32 128 if (!EGpxLayer.this.painting) { 129 Main.map.repaint(); 130 } 33 Main.map.repaint(); 131 34 } 132 35 grabber.cancel(); … … 136 39 t.start(); 137 40 } 138 139 @Override140 public Icon getIcon() {141 return ImageProvider.get("layer", "gpx_small");142 }143 144 @Override145 public synchronized Object getInfoComponent() {146 StringBuilder info = new StringBuilder();147 148 if (data.attr.containsKey("name")) {149 info.append(tr("Name: {0}", data.attr.get(GpxData.META_NAME))).append("<br>");150 }151 152 if (data.attr.containsKey("desc")) {153 info.append(tr("Description: {0}", data.attr.get(GpxData.META_DESC))).append("<br>");154 }155 156 if (data.tracks.size() > 0) {157 info.append("<table><thead align='center'><tr><td colspan='5'>"158 + trn("{0} track", "{0} tracks", data.tracks.size(), data.tracks.size())159 + "</td></tr><tr align='center'><td>" + tr("Name") + "</td><td>"160 + tr("Description") + "</td><td>" + tr("Timespan")161 + "</td><td>" + tr("Length") + "</td><td>" + tr("URL")162 + "</td></tr></thead>");163 164 for (GpxTrack trk : data.tracks) {165 WayPoint earliest = null, latest = null;166 167 info.append("<tr><td>");168 if (trk.getAttributes().containsKey("name")) {169 info.append(trk.getAttributes().get("name"));170 }171 info.append("</td><td>");172 if (trk.getAttributes().containsKey("desc")) {173 info.append(" ").append(trk.getAttributes().get("desc"));174 }175 info.append("</td><td>");176 177 for (GpxTrackSegment seg : trk.getSegments()) {178 for (WayPoint pnt : seg.getWayPoints()) {179 if (latest == null) {180 latest = earliest = pnt;181 } else {182 if (pnt.compareTo(earliest) < 0) {183 earliest = pnt;184 } else {185 latest = pnt;186 }187 }188 }189 }190 191 if (earliest != null && latest != null) {192 DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT);193 String earliestDate = df.format(earliest.getTime());194 String latestDate = df.format(latest.getTime());195 196 if (earliestDate.equals(latestDate)) {197 DateFormat tf = DateFormat.getTimeInstance(DateFormat.SHORT);198 info.append(earliestDate).append(" ");199 info.append(tf.format(earliest.getTime())).append(" - ").append(tf.format(latest.getTime()));200 } else {201 DateFormat dtf = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);202 info.append(dtf.format(earliest.getTime())).append(" - ").append(dtf.format(latest.getTime()));203 }204 205 int diff = (int) (latest.time - earliest.time);206 info.append(String.format(" (%d:%02d)", diff / 3600, (diff % 3600) / 60));207 }208 209 info.append("</td><td>");210 info.append(NavigatableComponent.getSystemOfMeasurement().getDistText(trk.length()));211 info.append("</td><td>");212 if (trk.getAttributes().containsKey("url")) {213 info.append(trk.getAttributes().get("url"));214 }215 info.append("</td></tr>");216 }217 218 info.append("</table><br><br>");219 220 }221 222 info.append(tr("Length: {0}", NavigatableComponent.getSystemOfMeasurement().getDistText(data.length()))).append("<br>");223 224 info.append(trn("{0} route, ", "{0} routes, ", data.routes.size(), data.routes.size())).append(225 trn("{0} waypoint", "{0} waypoints", data.waypoints.size(), data.waypoints.size())).append("<br>");226 227 final JScrollPane sp = new JScrollPane(new HtmlPanel(info.toString()), JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);228 sp.setPreferredSize(new Dimension(sp.getPreferredSize().width, 350));229 SwingUtilities.invokeLater(new Runnable() {230 @Override231 public void run() {232 sp.getVerticalScrollBar().setValue(0);233 }234 });235 return sp;236 }237 238 static public Color getColor(String name) {239 return Main.pref.getColor(marktr("gps point"), name != null ? "layer " + name : null, Color.gray);240 }241 242 @Override243 public Action[] getMenuEntries() {244 if (Main.applet)245 return new Action[] {246 LayerListDialog.getInstance().createShowHideLayerAction(),247 LayerListDialog.getInstance().createDeleteLayerAction(),248 SeparatorLayerAction.INSTANCE,249 new CustomizeColor(),250 new CustomizeLineDrawing(),251 new ConvertToDataLayerAction(),252 SeparatorLayerAction.INSTANCE,253 new RenameLayerAction(getAssociatedFile(), this),254 SeparatorLayerAction.INSTANCE,255 new LayerListPopup.InfoAction(this) };256 return new Action[] {257 LayerListDialog.getInstance().createShowHideLayerAction(),258 LayerListDialog.getInstance().createDeleteLayerAction(),259 SeparatorLayerAction.INSTANCE,260 new LayerSaveAction(this),261 new LayerSaveAsAction(this),262 new CustomizeColor(),263 new CustomizeLineDrawing(),264 new ImportImages(),265 new ImportAudio(),266 new MarkersFromNamedPoins(),267 new ConvertToDataLayerAction(),268 new DownloadAlongTrackAction(),269 SeparatorLayerAction.INSTANCE,270 new RenameLayerAction(getAssociatedFile(), this),271 SeparatorLayerAction.INSTANCE,272 new LayerListPopup.InfoAction(this) };273 }274 275 @Override276 public synchronized String getToolTipText() {277 StringBuilder info = new StringBuilder().append("<html>");278 279 if (data.attr.containsKey("name")) {280 info.append(tr("Name: {0}", data.attr.get(GpxData.META_NAME))).append("<br>");281 }282 283 if (data.attr.containsKey("desc")) {284 info.append(tr("Description: {0}", data.attr.get(GpxData.META_DESC))).append("<br>");285 }286 287 info.append(trn("{0} track, ", "{0} tracks, ", data.tracks.size(), data.tracks.size()));288 info.append(trn("{0} route, ", "{0} routes, ", data.routes.size(), data.routes.size()));289 info.append(trn("{0} waypoint", "{0} waypoints", data.waypoints.size(), data.waypoints.size())).append("<br>");290 291 info.append(tr("Length: {0}", NavigatableComponent.getSystemOfMeasurement().getDistText(data.length())));292 info.append("<br>");293 294 return info.append("</html>").toString();295 }296 297 @Override298 public boolean isMergable(Layer other) {299 return other instanceof EGpxLayer;300 }301 302 private synchronized int sumUpdateCount() {303 int updateCount = 0;304 for (GpxTrack track: data.tracks) {305 updateCount += track.getUpdateCount();306 }307 return updateCount;308 }309 310 @Override311 public synchronized boolean isChanged() {312 if (data.tracks.equals(lastTracks))313 return sumUpdateCount() != lastUpdateCount;314 else315 return true;316 }317 318 @Override319 public synchronized void mergeFrom(Layer from) {320 data.mergeFrom(((EGpxLayer) from).data);321 computeCacheInSync = false;322 }323 324 private static Color[] colors = new Color[256];325 static {326 for (int i = 0; i < colors.length; i++) {327 colors[i] = Color.getHSBColor(i / 300.0f, 1, 1);328 }329 }330 331 // lookup array to draw arrows without doing any math332 private static int ll0 = 9;333 private static int sl4 = 5;334 private static int sl9 = 3;335 private static int[][] dir = { { +sl4, +ll0, +ll0, +sl4 }, { -sl9, +ll0, +sl9, +ll0 }, { -ll0, +sl4, -sl4, +ll0 },336 { -ll0, -sl9, -ll0, +sl9 }, { -sl4, -ll0, -ll0, -sl4 }, { +sl9, -ll0, -sl9, -ll0 },337 { +ll0, -sl4, +sl4, -ll0 }, { +ll0, +sl9, +ll0, -sl9 }, { +sl4, +ll0, +ll0, +sl4 },338 { -sl9, +ll0, +sl9, +ll0 }, { -ll0, +sl4, -sl4, +ll0 }, { -ll0, -sl9, -ll0, +sl9 } };339 340 // the different color modes341 enum colorModes {342 none, velocity, dilution343 }344 345 @Override346 public synchronized void paint(Graphics2D g, MapView mv, Bounds box) {347 painting = true;348 lastUpdateCount = sumUpdateCount();349 lastTracks.clear();350 lastTracks.addAll(data.tracks);351 352 /****************************************************************353 ********** STEP 1 - GET CONFIG VALUES **************************354 ****************************************************************/355 // Long startTime = System.currentTimeMillis();356 Color neutralColor = getColor(getName());357 // also draw lines between points belonging to different segments358 boolean forceLines = Main.pref.getBoolean("draw.rawgps.lines.force");359 // draw direction arrows on the lines360 boolean direction = Main.pref.getBoolean("draw.rawgps.direction");361 // don't draw lines if longer than x meters362 int lineWidth = Main.pref.getInteger("draw.rawgps.linewidth",0);363 364 int maxLineLength;365 if (this.isLocalFile) {366 maxLineLength = Main.pref.getInteger("draw.rawgps.max-line-length.local", -1);367 } else {368 maxLineLength = Main.pref.getInteger("draw.rawgps.max-line-length", 200);369 }370 // draw line between points, global setting371 boolean lines = (Main.pref.getBoolean("draw.rawgps.lines", true) || (Main.pref372 .getBoolean("draw.rawgps.lines.localfiles") && this.isLocalFile));373 String linesKey = "draw.rawgps.lines.layer " + getName();374 // draw lines, per-layer setting375 if (Main.pref.hasKey(linesKey)) {376 lines = Main.pref.getBoolean(linesKey);377 }378 // paint large dots for points379 boolean large = Main.pref.getBoolean("draw.rawgps.large");380 boolean hdopcircle = Main.pref.getBoolean("draw.rawgps.hdopcircle", true);381 // color the lines382 colorModes colored = colorModes.none;383 try {384 colored = colorModes.values()[Main.pref.getInteger("draw.rawgps.colors", 0)];385 } catch (Exception e) {386 }387 // paint direction arrow with alternate math. may be faster388 boolean alternatedirection = Main.pref.getBoolean("draw.rawgps.alternatedirection");389 // don't draw arrows nearer to each other than this390 int delta = Main.pref.getInteger("draw.rawgps.min-arrow-distance", 0);391 // allows to tweak line coloring for different speed levels.392 int colorTracksTune = Main.pref.getInteger("draw.rawgps.colorTracksTune", 45);393 394 if(lineWidth != 0)395 {396 g.setStroke(new BasicStroke(lineWidth,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND));397 }398 399 /****************************************************************400 ********** STEP 2a - CHECK CACHE VALIDITY **********************401 ****************************************************************/402 if ((computeCacheMaxLineLengthUsed != maxLineLength) || (!neutralColor.equals(computeCacheColorUsed))403 || (computeCacheColored != colored) || (computeCacheColorTracksTune != colorTracksTune)) {404 // System.out.println("(re-)computing gpx line styles, reason: CCIS=" +405 // computeCacheInSync + " CCMLLU=" + (computeCacheMaxLineLengthUsed != maxLineLength) +406 // " CCCU=" + (!neutralColor.equals(computeCacheColorUsed)) + " CCC=" +407 // (computeCacheColored != colored));408 computeCacheMaxLineLengthUsed = maxLineLength;409 computeCacheInSync = false;410 computeCacheColorUsed = neutralColor;411 computeCacheColored = colored;412 computeCacheColorTracksTune = colorTracksTune;413 }414 415 /****************************************************************416 ********** STEP 2b - RE-COMPUTE CACHE DATA *********************417 ****************************************************************/418 if (!computeCacheInSync) { // don't compute if the cache is good419 WayPoint oldWp = null;420 for (GpxTrack trk : data.tracks) {421 for (GpxTrackSegment segment : trk.getSegments()) {422 if (!forceLines) { // don't draw lines between segments, unless forced to423 oldWp = null;424 }425 for (WayPoint trkPnt : segment.getWayPoints()) {426 LatLon c = trkPnt.getCoor();427 if (Double.isNaN(c.lat()) || Double.isNaN(c.lon())) {428 continue;429 }430 trkPnt.customColoring = neutralColor;431 if (oldWp != null) {432 double dist = c.greatCircleDistance(oldWp.getCoor());433 434 switch (colored) {435 case velocity:436 double dtime = trkPnt.time - oldWp.time;437 double vel = dist / dtime;438 double velColor = vel / colorTracksTune * 255;439 // Bad case first440 if (dtime <= 0 || vel < 0 || velColor > 255) {441 trkPnt.customColoring = colors[255];442 } else {443 trkPnt.customColoring = colors[(int) (velColor)];444 }445 break;446 447 case dilution:448 if (trkPnt.attr.get("hdop") != null) {449 float hdop = ((Float) trkPnt.attr.get("hdop")).floatValue();450 if (hdop < 0) {451 hdop = 0;452 }453 int hdoplvl = Math.round(hdop * Main.pref.getInteger("hdop.factor", 25));454 // High hdop is bad, but high values in colors are green.455 // Therefore inverse the logic456 int hdopcolor = 255 - (hdoplvl > 255 ? 255 : hdoplvl);457 trkPnt.customColoring = colors[hdopcolor];458 }459 break;460 }461 462 if (maxLineLength == -1 || dist <= maxLineLength) {463 trkPnt.drawLine = true;464 trkPnt.dir = (int) oldWp.getCoor().heading(trkPnt.getCoor());465 } else {466 trkPnt.drawLine = false;467 }468 } else { // make sure we reset outdated data469 trkPnt.drawLine = false;470 }471 oldWp = trkPnt;472 }473 }474 }475 computeCacheInSync = true;476 }477 478 List<Collection<WayPoint>> visibleSegments = new ArrayList<Collection<WayPoint>>();479 for (GpxTrack trk: data.tracks) {480 for (GpxTrackSegment trkSeg: trk.getSegments()) {481 if (trkSeg.getBounds() != null && trkSeg.getBounds().intersects(box)) {482 visibleSegments.add(trkSeg.getWayPoints());483 }484 }485 }486 487 /****************************************************************488 ********** STEP 3a - DRAW LINES ********************************489 ****************************************************************/490 if (lines) {491 Point old = null;492 for (Collection<WayPoint> segment : visibleSegments) {493 for (WayPoint trkPnt : segment) {494 LatLon c = trkPnt.getCoor();495 if (Double.isNaN(c.lat()) || Double.isNaN(c.lon())) {496 continue;497 }498 Point screen = mv.getPoint(trkPnt.getEastNorth());499 if (trkPnt.drawLine) {500 // skip points that are on the same screenposition501 if (old != null && ((old.x != screen.x) || (old.y != screen.y))) {502 g.setColor(trkPnt.customColoring);503 g.drawLine(old.x, old.y, screen.x, screen.y);504 }505 }506 old = screen;507 } // end for trkpnt508 } // end for segment509 } // end if lines510 511 /****************************************************************512 ********** STEP 3b - DRAW NICE ARROWS **************************513 ****************************************************************/514 if (lines && direction && !alternatedirection) {515 Point old = null;516 Point oldA = null; // last arrow painted517 for (Collection<WayPoint> segment : visibleSegments) {518 for (WayPoint trkPnt : segment) {519 LatLon c = trkPnt.getCoor();520 if (Double.isNaN(c.lat()) || Double.isNaN(c.lon())) {521 continue;522 }523 if (trkPnt.drawLine) {524 Point screen = mv.getPoint(trkPnt.getEastNorth());525 // skip points that are on the same screenposition526 if (old != null527 && (oldA == null || screen.x < oldA.x - delta || screen.x > oldA.x + delta528 || screen.y < oldA.y - delta || screen.y > oldA.y + delta)) {529 g.setColor(trkPnt.customColoring);530 double t = Math.atan2(screen.y - old.y, screen.x - old.x) + Math.PI;531 g.drawLine(screen.x, screen.y, (int) (screen.x + 10 * Math.cos(t - PHI)),532 (int) (screen.y + 10 * Math.sin(t - PHI)));533 g.drawLine(screen.x, screen.y, (int) (screen.x + 10 * Math.cos(t + PHI)),534 (int) (screen.y + 10 * Math.sin(t + PHI)));535 oldA = screen;536 }537 old = screen;538 }539 } // end for trkpnt540 } // end for segment541 } // end if lines542 543 /****************************************************************544 ********** STEP 3c - DRAW FAST ARROWS **************************545 ****************************************************************/546 if (lines && direction && alternatedirection) {547 Point old = null;548 Point oldA = null; // last arrow painted549 for (Collection<WayPoint> segment : visibleSegments) {550 for (WayPoint trkPnt : segment) {551 LatLon c = trkPnt.getCoor();552 if (Double.isNaN(c.lat()) || Double.isNaN(c.lon())) {553 continue;554 }555 if (trkPnt.drawLine) {556 Point screen = mv.getPoint(trkPnt.getEastNorth());557 // skip points that are on the same screenposition558 if (old != null559 && (oldA == null || screen.x < oldA.x - delta || screen.x > oldA.x + delta560 || screen.y < oldA.y - delta || screen.y > oldA.y + delta)) {561 g.setColor(trkPnt.customColoring);562 g.drawLine(screen.x, screen.y, screen.x + dir[trkPnt.dir][0], screen.y563 + dir[trkPnt.dir][1]);564 g.drawLine(screen.x, screen.y, screen.x + dir[trkPnt.dir][2], screen.y565 + dir[trkPnt.dir][3]);566 oldA = screen;567 }568 old = screen;569 }570 } // end for trkpnt571 } // end for segment572 } // end if lines573 574 /****************************************************************575 ********** STEP 3d - DRAW LARGE POINTS AND HDOP CIRCLE *********576 ****************************************************************/577 if (large || hdopcircle) {578 g.setColor(neutralColor);579 for (Collection<WayPoint> segment : visibleSegments) {580 for (WayPoint trkPnt : segment) {581 LatLon c = trkPnt.getCoor();582 if (Double.isNaN(c.lat()) || Double.isNaN(c.lon())) {583 continue;584 }585 Point screen = mv.getPoint(trkPnt.getEastNorth());586 g.setColor(trkPnt.customColoring);587 if (hdopcircle && trkPnt.attr.get("hdop") != null) {588 // hdop value589 float hdop = ((Float)trkPnt.attr.get("hdop")).floatValue();590 if (hdop < 0) {591 hdop = 0;592 }593 // hdop pixels594 int hdopp = mv.getPoint(new LatLon(trkPnt.getCoor().lat(), trkPnt.getCoor().lon() + 2*6*hdop*360/40000000)).x - screen.x;595 g.drawArc(screen.x-hdopp/2, screen.y-hdopp/2, hdopp, hdopp, 0, 360);596 }597 if (large) {598 g.fillRect(screen.x-1, screen.y-1, 3, 3);599 }600 } // end for trkpnt601 } // end for segment602 } // end if large || hdopcircle603 604 /****************************************************************605 ********** STEP 3e - DRAW SMALL POINTS FOR LINES ***************606 ****************************************************************/607 if (!large && lines) {608 g.setColor(neutralColor);609 for (Collection<WayPoint> segment : visibleSegments) {610 for (WayPoint trkPnt : segment) {611 LatLon c = trkPnt.getCoor();612 if (Double.isNaN(c.lat()) || Double.isNaN(c.lon())) {613 continue;614 }615 if (!trkPnt.drawLine) {616 Point screen = mv.getPoint(trkPnt.getEastNorth());617 g.drawRect(screen.x, screen.y, 0, 0);618 }619 } // end for trkpnt620 } // end for segment621 } // end if large622 623 /****************************************************************624 ********** STEP 3f - DRAW SMALL POINTS INSTEAD OF LINES ********625 ****************************************************************/626 if (!large && !lines) {627 g.setColor(neutralColor);628 for (Collection<WayPoint> segment : visibleSegments) {629 for (WayPoint trkPnt : segment) {630 LatLon c = trkPnt.getCoor();631 if (Double.isNaN(c.lat()) || Double.isNaN(c.lon())) {632 continue;633 }634 Point screen = mv.getPoint(trkPnt.getEastNorth());635 g.setColor(trkPnt.customColoring);636 g.drawRect(screen.x, screen.y, 0, 0);637 } // end for trkpnt638 } // end for segment639 } // end if large640 641 // Long duration = System.currentTimeMillis() - startTime;642 // System.out.println(duration);643 painting = false;644 } // end paint645 646 @Override647 public synchronized void visitBoundingBox(BoundingXYVisitor v) {648 v.visit(data.recalculateBounds());649 }650 651 public class ConvertToDataLayerAction extends AbstractAction {652 public ConvertToDataLayerAction() {653 super(tr("Convert to data layer"), ImageProvider.get("converttoosm"));654 }655 656 public void actionPerformed(ActionEvent e) {657 JPanel msg = new JPanel(new GridBagLayout());658 msg659 .add(660 new JLabel(661 tr("<html>Upload of unprocessed GPS data as map data is considered harmful.<br>If you want to upload traces, look here:")),662 GBC.eol());663 msg.add(new UrlLabel(tr("http://www.openstreetmap.org/traces")), GBC.eop());664 if (!ConditionalOptionPaneUtil.showConfirmationDialog("convert_to_data", Main.parent, msg, tr("Warning"),665 JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE, JOptionPane.OK_OPTION))666 return;667 DataSet ds = new DataSet();668 for (GpxTrack trk : data.tracks) {669 for (GpxTrackSegment segment : trk.getSegments()) {670 List<Node> nodes = new ArrayList<Node>();671 for (WayPoint p : segment.getWayPoints()) {672 Node n = new Node(p.getCoor());673 String timestr = p.getString("time");674 if (timestr != null) {675 n.setTimestamp(DateUtils.fromString(timestr));676 }677 ds.addPrimitive(n);678 nodes.add(n);679 }680 Way w = new Way();681 w.setNodes(nodes);682 ds.addPrimitive(w);683 }684 }685 Main.main686 .addLayer(new OsmDataLayer(ds, tr("Converted from: {0}", EGpxLayer.this.getName()), getAssociatedFile()));687 Main.main.removeLayer(EGpxLayer.this);688 }689 }690 691 @Override692 public File getAssociatedFile() {693 return data.storageFile;694 }695 696 @Override697 public void setAssociatedFile(File file) {698 data.storageFile = file;699 }700 701 /**702 * Action that issues a series of download requests to the API, following the GPX track.703 *704 * @author fred705 */706 public class DownloadAlongTrackAction extends AbstractAction {707 public DownloadAlongTrackAction() {708 super(tr("Download from OSM along this track"), ImageProvider.get("downloadalongtrack"));709 }710 711 public void actionPerformed(ActionEvent e) {712 JPanel msg = new JPanel(new GridBagLayout());713 Integer dist[] = { 5000, 500, 50 };714 Integer area[] = { 20, 10, 5, 1 };715 716 msg.add(new JLabel(tr("Download everything within:")), GBC.eol());717 String s[] = new String[dist.length];718 for (int i = 0; i < dist.length; ++i) {719 s[i] = tr("{0} meters", dist[i]);720 }721 JList buffer = new JList(s);722 buffer.setSelectedIndex(Main.pref.getInteger(PREF_DOWNLOAD_ALONG_TRACK_DISTANCE, 0));723 msg.add(buffer, GBC.eol());724 725 msg.add(new JLabel(tr("Maximum area per request:")), GBC.eol());726 s = new String[area.length];727 for (int i = 0; i < area.length; ++i) {728 s[i] = tr("{0} sq km", area[i]);729 }730 JList maxRect = new JList(s);731 maxRect.setSelectedIndex(Main.pref.getInteger(PREF_DOWNLOAD_ALONG_TRACK_AREA, 0));732 msg.add(maxRect, GBC.eol());733 734 msg.add(new JLabel(tr("Download near:")), GBC.eol());735 JList downloadNear = new JList(new String[] { tr("track only"), tr("waypoints only"), tr("track and waypoints") });736 int NEAR_TRACK=0;737 int NEAR_WAYPOINTS=1;738 int NEAR_BOTH=2;739 740 downloadNear.setSelectedIndex(Main.pref.getInteger(PREF_DOWNLOAD_ALONG_TRACK_NEAR, 0));741 msg.add(downloadNear, GBC.eol());742 743 int ret = JOptionPane.showConfirmDialog(744 Main.parent,745 msg,746 tr("Download from OSM along this track"),747 JOptionPane.OK_CANCEL_OPTION,748 JOptionPane.QUESTION_MESSAGE749 );750 switch(ret) {751 case JOptionPane.CANCEL_OPTION:752 case JOptionPane.CLOSED_OPTION:753 return;754 default:755 // continue756 }757 758 Main.pref.putInteger(PREF_DOWNLOAD_ALONG_TRACK_DISTANCE, buffer.getSelectedIndex());759 Main.pref.putInteger(PREF_DOWNLOAD_ALONG_TRACK_AREA, maxRect.getSelectedIndex());760 int near = downloadNear.getSelectedIndex();761 Main.pref.putInteger(PREF_DOWNLOAD_ALONG_TRACK_NEAR, near);762 763 /*764 * Find the average latitude for the data we're contemplating, so we can know how many765 * metres per degree of longitude we have.766 */767 double latsum = 0;768 int latcnt = 0;769 770 if (near == NEAR_TRACK || near == NEAR_BOTH) {771 for (GpxTrack trk : data.tracks) {772 for (GpxTrackSegment segment : trk.getSegments()) {773 for (WayPoint p : segment.getWayPoints()) {774 latsum += p.getCoor().lat();775 latcnt++;776 }777 }778 }779 }780 781 if (near == NEAR_WAYPOINTS || near == NEAR_BOTH) {782 for (WayPoint p : data.waypoints) {783 latsum += p.getCoor().lat();784 latcnt++;785 }786 }787 788 double avglat = latsum / latcnt;789 double scale = Math.cos(Math.toRadians(avglat));790 791 /*792 * Compute buffer zone extents and maximum bounding box size. Note that the maximum we793 * ever offer is a bbox area of 0.002, while the API theoretically supports 0.25, but as794 * soon as you touch any built-up area, that kind of bounding box will download forever795 * and then stop because it has more than 50k nodes.796 */797 Integer i = buffer.getSelectedIndex();798 int buffer_dist = dist[i < 0 ? 0 : i];799 double buffer_y = buffer_dist / 100000.0;800 double buffer_x = buffer_y / scale;801 i = maxRect.getSelectedIndex();802 double max_area = area[i < 0 ? 0 : i] / 10000.0 / scale;803 Area a = new Area();804 Rectangle2D r = new Rectangle2D.Double();805 806 /*807 * Collect the combined area of all gpx points plus buffer zones around them. We ignore808 * points that lie closer to the previous point than the given buffer size because809 * otherwise this operation takes ages.810 */811 LatLon previous = null;812 if (near == NEAR_TRACK || near == NEAR_BOTH) {813 for (GpxTrack trk : data.tracks) {814 for (GpxTrackSegment segment : trk.getSegments()) {815 for (WayPoint p : segment.getWayPoints()) {816 LatLon c = p.getCoor();817 if (previous == null || c.greatCircleDistance(previous) > buffer_dist) {818 // we add a buffer around the point.819 r.setRect(c.lon() - buffer_x, c.lat() - buffer_y, 2 * buffer_x, 2 * buffer_y);820 a.add(new Area(r));821 previous = c;822 }823 }824 }825 }826 }827 if (near == NEAR_WAYPOINTS || near == NEAR_BOTH) {828 for (WayPoint p : data.waypoints) {829 LatLon c = p.getCoor();830 if (previous == null || c.greatCircleDistance(previous) > buffer_dist) {831 // we add a buffer around the point.832 r.setRect(c.lon() - buffer_x, c.lat() - buffer_y, 2 * buffer_x, 2 * buffer_y);833 a.add(new Area(r));834 previous = c;835 }836 }837 }838 839 /*840 * Area "a" now contains the hull that we would like to download data for. however we841 * can only download rectangles, so the following is an attempt at finding a number of842 * rectangles to download.843 *844 * The idea is simply: Start out with the full bounding box. If it is too large, then845 * split it in half and repeat recursively for each half until you arrive at something846 * small enough to download. The algorithm is improved by always using the intersection847 * between the rectangle and the actual desired area. For example, if you have a track848 * that goes like this: +----+ | /| | / | | / | |/ | +----+ then we would first look at849 * downloading the whole rectangle (assume it's too big), after that we split it in half850 * (upper and lower half), but we donot request the full upper and lower rectangle, only851 * the part of the upper/lower rectangle that actually has something in it.852 */853 854 List<Rectangle2D> toDownload = new ArrayList<Rectangle2D>();855 856 addToDownload(a, a.getBounds(), toDownload, max_area);857 858 msg = new JPanel(new GridBagLayout());859 860 msg.add(new JLabel(861 tr("<html>This action will require {0} individual<br>"862 + "download requests. Do you wish<br>to continue?</html>",863 toDownload.size())), GBC.eol());864 865 if (toDownload.size() > 1) {866 ret = JOptionPane.showConfirmDialog(867 Main.parent,868 msg,869 tr("Download from OSM along this track"),870 JOptionPane.OK_CANCEL_OPTION,871 JOptionPane.PLAIN_MESSAGE872 );873 switch(ret) {874 case JOptionPane.CANCEL_OPTION:875 case JOptionPane.CLOSED_OPTION:876 return;877 default:878 // continue879 }880 }881 final PleaseWaitProgressMonitor monitor = new PleaseWaitProgressMonitor(tr("Download data"));882 final Future<?> future = new DownloadOsmTaskList().download(false, toDownload, monitor);883 Main.worker.submit(884 new Runnable() {885 public void run() {886 try {887 future.get();888 } catch(Exception e) {889 e.printStackTrace();890 return;891 }892 monitor.close();893 }894 }895 );896 }897 }898 899 private static void addToDownload(Area a, Rectangle2D r, Collection<Rectangle2D> results, double max_area) {900 Area tmp = new Area(r);901 // intersect with sought-after area902 tmp.intersect(a);903 if (tmp.isEmpty())904 return;905 Rectangle2D bounds = tmp.getBounds2D();906 if (bounds.getWidth() * bounds.getHeight() > max_area) {907 // the rectangle gets too large; split it and make recursive call.908 Rectangle2D r1;909 Rectangle2D r2;910 if (bounds.getWidth() > bounds.getHeight()) {911 // rectangles that are wider than high are split into a left and right half,912 r1 = new Rectangle2D.Double(bounds.getX(), bounds.getY(), bounds.getWidth() / 2, bounds.getHeight());913 r2 = new Rectangle2D.Double(bounds.getX() + bounds.getWidth() / 2, bounds.getY(),914 bounds.getWidth() / 2, bounds.getHeight());915 } else {916 // others into a top and bottom half.917 r1 = new Rectangle2D.Double(bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight() / 2);918 r2 = new Rectangle2D.Double(bounds.getX(), bounds.getY() + bounds.getHeight() / 2, bounds.getWidth(),919 bounds.getHeight() / 2);920 }921 addToDownload(a, r1, results, max_area);922 addToDownload(a, r2, results, max_area);923 } else {924 results.add(bounds);925 }926 }927 928 /**929 * Makes a new marker layer derived from this GpxLayer containing at least one audio marker930 * which the given audio file is associated with. Markers are derived from the following (a)931 * explict waypoints in the GPX layer, or (b) named trackpoints in the GPX layer, or (d)932 * timestamp on the wav file (e) (in future) voice recognised markers in the sound recording (f)933 * a single marker at the beginning of the track934 * @param wavFile : the file to be associated with the markers in the new marker layer935 * @param markers : keeps track of warning messages to avoid repeated warnings936 */937 private void importAudio(File wavFile, MarkerLayer ml, double firstStartTime, Markers markers) {938 URL url = null;939 try {940 url = wavFile.toURI().toURL();941 } catch (MalformedURLException e) {942 System.err.println("Unable to convert filename " + wavFile.getAbsolutePath() + " to URL");943 }944 Collection<WayPoint> waypoints = new ArrayList<WayPoint>();945 boolean timedMarkersOmitted = false;946 boolean untimedMarkersOmitted = false;947 double snapDistance = Main.pref.getDouble("marker.audiofromuntimedwaypoints.distance", 1.0e-3); /*948 * about949 * 25950 * m951 */952 WayPoint wayPointFromTimeStamp = null;953 954 // determine time of first point in track955 double firstTime = -1.0;956 if (data.tracks != null && !data.tracks.isEmpty()) {957 for (GpxTrack track : data.tracks) {958 for (GpxTrackSegment seg : track.getSegments()) {959 for (WayPoint w : seg.getWayPoints()) {960 firstTime = w.time;961 break;962 }963 if (firstTime >= 0.0) {964 break;965 }966 }967 if (firstTime >= 0.0) {968 break;969 }970 }971 }972 if (firstTime < 0.0) {973 JOptionPane.showMessageDialog(974 Main.parent,975 tr("No GPX track available in layer to associate audio with."),976 tr("Error"),977 JOptionPane.ERROR_MESSAGE978 );979 return;980 }981 982 // (a) try explicit timestamped waypoints - unless suppressed983 if (Main.pref.getBoolean("marker.audiofromexplicitwaypoints", true) && data.waypoints != null984 && !data.waypoints.isEmpty()) {985 for (WayPoint w : data.waypoints) {986 if (w.time > firstTime) {987 waypoints.add(w);988 } else if (w.time > 0.0) {989 timedMarkersOmitted = true;990 }991 }992 }993 994 // (b) try explicit waypoints without timestamps - unless suppressed995 if (Main.pref.getBoolean("marker.audiofromuntimedwaypoints", true) && data.waypoints != null996 && !data.waypoints.isEmpty()) {997 for (WayPoint w : data.waypoints) {998 if (waypoints.contains(w)) {999 continue;1000 }1001 WayPoint wNear = nearestPointOnTrack(w.getEastNorth(), snapDistance);1002 if (wNear != null) {1003 WayPoint wc = new WayPoint(w.getCoor());1004 wc.time = wNear.time;1005 if (w.attr.containsKey("name")) {1006 wc.attr.put("name", w.getString("name"));1007 }1008 waypoints.add(wc);1009 } else {1010 untimedMarkersOmitted = true;1011 }1012 }1013 }1014 1015 // (c) use explicitly named track points, again unless suppressed1016 if ((Main.pref.getBoolean("marker.audiofromnamedtrackpoints", false)) && data.tracks != null1017 && !data.tracks.isEmpty()) {1018 for (GpxTrack track : data.tracks) {1019 for (GpxTrackSegment seg : track.getSegments()) {1020 for (WayPoint w : seg.getWayPoints()) {1021 if (w.attr.containsKey("name") || w.attr.containsKey("desc")) {1022 waypoints.add(w);1023 }1024 }1025 }1026 }1027 }1028 1029 // (d) use timestamp of file as location on track1030 if ((Main.pref.getBoolean("marker.audiofromwavtimestamps", false)) && data.tracks != null1031 && !data.tracks.isEmpty()) {1032 double lastModified = wavFile.lastModified() / 1000.0; // lastModified is in1033 // milliseconds1034 double duration = AudioUtil.getCalibratedDuration(wavFile);1035 double startTime = lastModified - duration;1036 startTime = firstStartTime + (startTime - firstStartTime)1037 / Main.pref.getDouble("audio.calibration", "1.0" /* default, ratio */);1038 WayPoint w1 = null;1039 WayPoint w2 = null;1040 1041 for (GpxTrack track : data.tracks) {1042 for (GpxTrackSegment seg : track.getSegments()) {1043 for (WayPoint w : seg.getWayPoints()) {1044 if (startTime < w.time) {1045 w2 = w;1046 break;1047 }1048 w1 = w;1049 }1050 if (w2 != null) {1051 break;1052 }1053 }1054 }1055 1056 if (w1 == null || w2 == null) {1057 timedMarkersOmitted = true;1058 } else {1059 wayPointFromTimeStamp = new WayPoint(w1.getCoor().interpolate(w2.getCoor(),1060 (startTime - w1.time) / (w2.time - w1.time)));1061 wayPointFromTimeStamp.time = startTime;1062 String name = wavFile.getName();1063 int dot = name.lastIndexOf(".");1064 if (dot > 0) {1065 name = name.substring(0, dot);1066 }1067 wayPointFromTimeStamp.attr.put("name", name);1068 waypoints.add(wayPointFromTimeStamp);1069 }1070 }1071 1072 // (e) analyse audio for spoken markers here, in due course1073 1074 // (f) simply add a single marker at the start of the track1075 if ((Main.pref.getBoolean("marker.audiofromstart") || waypoints.isEmpty()) && data.tracks != null1076 && !data.tracks.isEmpty()) {1077 boolean gotOne = false;1078 for (GpxTrack track : data.tracks) {1079 for (GpxTrackSegment seg : track.getSegments()) {1080 for (WayPoint w : seg.getWayPoints()) {1081 WayPoint wStart = new WayPoint(w.getCoor());1082 wStart.attr.put("name", "start");1083 wStart.time = w.time;1084 waypoints.add(wStart);1085 gotOne = true;1086 break;1087 }1088 if (gotOne) {1089 break;1090 }1091 }1092 if (gotOne) {1093 break;1094 }1095 }1096 }1097 1098 /* we must have got at least one waypoint now */1099 1100 Collections.sort((ArrayList<WayPoint>) waypoints, new Comparator<WayPoint>() {1101 public int compare(WayPoint a, WayPoint b) {1102 return a.time <= b.time ? -1 : 1;1103 }1104 });1105 1106 firstTime = -1.0; /* this time of the first waypoint, not first trackpoint */1107 for (WayPoint w : waypoints) {1108 if (firstTime < 0.0) {1109 firstTime = w.time;1110 }1111 double offset = w.time - firstTime;1112 AudioMarker am = new AudioMarker(w.getCoor(), w, url, ml, w.time, offset);1113 /*1114 * timeFromAudio intended for future use to shift markers of this type on1115 * synchronization1116 */1117 if (w == wayPointFromTimeStamp) {1118 am.timeFromAudio = true;1119 }1120 ml.data.add(am);1121 }1122 1123 if (timedMarkersOmitted && !markers.timedMarkersOmitted) {1124 JOptionPane1125 .showMessageDialog(1126 Main.parent,1127 tr("Some waypoints with timestamps from before the start of the track or after the end were omitted or moved to the start."));1128 markers.timedMarkersOmitted = timedMarkersOmitted;1129 }1130 if (untimedMarkersOmitted && !markers.untimedMarkersOmitted) {1131 JOptionPane1132 .showMessageDialog(1133 Main.parent,1134 tr("Some waypoints which were too far from the track to sensibly estimate their time were omitted."));1135 markers.untimedMarkersOmitted = untimedMarkersOmitted;1136 }1137 }1138 1139 /**1140 * Makes a WayPoint at the projection of point P onto the track providing P is less than1141 * tolerance away from the track1142 *1143 * @param P : the point to determine the projection for1144 * @param tolerance : must be no further than this from the track1145 * @return the closest point on the track to P, which may be the first or last point if off the1146 * end of a segment, or may be null if nothing close enough1147 */1148 public WayPoint nearestPointOnTrack(EastNorth P, double tolerance) {1149 /*1150 * assume the coordinates of P are xp,yp, and those of a section of track between two1151 * trackpoints are R=xr,yr and S=xs,ys. Let N be the projected point.1152 *1153 * The equation of RS is Ax + By + C = 0 where A = ys - yr B = xr - xs C = - Axr - Byr1154 *1155 * Also, note that the distance RS^2 is A^2 + B^21156 *1157 * If RS^2 == 0.0 ignore the degenerate section of track1158 *1159 * PN^2 = (Axp + Byp + C)^2 / RS^2 that is the distance from P to the line1160 *1161 * so if PN^2 is less than PNmin^2 (initialized to tolerance) we can reject the line;1162 * otherwise... determine if the projected poijnt lies within the bounds of the line: PR^2 -1163 * PN^2 <= RS^2 and PS^2 - PN^2 <= RS^21164 *1165 * where PR^2 = (xp - xr)^2 + (yp-yr)^2 and PS^2 = (xp - xs)^2 + (yp-ys)^21166 *1167 * If so, calculate N as xn = xr + (RN/RS) B yn = y1 + (RN/RS) A1168 *1169 * where RN = sqrt(PR^2 - PN^2)1170 */1171 1172 double PNminsq = tolerance * tolerance;1173 EastNorth bestEN = null;1174 double bestTime = 0.0;1175 double px = P.east();1176 double py = P.north();1177 double rx = 0.0, ry = 0.0, sx, sy, x, y;1178 if (data.tracks == null)1179 return null;1180 for (GpxTrack track : data.tracks) {1181 for (GpxTrackSegment seg : track.getSegments()) {1182 WayPoint R = null;1183 for (WayPoint S : seg.getWayPoints()) {1184 EastNorth c = S.getEastNorth();1185 if (R == null) {1186 R = S;1187 rx = c.east();1188 ry = c.north();1189 x = px - rx;1190 y = py - ry;1191 double PRsq = x * x + y * y;1192 if (PRsq < PNminsq) {1193 PNminsq = PRsq;1194 bestEN = c;1195 bestTime = R.time;1196 }1197 } else {1198 sx = c.east();1199 sy = c.north();1200 double A = sy - ry;1201 double B = rx - sx;1202 double C = -A * rx - B * ry;1203 double RSsq = A * A + B * B;1204 if (RSsq == 0.0) {1205 continue;1206 }1207 double PNsq = A * px + B * py + C;1208 PNsq = PNsq * PNsq / RSsq;1209 if (PNsq < PNminsq) {1210 x = px - rx;1211 y = py - ry;1212 double PRsq = x * x + y * y;1213 x = px - sx;1214 y = py - sy;1215 double PSsq = x * x + y * y;1216 if (PRsq - PNsq <= RSsq && PSsq - PNsq <= RSsq) {1217 double RNoverRS = Math.sqrt((PRsq - PNsq) / RSsq);1218 double nx = rx - RNoverRS * B;1219 double ny = ry + RNoverRS * A;1220 bestEN = new EastNorth(nx, ny);1221 bestTime = R.time + RNoverRS * (S.time - R.time);1222 PNminsq = PNsq;1223 }1224 }1225 R = S;1226 rx = sx;1227 ry = sy;1228 }1229 }1230 if (R != null) {1231 EastNorth c = R.getEastNorth();1232 /* if there is only one point in the seg, it will do this twice, but no matter */1233 rx = c.east();1234 ry = c.north();1235 x = px - rx;1236 y = py - ry;1237 double PRsq = x * x + y * y;1238 if (PRsq < PNminsq) {1239 PNminsq = PRsq;1240 bestEN = c;1241 bestTime = R.time;1242 }1243 }1244 }1245 }1246 if (bestEN == null)1247 return null;1248 WayPoint best = new WayPoint(Main.getProjection().eastNorth2latlon(bestEN));1249 best.time = bestTime;1250 return best;1251 }1252 1253 private class CustomizeLineDrawing extends AbstractAction {1254 1255 CustomizeLineDrawing() {1256 super(tr("Customize line drawing"), ImageProvider.get("mapmode/addsegment"));1257 }1258 1259 public void actionPerformed(ActionEvent e) {1260 JRadioButton[] r = new JRadioButton[3];1261 r[0] = new JRadioButton(tr("Use global settings."));1262 r[1] = new JRadioButton(tr("Draw lines between points for this layer."));1263 r[2] = new JRadioButton(tr("Do not draw lines between points for this layer."));1264 ButtonGroup group = new ButtonGroup();1265 Box panel = Box.createVerticalBox();1266 for (JRadioButton b : r) {1267 group.add(b);1268 panel.add(b);1269 }1270 String propName = "draw.rawgps.lines.layer " + getName();1271 if (Main.pref.hasKey(propName)) {1272 group.setSelected(r[Main.pref.getBoolean(propName) ? 1 : 2].getModel(), true);1273 } else {1274 group.setSelected(r[0].getModel(), true);1275 }1276 int answer = JOptionPane.showConfirmDialog(Main.parent, panel,1277 tr("Select line drawing options"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);1278 switch (answer) {1279 case JOptionPane.CANCEL_OPTION:1280 case JOptionPane.CLOSED_OPTION:1281 return;1282 default:1283 // continue1284 }1285 if (group.getSelection() == r[0].getModel()) {1286 Main.pref.put(propName, null);1287 } else {1288 Main.pref.put(propName, group.getSelection() == r[1].getModel());1289 }1290 Main.map.repaint();1291 }1292 }1293 1294 private class CustomizeColor extends AbstractAction {1295 1296 public CustomizeColor() {1297 super(tr("Customize Color"), ImageProvider.get("colorchooser"));1298 putValue("help", "Action/LayerCustomizeColor");1299 }1300 1301 public void actionPerformed(ActionEvent e) {1302 JColorChooser c = new JColorChooser(getColor(getName()));1303 Object[] options = new Object[] { tr("OK"), tr("Cancel"), tr("Default") };1304 int answer = JOptionPane.showOptionDialog(1305 Main.parent,1306 c,1307 tr("Choose a color"),1308 JOptionPane.OK_CANCEL_OPTION,1309 JOptionPane.PLAIN_MESSAGE,1310 null,1311 options, options[0]1312 );1313 switch (answer) {1314 case 0:1315 Main.pref.putColor("layer " + getName(), c.getColor());1316 break;1317 case 1:1318 return;1319 case 2:1320 Main.pref.putColor("layer " + getName(), null);1321 break;1322 }1323 Main.map.repaint();1324 }1325 1326 }1327 1328 private class MarkersFromNamedPoins extends AbstractAction {1329 1330 public MarkersFromNamedPoins() {1331 super(tr("Markers From Named Points"), ImageProvider.get("addmarkers"));1332 putValue("help", "Action/MarkersFromNamedPoints");1333 }1334 1335 public void actionPerformed(ActionEvent e) {1336 GpxData namedTrackPoints = new GpxData();1337 for (GpxTrack track : data.tracks) {1338 for (GpxTrackSegment seg : track.getSegments()) {1339 for (WayPoint point : seg.getWayPoints())1340 if (point.attr.containsKey("name") || point.attr.containsKey("desc")) {1341 namedTrackPoints.waypoints.add(point);1342 }1343 }1344 }1345 1346 MarkerLayer ml = new MarkerLayer(namedTrackPoints, tr("Named Trackpoints from {0}", getName()),1347 getAssociatedFile(), null);1348 if (ml.data.size() > 0) {1349 Main.main.addLayer(ml);1350 }1351 1352 }1353 }1354 1355 private class ImportAudio extends AbstractAction {1356 1357 public ImportAudio() {1358 super(tr("Import Audio"), ImageProvider.get("importaudio"));1359 putValue("help", "ImportAudio");1360 }1361 1362 private void warnCantImportIntoServerLayer(EGpxLayer layer) {1363 String msg = tr("<html>The data in the GPX layer ''{0}'' has been downloaded from the server.<br>"1364 + "Because its way points do not include a timestamp we cannot correlate them with audio data.</html>",1365 layer.getName()1366 );1367 HelpAwareOptionPane.showOptionDialog(1368 Main.parent,1369 msg,1370 tr("Import not possible"),1371 JOptionPane.WARNING_MESSAGE,1372 ht("/Action/ImportImages#CantImportIntoGpxLayerFromServer")1373 );1374 }1375 1376 public void actionPerformed(ActionEvent e) {1377 if (EGpxLayer.this.data.fromServer) {1378 warnCantImportIntoServerLayer(EGpxLayer.this);1379 return;1380 }1381 String dir = Main.pref.get("markers.lastaudiodirectory");1382 JFileChooser fc = new JFileChooser(dir);1383 fc.setFileSelectionMode(JFileChooser.FILES_ONLY);1384 fc.setAcceptAllFileFilterUsed(false);1385 fc.setFileFilter(new FileFilter() {1386 @Override1387 public boolean accept(File f) {1388 return f.isDirectory() || f.getName().toLowerCase().endsWith(".wav");1389 }1390 1391 @Override1392 public String getDescription() {1393 return tr("Wave Audio files (*.wav)");1394 }1395 });1396 fc.setMultiSelectionEnabled(true);1397 if (fc.showOpenDialog(Main.parent) == JFileChooser.APPROVE_OPTION) {1398 if (!fc.getCurrentDirectory().getAbsolutePath().equals(dir)) {1399 Main.pref.put("markers.lastaudiodirectory", fc.getCurrentDirectory().getAbsolutePath());1400 }1401 1402 File sel[] = fc.getSelectedFiles();1403 // sort files in increasing order of timestamp (this is the end time, but so1404 // long as they don't overlap, that's fine)1405 if (sel.length > 1) {1406 Arrays.sort(sel, new Comparator<File>() {1407 public int compare(File a, File b) {1408 return a.lastModified() <= b.lastModified() ? -1 : 1;1409 }1410 });1411 }1412 1413 String names = null;1414 for (int i = 0; i < sel.length; i++) {1415 if (names == null) {1416 names = " (";1417 } else {1418 names += ", ";1419 }1420 names += sel[i].getName();1421 }1422 if (names != null) {1423 names += ")";1424 } else {1425 names = "";1426 }1427 MarkerLayer ml = new MarkerLayer(new GpxData(), tr("Audio markers from {0}", getName()) + names,1428 getAssociatedFile(), null);1429 double firstStartTime = sel[0].lastModified() / 1000.0 /* ms -> seconds */1430 - AudioUtil.getCalibratedDuration(sel[0]);1431 1432 Markers m = new Markers();1433 for (int i = 0; i < sel.length; i++) {1434 importAudio(sel[i], ml, firstStartTime, m);1435 }1436 Main.main.addLayer(ml);1437 Main.map.repaint();1438 }1439 1440 }1441 }1442 1443 private class ImportImages extends AbstractAction {1444 1445 public ImportImages() {1446 super(tr("Import images"), ImageProvider.get("dialogs/geoimage"));1447 putValue("help", ht("/Action/ImportImages"));1448 }1449 1450 private void warnCantImportIntoServerLayer(EGpxLayer layer) {1451 String msg = tr("<html>The data in the GPX layer ''{0}'' has been downloaded from the server.<br>"1452 + "Because its way points do not include a timestamp we cannot correlate them with images.</html>",1453 layer.getName()1454 );1455 HelpAwareOptionPane.showOptionDialog(1456 Main.parent,1457 msg,1458 tr("Import not possible"),1459 JOptionPane.WARNING_MESSAGE,1460 ht("/Action/ImportImages#CantImportIntoGpxLayerFromServer")1461 );1462 }1463 1464 private void addRecursiveFiles(LinkedList<File> files, File[] sel) {1465 for (File f : sel) {1466 if (f.isDirectory()) {1467 addRecursiveFiles(files, f.listFiles());1468 } else if (f.getName().toLowerCase().endsWith(".jpg")) {1469 files.add(f);1470 }1471 }1472 }1473 1474 public void actionPerformed(ActionEvent e) {1475 1476 if (EGpxLayer.this.data.fromServer) {1477 warnCantImportIntoServerLayer(EGpxLayer.this);1478 return;1479 }1480 String curDir = Main.pref.get("geoimage.lastdirectory", Main.pref.get("lastDirectory"));1481 if (curDir.equals("")) {1482 curDir = ".";1483 }1484 JFileChooser fc = new JFileChooser(new File(curDir));1485 1486 fc.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);1487 fc.setMultiSelectionEnabled(true);1488 fc.setAcceptAllFileFilterUsed(false);1489 JpgImporter importer = new JpgImporter();1490 fc.setFileFilter(importer.filter);1491 fc.showOpenDialog(Main.parent);1492 LinkedList<File> files = new LinkedList<File>();1493 File[] sel = fc.getSelectedFiles();1494 if (sel == null || sel.length == 0)1495 return;1496 if (!fc.getCurrentDirectory().getAbsolutePath().equals(curDir)) {1497 Main.pref.put("geoimage.lastdirectory", fc.getCurrentDirectory().getAbsolutePath());1498 }1499 addRecursiveFiles(files, sel);1500 importer.importDataHandleExceptions(files, NullProgressMonitor.INSTANCE);1501 }1502 1503 }1504 41 } -
applications/editors/josm/plugins/gpxfilter/src/gpxfilter/GpxFilterPlugin.java
r23541 r27760 1 1 package gpxfilter; 2 3 import static org.openstreetmap.josm.gui.help.HelpUtil.ht;4 import static org.openstreetmap.josm.tools.I18n.marktr;5 6 import java.awt.event.KeyEvent;7 8 import javax.swing.JMenu;9 2 10 3 import org.openstreetmap.josm.Main; … … 17 10 public GpxFilterPlugin(PluginInformation info) { 18 11 super(info); 19 JMenu historyMenu = Main.main.menu.addMenu(marktr("GPX"), KeyEvent.VK_R, 20 Main.main.menu.defaultMenuPos,null); 21 //MainMenu.add(historyMenu, new ObjectsHistoryAction()); 22 MainMenu.add(historyMenu, new AddEGpxLayerAction()); 12 MainMenu.add(Main.main.menu.toolsMenu, new AddEGpxLayerAction()); 23 13 } 24 14 -
applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/plugin/preferences/GraphViewPreferences.java
r26481 r27760 1 1 package org.openstreetmap.josm.plugins.graphview.plugin.preferences; 2 3 import static org.openstreetmap.josm.tools.I18n.marktr; 2 4 3 5 import static org.openstreetmap.josm.plugins.graphview.core.property.VehiclePropertyTypes.AXLELOAD; … … 234 236 currentColorScheme = new PreferencesColorScheme(this); 235 237 236 nodeColor = Color.WHITE;237 segmentColor = Color.WHITE;238 arrowheadFillColor = Color.BLACK;239 240 238 separateDirections = false; 241 239 … … 262 260 } 263 261 264 Main.pref.put ("graphview.defaultNodeColor", createColorString(nodeColor));265 Main.pref.put ("graphview.defaultSegmentColor", createColorString(segmentColor));266 Main.pref.put ("graphview.defaultArrowheadCoreColor", createColorString(arrowheadFillColor));262 Main.pref.putColor(marktr("graphview default node"), Color.WHITE); 263 Main.pref.putColor(marktr("graphview default segment"), Color.WHITE); 264 Main.pref.putColor(marktr("graphview arrowhead core"), Color.BLACK); 267 265 268 266 Main.pref.put("graphview.separateDirections", separateDirections); … … 274 272 private void readPreferences() { 275 273 276 if ( Main.pref.hasKey("graphview.parameterBookmarks")) {274 if (!Main.pref.get("graphview.parameterBookmarks").isEmpty()) { 277 275 String bookmarksString = Main.pref.get("graphview.parameterBookmarks"); 278 276 parameterBookmarks = parseAccessParameterBookmarksString(bookmarksString); 279 277 } 280 278 281 if ( Main.pref.hasKey("graphview.activeBookmark")) {279 if (!Main.pref.get("graphview.activeBookmark").isEmpty()) { 282 280 currentParameterBookmarkName = Main.pref.get("graphview.activeBookmark"); 283 281 } … … 289 287 useInternalRulesets = Main.pref.getBoolean("graphview.useInternalRulesets", true); 290 288 291 if ( Main.pref.hasKey("graphview.rulesetFolder")) {289 if (!Main.pref.get("graphview.rulesetFolder").isEmpty()) { 292 290 String dirString = Main.pref.get("graphview.rulesetFolder"); 293 291 rulesetFolder = new File(dirString); 294 292 } 295 if ( Main.pref.hasKey("graphview.rulesetFile")) {293 if (!Main.pref.get("graphview.rulesetFile").isEmpty()) { 296 294 String fileString = Main.pref.get("graphview.rulesetFile"); 297 295 currentRulesetFile = new File(fileString); 298 296 } 299 297 300 if ( Main.pref.hasKey("graphview.rulesetResource")) {298 if (!Main.pref.get("graphview.rulesetResource").isEmpty()) { 301 299 String rulesetString = Main.pref.get("graphview.rulesetResource"); 302 300 //get the enum value for the string … … 310 308 } 311 309 312 if (Main.pref.hasKey("graphview.defaultNodeColor")) { 313 Color color = parseColorString(Main.pref.get("graphview.defaultNodeColor")); 314 if (color != null) { 315 nodeColor = color; 316 } 317 } 318 if (Main.pref.hasKey("graphview.defaultSegmentColor")) { 319 Color color = parseColorString(Main.pref.get("graphview.defaultSegmentColor")); 320 if (color != null) { 321 segmentColor = color; 322 } 323 } 324 if (Main.pref.hasKey("graphview.defaultArrowheadCoreColor")) { 325 Color color = parseColorString(Main.pref.get("graphview.defaultArrowheadCoreColor")); 326 if (color != null) { 327 arrowheadFillColor = color; 328 } 329 } 330 310 nodeColor = Main.pref.getColor(marktr("graphview default node"), Color.WHITE); 311 segmentColor = Main.pref.getColor(marktr("graphview default segment"), Color.WHITE); 312 arrowheadFillColor = Main.pref.getColor(marktr("graphview arrowhead core"), Color.BLACK); 331 313 separateDirections = Main.pref.getBoolean("graphview.separateDirections", false); 332 314 … … 503 485 } 504 486 } 505 506 private static final Pattern COLOR_PATTERN =507 Pattern.compile("^(\\d{1,3}),\\s*(\\d{1,3}),\\s*(\\d{1,3})$");508 509 private String createColorString(Color color) {510 return color.getRed() + ", " + color.getGreen() + ", " + color.getBlue();511 }512 513 private Color parseColorString(String string) {514 Matcher matcher = COLOR_PATTERN.matcher(string);515 if (!matcher.matches()) {516 return null;517 } else {518 int r = Integer.parseInt(matcher.group(1));519 int g = Integer.parseInt(matcher.group(2));520 int b = Integer.parseInt(matcher.group(3));521 return new Color(r, g, b);522 }523 }524 525 487 } -
applications/editors/josm/plugins/print/src/org/openstreetmap/josm/plugins/print/PrintPlugin.java
r27282 r27760 125 125 */ 126 126 public static void adjustPrefs() { 127 if (! 127 if (!Main.pref.getBoolean("print.saved-prefs", false)) { 128 128 Main.pref.put("print.saved-prefs", true); 129 129 adjustPref("draw.data.downloaded_area", false); … … 145 145 */ 146 146 protected static void adjustPref(String key, int value) { 147 if ( Main.pref.hasKey(key)) {147 if (!Main.pref.get(key).isEmpty()) { 148 148 Main.pref.put("print.saved-prefs."+key, Main.pref.get(key)); 149 149 } … … 160 160 */ 161 161 protected static void adjustPref(String key, boolean value) { 162 if ( Main.pref.hasKey(key)) {162 if (!Main.pref.get(key).isEmpty()) { 163 163 Main.pref.put("print.saved-prefs."+key, Main.pref.get(key)); 164 164 } … … 175 175 */ 176 176 protected static void adjustPref(String key, String value) { 177 if ( Main.pref.hasKey(key)) {177 if (!Main.pref.get(key).isEmpty()) { 178 178 Main.pref.put("print.saved-prefs."+key, Main.pref.get(key)); 179 179 } -
applications/editors/josm/plugins/routing/src/com/innovant/josm/plugin/routing/RoutingLayer.java
r26119 r27760 27 27 28 28 package com.innovant.josm.plugin.routing; 29 30 import static org.openstreetmap.josm.tools.I18n.marktr; 29 31 30 32 import java.awt.BasicStroke; … … 59 61 import org.openstreetmap.josm.tools.ImageProvider; 60 62 61 import com.innovant.josm.jrt.core.PreferencesKeys;62 63 import com.innovant.josm.jrt.osm.OsmEdge; 63 64 … … 69 70 */ 70 71 public class RoutingLayer extends Layer { 72 73 public enum PreferencesKeys { 74 KEY_ACTIVE_ROUTE_COLOR (marktr("routing active route")), 75 KEY_INACTIVE_ROUTE_COLOR (marktr("routing inactive route")), 76 KEY_ROUTE_WIDTH ("routing.route.width"), 77 KEY_ROUTE_SELECT ("routing.route.select"); 78 79 public final String key; 80 PreferencesKeys (String key) { 81 this.key=key; 82 } 83 84 public String getKey() {return key;}; 85 } 71 86 72 87 /** … … 240 255 // Get path stroke color from preferences 241 256 // Color is different for active and inactive layers 242 String colorString;257 Color color; 243 258 if (isActiveLayer) { 244 if (Main.pref.hasKey(PreferencesKeys.KEY_ACTIVE_ROUTE_COLOR.key)) 245 colorString = Main.pref.get(PreferencesKeys.KEY_ACTIVE_ROUTE_COLOR.key); 246 else { 247 colorString = ColorHelper.color2html(Color.RED); 248 Main.pref.put(PreferencesKeys.KEY_ACTIVE_ROUTE_COLOR.key, colorString); 249 } 259 color = Main.pref.getColor(PreferencesKeys.KEY_ACTIVE_ROUTE_COLOR.key, Color.RED); 250 260 } else { 251 if (Main.pref.hasKey(PreferencesKeys.KEY_INACTIVE_ROUTE_COLOR.key)) 252 colorString = Main.pref.get(PreferencesKeys.KEY_INACTIVE_ROUTE_COLOR.key); 253 else { 254 colorString = ColorHelper.color2html(Color.decode("#dd2222")); 255 Main.pref.put(PreferencesKeys.KEY_INACTIVE_ROUTE_COLOR.key, colorString); 256 } 257 } 258 Color color = ColorHelper.html2color(colorString); 261 color = Main.pref.getColor(PreferencesKeys.KEY_INACTIVE_ROUTE_COLOR.key, Color.decode("#dd2222")); 262 } 259 263 260 264 // Get path stroke width from preferences
Note:
See TracChangeset
for help on using the changeset viewer.