Changeset 5715 in josm
- Timestamp:
- 2013-02-13T19:12:38+01:00 (12 years ago)
- Location:
- trunk/src/org/openstreetmap/josm
- Files:
-
- 9 added
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/data/gpx/GpxData.java
r5681 r5715 7 7 import java.util.LinkedList; 8 8 import java.util.Map; 9 import org.openstreetmap.josm.Main; 9 10 10 11 import org.openstreetmap.josm.data.Bounds; 12 import org.openstreetmap.josm.data.coor.EastNorth; 11 13 12 14 /** … … 122 124 return result; 123 125 } 126 127 /** 128 * Makes a WayPoint at the projection of point P onto the track providing P is less than 129 * tolerance away from the track 130 * 131 * @param P : the point to determine the projection for 132 * @param tolerance : must be no further than this from the track 133 * @return the closest point on the track to P, which may be the first or last point if off the 134 * end of a segment, or may be null if nothing close enough 135 */ 136 public WayPoint nearestPointOnTrack(EastNorth P, double tolerance) { 137 /* 138 * assume the coordinates of P are xp,yp, and those of a section of track between two 139 * trackpoints are R=xr,yr and S=xs,ys. Let N be the projected point. 140 * 141 * The equation of RS is Ax + By + C = 0 where A = ys - yr B = xr - xs C = - Axr - Byr 142 * 143 * Also, note that the distance RS^2 is A^2 + B^2 144 * 145 * If RS^2 == 0.0 ignore the degenerate section of track 146 * 147 * PN^2 = (Axp + Byp + C)^2 / RS^2 that is the distance from P to the line 148 * 149 * so if PN^2 is less than PNmin^2 (initialized to tolerance) we can reject the line; 150 * otherwise... determine if the projected poijnt lies within the bounds of the line: PR^2 - 151 * PN^2 <= RS^2 and PS^2 - PN^2 <= RS^2 152 * 153 * where PR^2 = (xp - xr)^2 + (yp-yr)^2 and PS^2 = (xp - xs)^2 + (yp-ys)^2 154 * 155 * If so, calculate N as xn = xr + (RN/RS) B yn = y1 + (RN/RS) A 156 * 157 * where RN = sqrt(PR^2 - PN^2) 158 */ 159 160 double PNminsq = tolerance * tolerance; 161 EastNorth bestEN = null; 162 double bestTime = 0.0; 163 double px = P.east(); 164 double py = P.north(); 165 double rx = 0.0, ry = 0.0, sx, sy, x, y; 166 if (tracks == null) 167 return null; 168 for (GpxTrack track : tracks) { 169 for (GpxTrackSegment seg : track.getSegments()) { 170 WayPoint R = null; 171 for (WayPoint S : seg.getWayPoints()) { 172 EastNorth c = S.getEastNorth(); 173 if (R == null) { 174 R = S; 175 rx = c.east(); 176 ry = c.north(); 177 x = px - rx; 178 y = py - ry; 179 double PRsq = x * x + y * y; 180 if (PRsq < PNminsq) { 181 PNminsq = PRsq; 182 bestEN = c; 183 bestTime = R.time; 184 } 185 } else { 186 sx = c.east(); 187 sy = c.north(); 188 double A = sy - ry; 189 double B = rx - sx; 190 double C = -A * rx - B * ry; 191 double RSsq = A * A + B * B; 192 if (RSsq == 0.0) { 193 continue; 194 } 195 double PNsq = A * px + B * py + C; 196 PNsq = PNsq * PNsq / RSsq; 197 if (PNsq < PNminsq) { 198 x = px - rx; 199 y = py - ry; 200 double PRsq = x * x + y * y; 201 x = px - sx; 202 y = py - sy; 203 double PSsq = x * x + y * y; 204 if (PRsq - PNsq <= RSsq && PSsq - PNsq <= RSsq) { 205 double RNoverRS = Math.sqrt((PRsq - PNsq) / RSsq); 206 double nx = rx - RNoverRS * B; 207 double ny = ry + RNoverRS * A; 208 bestEN = new EastNorth(nx, ny); 209 bestTime = R.time + RNoverRS * (S.time - R.time); 210 PNminsq = PNsq; 211 } 212 } 213 R = S; 214 rx = sx; 215 ry = sy; 216 } 217 } 218 if (R != null) { 219 EastNorth c = R.getEastNorth(); 220 /* if there is only one point in the seg, it will do this twice, but no matter */ 221 rx = c.east(); 222 ry = c.north(); 223 x = px - rx; 224 y = py - ry; 225 double PRsq = x * x + y * y; 226 if (PRsq < PNminsq) { 227 PNminsq = PRsq; 228 bestEN = c; 229 bestTime = R.time; 230 } 231 } 232 } 233 } 234 if (bestEN == null) 235 return null; 236 WayPoint best = new WayPoint(Main.getProjection().eastNorth2latlon(bestEN)); 237 best.time = bestTime; 238 return best; 239 } 124 240 } -
trunk/src/org/openstreetmap/josm/gui/layer/GpxLayer.java
r5681 r5715 3 3 package org.openstreetmap.josm.gui.layer; 4 4 5 import static org.openstreetmap.josm.gui.help.HelpUtil.ht;6 5 import static org.openstreetmap.josm.tools.I18n.marktr; 7 6 import static org.openstreetmap.josm.tools.I18n.tr; … … 10 9 import java.awt.BasicStroke; 11 10 import java.awt.Color; 12 import java.awt.Component;13 11 import java.awt.Dimension; 14 12 import java.awt.Graphics2D; 15 import java.awt.GridBagLayout;16 13 import java.awt.Point; 17 14 import java.awt.RenderingHints; 18 15 import java.awt.Stroke; 19 import java.awt.Toolkit;20 import java.awt.event.ActionEvent;21 import java.awt.event.MouseAdapter;22 import java.awt.event.MouseEvent;23 import java.awt.event.MouseListener;24 import java.awt.geom.Area;25 import java.awt.geom.Rectangle2D;26 16 import java.io.File; 27 import java.io.IOException;28 import java.net.MalformedURLException;29 import java.net.URL;30 17 import java.text.DateFormat; 31 18 import java.util.ArrayList; 32 import java.util.Arrays;33 import java.util.Collection;34 import java.util.Collections;35 import java.util.Comparator;36 19 import java.util.LinkedList; 37 20 import java.util.List; 38 import java.util.Map; 39 import java.util.concurrent.Future; 40 41 import javax.swing.AbstractAction; 21 42 22 import javax.swing.Action; 43 import javax.swing.BorderFactory;44 23 import javax.swing.Icon; 45 import javax.swing.JComponent;46 import javax.swing.JFileChooser;47 import javax.swing.JLabel;48 import javax.swing.JList;49 import javax.swing.JMenuItem;50 import javax.swing.JOptionPane;51 import javax.swing.JPanel;52 24 import javax.swing.JScrollPane; 53 import javax.swing.JTable;54 import javax.swing.ListSelectionModel;55 25 import javax.swing.SwingUtilities; 56 import javax.swing.event.ListSelectionEvent;57 import javax.swing.event.ListSelectionListener;58 import javax.swing.filechooser.FileFilter;59 import javax.swing.table.TableCellRenderer;60 26 61 27 import org.openstreetmap.josm.Main; 62 import org.openstreetmap.josm.actions.AbstractMergeAction.LayerListCellRenderer;63 import org.openstreetmap.josm.actions.DiskAccessAction;64 28 import org.openstreetmap.josm.actions.RenameLayerAction; 65 29 import org.openstreetmap.josm.actions.SaveActionBase; 66 import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmTaskList;67 30 import org.openstreetmap.josm.data.Bounds; 68 import org.openstreetmap.josm.data.coor.EastNorth;69 31 import org.openstreetmap.josm.data.coor.LatLon; 70 32 import org.openstreetmap.josm.data.gpx.GpxConstants; … … 74 36 import org.openstreetmap.josm.data.gpx.GpxTrackSegment; 75 37 import org.openstreetmap.josm.data.gpx.WayPoint; 76 import org.openstreetmap.josm.data.osm.DataSet;77 import org.openstreetmap.josm.data.osm.Node;78 import org.openstreetmap.josm.data.osm.Way;79 38 import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor; 80 39 import org.openstreetmap.josm.data.projection.Projection; 81 import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil;82 import org.openstreetmap.josm.gui.ExtendedDialog;83 import org.openstreetmap.josm.gui.HelpAwareOptionPane;84 40 import org.openstreetmap.josm.gui.MapView; 85 41 import org.openstreetmap.josm.gui.NavigatableComponent; 86 import org.openstreetmap.josm.gui.PleaseWaitRunnable;87 42 import org.openstreetmap.josm.gui.dialogs.LayerListDialog; 88 43 import org.openstreetmap.josm.gui.dialogs.LayerListPopup; 89 import org.openstreetmap.josm.gui.layer.WMSLayer.PrecacheTask; 90 import org.openstreetmap.josm.gui.layer.markerlayer.AudioMarker; 91 import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer; 92 import org.openstreetmap.josm.gui.preferences.display.GPXSettingsPanel; 93 import org.openstreetmap.josm.gui.progress.NullProgressMonitor; 94 import org.openstreetmap.josm.gui.progress.PleaseWaitProgressMonitor; 95 import org.openstreetmap.josm.gui.progress.ProgressMonitor; 96 import org.openstreetmap.josm.gui.progress.ProgressTaskId; 97 import org.openstreetmap.josm.gui.progress.ProgressTaskIds; 44 import org.openstreetmap.josm.gui.layer.gpx.ChooseTrackVisibilityAction; 45 import org.openstreetmap.josm.gui.layer.gpx.ConvertToDataLayerAction; 46 import org.openstreetmap.josm.gui.layer.gpx.CustomizeDrawingAction; 47 import org.openstreetmap.josm.gui.layer.gpx.DownloadAlongTrackAction; 48 import org.openstreetmap.josm.gui.layer.gpx.DownloadWmsAlongTrackAction; 49 import org.openstreetmap.josm.gui.layer.gpx.ImportAudioAction; 50 import org.openstreetmap.josm.gui.layer.gpx.ImportImagesAction; 51 import org.openstreetmap.josm.gui.layer.gpx.MarkersFromNamedPointsAction; 98 52 import org.openstreetmap.josm.gui.widgets.HtmlPanel; 99 import org.openstreetmap.josm.gui.widgets.JFileChooserManager;100 import org.openstreetmap.josm.gui.widgets.JosmComboBox;101 53 import org.openstreetmap.josm.io.GpxImporter; 102 import org.openstreetmap.josm.io.JpgImporter;103 import org.openstreetmap.josm.io.OsmTransferException;104 import org.openstreetmap.josm.tools.AudioUtil;105 import org.openstreetmap.josm.tools.DateUtils;106 import org.openstreetmap.josm.tools.GBC;107 54 import org.openstreetmap.josm.tools.ImageProvider; 108 import org.openstreetmap.josm.tools.OpenBrowser;109 import org.openstreetmap.josm.tools.UrlLabel;110 55 import org.openstreetmap.josm.tools.Utils; 111 import org.openstreetmap.josm.tools.WindowGeometry;112 import org.xml.sax.SAXException;113 56 114 57 public class GpxLayer extends Layer { 115 116 private static final String PREF_DOWNLOAD_ALONG_TRACK_DISTANCE = "gpxLayer.downloadAlongTrack.distance";117 private static final String PREF_DOWNLOAD_ALONG_TRACK_AREA = "gpxLayer.downloadAlongTrack.area";118 private static final String PREF_DOWNLOAD_ALONG_TRACK_NEAR = "gpxLayer.downloadAlongTrack.near";119 58 120 59 public GpxData data; … … 128 67 private boolean isLocalFile; 129 68 // used by ChooseTrackVisibilityAction to determine which tracks to show/hide 130 p rivateboolean[] trackVisibility = new boolean[0];69 public boolean[] trackVisibility = new boolean[0]; 131 70 132 71 private final List<GpxTrack> lastTracks = new ArrayList<GpxTrack>(); // List of tracks at last paint 133 72 private int lastUpdateCount; 134 135 private static class Markers {136 public boolean timedMarkersOmitted = false;137 public boolean untimedMarkersOmitted = false;138 }139 73 140 74 public GpxLayer(GpxData d) { … … 159 93 * returns a human readable string that shows the timespan of the given track 160 94 */ 161 p rivatestatic String getTimespanForTrack(GpxTrack trk) {95 public static String getTimespanForTrack(GpxTrack trk) { 162 96 WayPoint earliest = null, latest = null; 163 97 … … 286 220 @Override 287 221 public Action[] getMenuEntries() { 288 if (Main.applet) 222 if (Main.applet) { 289 223 return new Action[] { 290 224 LayerListDialog.getInstance().createShowHideLayerAction(), … … 292 226 SeparatorLayerAction.INSTANCE, 293 227 new CustomizeColor(this), 294 new CustomizeDrawing(this), 295 new ConvertToDataLayerAction(), 228 new CustomizeDrawingAction(this), 229 new ConvertToDataLayerAction(this), 296 230 SeparatorLayerAction.INSTANCE, 297 new ChooseTrackVisibilityAction(), 231 new ChooseTrackVisibilityAction(this), 298 232 new RenameLayerAction(getAssociatedFile(), this), 299 233 SeparatorLayerAction.INSTANCE, 300 234 new LayerListPopup.InfoAction(this) }; 235 } 301 236 return new Action[] { 302 237 LayerListDialog.getInstance().createShowHideLayerAction(), … … 306 241 new LayerSaveAsAction(this), 307 242 new CustomizeColor(this), 308 new CustomizeDrawing(this), 309 new ImportImages (),310 new ImportAudio (),311 new MarkersFromNamedPoin s(),312 new ConvertToDataLayerAction(), 313 new DownloadAlongTrackAction(), 314 new DownloadWmsAlongTrackAction(), 243 new CustomizeDrawingAction(this), 244 new ImportImagesAction(this), 245 new ImportAudioAction(this), 246 new MarkersFromNamedPointsAction(this), 247 new ConvertToDataLayerAction(this), 248 new DownloadAlongTrackAction(data), 249 new DownloadWmsAlongTrackAction(data), 315 250 SeparatorLayerAction.INSTANCE, 316 new ChooseTrackVisibilityAction(), 251 new ChooseTrackVisibilityAction(this), 317 252 new RenameLayerAction(getAssociatedFile(), this), 318 253 SeparatorLayerAction.INSTANCE, 319 254 new LayerListPopup.InfoAction(this) }; 255 } 256 257 public boolean isLocalFile() { 258 return isLocalFile; 320 259 } 321 260 … … 829 768 } 830 769 831 public class ConvertToDataLayerAction extends AbstractAction {832 public ConvertToDataLayerAction() {833 super(tr("Convert to data layer"), ImageProvider.get("converttoosm"));834 putValue("help", ht("/Action/ConvertToDataLayer"));835 }836 837 @Override838 public void actionPerformed(ActionEvent e) {839 JPanel msg = new JPanel(new GridBagLayout());840 msg841 .add(842 new JLabel(843 tr("<html>Upload of unprocessed GPS data as map data is considered harmful.<br>If you want to upload traces, look here:</html>")),844 GBC.eol());845 msg.add(new UrlLabel(tr("http://www.openstreetmap.org/traces"),2), GBC.eop());846 if (!ConditionalOptionPaneUtil.showConfirmationDialog("convert_to_data", Main.parent, msg, tr("Warning"),847 JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE, JOptionPane.OK_OPTION))848 return;849 DataSet ds = new DataSet();850 for (GpxTrack trk : data.tracks) {851 for (GpxTrackSegment segment : trk.getSegments()) {852 List<Node> nodes = new ArrayList<Node>();853 for (WayPoint p : segment.getWayPoints()) {854 Node n = new Node(p.getCoor());855 String timestr = p.getString("time");856 if (timestr != null) {857 n.setTimestamp(DateUtils.fromString(timestr));858 }859 ds.addPrimitive(n);860 nodes.add(n);861 }862 Way w = new Way();863 w.setNodes(nodes);864 ds.addPrimitive(w);865 }866 }867 Main.main868 .addLayer(new OsmDataLayer(ds, tr("Converted from: {0}", GpxLayer.this.getName()), getAssociatedFile()));869 Main.main.removeLayer(GpxLayer.this);870 }871 }872 873 770 @Override 874 771 public File getAssociatedFile() { … … 899 796 } 900 797 901 /**902 * allows the user to choose which of the downloaded tracks should be displayed.903 * they can be chosen from the gpx layer context menu.904 */905 public class ChooseTrackVisibilityAction extends AbstractAction {906 public ChooseTrackVisibilityAction() {907 super(tr("Choose visible tracks"), ImageProvider.get("dialogs/filter"));908 putValue("help", ht("/Action/ChooseTrackVisibility"));909 }910 911 /**912 * gathers all available data for the tracks and returns them as array of arrays913 * in the expected column order */914 private Object[][] buildTableContents() {915 Object[][] tracks = new Object[data.tracks.size()][5];916 int i = 0;917 for (GpxTrack trk : data.tracks) {918 Map<String, Object> attr = trk.getAttributes();919 String name = (String) (attr.containsKey("name") ? attr.get("name") : "");920 String desc = (String) (attr.containsKey("desc") ? attr.get("desc") : "");921 String time = getTimespanForTrack(trk);922 String length = NavigatableComponent.getSystemOfMeasurement().getDistText(trk.length());923 String url = (String) (attr.containsKey("url") ? attr.get("url") : "");924 tracks[i] = new String[] {name, desc, time, length, url};925 i++;926 }927 return tracks;928 }929 930 /**931 * Builds an non-editable table whose 5th column will open a browser when double clicked.932 * The table will fill its parent. */933 private JTable buildTable(String[] headers, Object[][] content) {934 final JTable t = new JTable(content, headers) {935 @Override936 public Component prepareRenderer(TableCellRenderer renderer, int row, int col) {937 Component c = super.prepareRenderer(renderer, row, col);938 if (c instanceof JComponent) {939 JComponent jc = (JComponent)c;940 jc.setToolTipText((String)getValueAt(row, col));941 }942 return c;943 }944 945 @Override946 public boolean isCellEditable(int rowIndex, int colIndex) {947 return false;948 }949 };950 // default column widths951 t.getColumnModel().getColumn(0).setPreferredWidth(220);952 t.getColumnModel().getColumn(1).setPreferredWidth(300);953 t.getColumnModel().getColumn(2).setPreferredWidth(200);954 t.getColumnModel().getColumn(3).setPreferredWidth(50);955 t.getColumnModel().getColumn(4).setPreferredWidth(100);956 // make the link clickable957 final MouseListener urlOpener = new MouseAdapter() {958 @Override959 public void mouseClicked(MouseEvent e) {960 if (e.getClickCount() != 2)961 return;962 JTable t = (JTable)e.getSource();963 int col = t.convertColumnIndexToModel(t.columnAtPoint(e.getPoint()));964 if(col != 4) // only accept clicks on the URL column965 return;966 int row = t.rowAtPoint(e.getPoint());967 String url = (String) t.getValueAt(row, col);968 if (url == null || url.isEmpty())969 return;970 OpenBrowser.displayUrl(url);971 }972 };973 t.addMouseListener(urlOpener);974 t.setFillsViewportHeight(true);975 return t;976 }977 978 /** selects all rows (=tracks) in the table that are currently visible */979 private void selectVisibleTracksInTable(JTable table) {980 // don't select any tracks if the layer is not visible981 if(!isVisible())982 return;983 ListSelectionModel s = table.getSelectionModel();984 s.clearSelection();985 for(int i=0; i < trackVisibility.length; i++)986 if(trackVisibility[i]) {987 s.addSelectionInterval(i, i);988 }989 }990 991 /** listens to selection changes in the table and redraws the map */992 private void listenToSelectionChanges(JTable table) {993 table.getSelectionModel().addListSelectionListener(new ListSelectionListener(){994 public void valueChanged(ListSelectionEvent e) {995 if(!(e.getSource() instanceof ListSelectionModel))996 return;997 998 ListSelectionModel s = (ListSelectionModel) e.getSource();999 for(int i = 0; i < data.tracks.size(); i++) {1000 trackVisibility[i] = s.isSelectedIndex(i);1001 }1002 Main.map.mapView.preferenceChanged(null);1003 Main.map.repaint(100);1004 }1005 });1006 }1007 1008 @Override1009 public void actionPerformed(ActionEvent arg0) {1010 final JPanel msg = new JPanel(new GridBagLayout());1011 msg.add(new JLabel(tr("<html>Select all tracks that you want to be displayed. You can drag select a "1012 + "range of tracks or use CTRL+Click to select specific ones. The map is updated live in the "1013 + "background. Open the URLs by double clicking them.</html>")),1014 GBC.eol().fill(GBC.HORIZONTAL));1015 1016 // build table1017 final boolean[] trackVisibilityBackup = trackVisibility.clone();1018 final String[] headers = {tr("Name"), tr("Description"), tr("Timespan"), tr("Length"), tr("URL")};1019 final JTable table = buildTable(headers, buildTableContents());1020 selectVisibleTracksInTable(table);1021 listenToSelectionChanges(table);1022 1023 // make the table scrollable1024 JScrollPane scrollPane = new JScrollPane(table);1025 msg.add(scrollPane, GBC.eol().fill(GBC.BOTH));1026 1027 // build dialog1028 ExtendedDialog ed = new ExtendedDialog(1029 Main.parent, tr("Set track visibility for {0}", getName()),1030 new String[] {tr("Show all"), tr("Show selected only"), tr("Cancel")});1031 ed.setButtonIcons(new String[] {"dialogs/layerlist/eye", "dialogs/filter", "cancel"});1032 ed.setContent(msg, false);1033 ed.setDefaultButton(2);1034 ed.setCancelButton(3);1035 ed.configureContextsensitiveHelp("/Action/ChooseTrackVisibility", true);1036 ed.setRememberWindowGeometry(1037 getClass().getName() + ".geometry",1038 WindowGeometry.centerInWindow(Main.parent, new Dimension(1000, 500))1039 );1040 ed.showDialog();1041 int v = ed.getValue();1042 // cancel for unknown buttons and copy back original settings1043 if(v != 1 && v != 2) {1044 for(int i = 0; i < data.tracks.size(); i++) {1045 trackVisibility[i] = trackVisibilityBackup[i];1046 }1047 Main.map.repaint();1048 return;1049 }1050 1051 // set visibility (1 = show all, 2 = filter). If no tracks are selected1052 // set all of them visible and...1053 ListSelectionModel s = table.getSelectionModel();1054 final boolean all = v == 1 || s.isSelectionEmpty();1055 for(int i = 0; i < data.tracks.size(); i++) {1056 trackVisibility[i] = all || s.isSelectedIndex(i);1057 }1058 // ...sync with layer visibility instead to avoid having two ways to hide everything1059 setVisible(v == 1 || !s.isSelectionEmpty());1060 Main.map.repaint();1061 }1062 }1063 1064 /**1065 * Action that issues a series of download requests to the API, following the GPX track.1066 *1067 * @author fred1068 */1069 public class DownloadAlongTrackAction extends AbstractAction {1070 final static int NEAR_TRACK=0;1071 final static int NEAR_WAYPOINTS=1;1072 final static int NEAR_BOTH=2;1073 final Integer dist[] = { 5000, 500, 50 };1074 final Integer area[] = { 20, 10, 5, 1 };1075 1076 public DownloadAlongTrackAction() {1077 super(tr("Download from OSM along this track"), ImageProvider.get("downloadalongtrack"));1078 }1079 1080 @Override1081 public void actionPerformed(ActionEvent e) {1082 /*1083 * build selection dialog1084 */1085 JPanel msg = new JPanel(new GridBagLayout());1086 1087 msg.add(new JLabel(tr("Download everything within:")), GBC.eol());1088 String s[] = new String[dist.length];1089 for (int i = 0; i < dist.length; ++i) {1090 s[i] = tr("{0} meters", dist[i]);1091 }1092 JList buffer = new JList(s);1093 buffer.setSelectedIndex(Main.pref.getInteger(PREF_DOWNLOAD_ALONG_TRACK_DISTANCE, 0));1094 msg.add(buffer, GBC.eol());1095 1096 msg.add(new JLabel(tr("Maximum area per request:")), GBC.eol());1097 s = new String[area.length];1098 for (int i = 0; i < area.length; ++i) {1099 s[i] = tr("{0} sq km", area[i]);1100 }1101 JList maxRect = new JList(s);1102 maxRect.setSelectedIndex(Main.pref.getInteger(PREF_DOWNLOAD_ALONG_TRACK_AREA, 0));1103 msg.add(maxRect, GBC.eol());1104 1105 msg.add(new JLabel(tr("Download near:")), GBC.eol());1106 JList downloadNear = new JList(new String[] { tr("track only"), tr("waypoints only"), tr("track and waypoints") });1107 1108 downloadNear.setSelectedIndex(Main.pref.getInteger(PREF_DOWNLOAD_ALONG_TRACK_NEAR, 0));1109 msg.add(downloadNear, GBC.eol());1110 1111 int ret = JOptionPane.showConfirmDialog(1112 Main.parent,1113 msg,1114 tr("Download from OSM along this track"),1115 JOptionPane.OK_CANCEL_OPTION,1116 JOptionPane.QUESTION_MESSAGE1117 );1118 switch(ret) {1119 case JOptionPane.CANCEL_OPTION:1120 case JOptionPane.CLOSED_OPTION:1121 return;1122 default:1123 // continue1124 }1125 1126 Main.pref.putInteger(PREF_DOWNLOAD_ALONG_TRACK_DISTANCE, buffer.getSelectedIndex());1127 Main.pref.putInteger(PREF_DOWNLOAD_ALONG_TRACK_AREA, maxRect.getSelectedIndex());1128 final int near = downloadNear.getSelectedIndex();1129 Main.pref.putInteger(PREF_DOWNLOAD_ALONG_TRACK_NEAR, near);1130 1131 /*1132 * Find the average latitude for the data we're contemplating, so we can know how many1133 * metres per degree of longitude we have.1134 */1135 double latsum = 0;1136 int latcnt = 0;1137 1138 if (near == NEAR_TRACK || near == NEAR_BOTH) {1139 for (GpxTrack trk : data.tracks) {1140 for (GpxTrackSegment segment : trk.getSegments()) {1141 for (WayPoint p : segment.getWayPoints()) {1142 latsum += p.getCoor().lat();1143 latcnt++;1144 }1145 }1146 }1147 }1148 1149 if (near == NEAR_WAYPOINTS || near == NEAR_BOTH) {1150 for (WayPoint p : data.waypoints) {1151 latsum += p.getCoor().lat();1152 latcnt++;1153 }1154 }1155 1156 double avglat = latsum / latcnt;1157 double scale = Math.cos(Math.toRadians(avglat));1158 1159 /*1160 * Compute buffer zone extents and maximum bounding box size. Note that the maximum we1161 * ever offer is a bbox area of 0.002, while the API theoretically supports 0.25, but as1162 * soon as you touch any built-up area, that kind of bounding box will download forever1163 * and then stop because it has more than 50k nodes.1164 */1165 Integer i = buffer.getSelectedIndex();1166 final int buffer_dist = dist[i < 0 ? 0 : i];1167 i = maxRect.getSelectedIndex();1168 final double max_area = area[i < 0 ? 0 : i] / 10000.0 / scale;1169 final double buffer_y = buffer_dist / 100000.0;1170 final double buffer_x = buffer_y / scale;1171 1172 final int totalTicks = latcnt;1173 // guess if a progress bar might be useful.1174 final boolean displayProgress = totalTicks > 2000 && buffer_y < 0.01;1175 1176 class CalculateDownloadArea extends PleaseWaitRunnable {1177 private Area a = new Area();1178 private boolean cancel = false;1179 private int ticks = 0;1180 private Rectangle2D r = new Rectangle2D.Double();1181 1182 public CalculateDownloadArea() {1183 super(tr("Calculating Download Area"),1184 (displayProgress ? null : NullProgressMonitor.INSTANCE),1185 false);1186 }1187 1188 @Override1189 protected void cancel() {1190 cancel = true;1191 }1192 1193 @Override1194 protected void finish() {1195 }1196 1197 @Override1198 protected void afterFinish() {1199 if(cancel)1200 return;1201 confirmAndDownloadAreas(a, max_area, progressMonitor);1202 }1203 1204 /**1205 * increase tick count by one, report progress every 100 ticks1206 */1207 private void tick() {1208 ticks++;1209 if(ticks % 100 == 0) {1210 progressMonitor.worked(100);1211 }1212 }1213 1214 /**1215 * calculate area for single, given way point and return new LatLon if the1216 * way point has been used to modify the area.1217 */1218 private LatLon calcAreaForWayPoint(WayPoint p, LatLon previous) {1219 tick();1220 LatLon c = p.getCoor();1221 if (previous == null || c.greatCircleDistance(previous) > buffer_dist) {1222 // we add a buffer around the point.1223 r.setRect(c.lon() - buffer_x, c.lat() - buffer_y, 2 * buffer_x, 2 * buffer_y);1224 a.add(new Area(r));1225 return c;1226 }1227 return previous;1228 }1229 1230 @Override1231 protected void realRun() {1232 progressMonitor.setTicksCount(totalTicks);1233 /*1234 * Collect the combined area of all gpx points plus buffer zones around them. We ignore1235 * points that lie closer to the previous point than the given buffer size because1236 * otherwise this operation takes ages.1237 */1238 LatLon previous = null;1239 if (near == NEAR_TRACK || near == NEAR_BOTH) {1240 for (GpxTrack trk : data.tracks) {1241 for (GpxTrackSegment segment : trk.getSegments()) {1242 for (WayPoint p : segment.getWayPoints()) {1243 if(cancel)1244 return;1245 previous = calcAreaForWayPoint(p, previous);1246 }1247 }1248 }1249 }1250 if (near == NEAR_WAYPOINTS || near == NEAR_BOTH) {1251 for (WayPoint p : data.waypoints) {1252 if(cancel)1253 return;1254 previous = calcAreaForWayPoint(p, previous);1255 }1256 }1257 }1258 }1259 1260 Main.worker.submit(new CalculateDownloadArea());1261 }1262 1263 1264 /**1265 * Area "a" contains the hull that we would like to download data for. however we1266 * can only download rectangles, so the following is an attempt at finding a number of1267 * rectangles to download.1268 *1269 * The idea is simply: Start out with the full bounding box. If it is too large, then1270 * split it in half and repeat recursively for each half until you arrive at something1271 * small enough to download. The algorithm is improved by always using the intersection1272 * between the rectangle and the actual desired area. For example, if you have a track1273 * that goes like this: +----+ | /| | / | | / | |/ | +----+ then we would first look at1274 * downloading the whole rectangle (assume it's too big), after that we split it in half1275 * (upper and lower half), but we donot request the full upper and lower rectangle, only1276 * the part of the upper/lower rectangle that actually has something in it.1277 *1278 * This functions calculates the rectangles, asks the user to continue and downloads1279 * the areas if applicable.1280 */1281 private void confirmAndDownloadAreas(Area a, double max_area, ProgressMonitor progressMonitor) {1282 List<Rectangle2D> toDownload = new ArrayList<Rectangle2D>();1283 1284 addToDownload(a, a.getBounds(), toDownload, max_area);1285 1286 if(toDownload.size() == 0)1287 return;1288 1289 JPanel msg = new JPanel(new GridBagLayout());1290 1291 msg.add(new JLabel(1292 tr("<html>This action will require {0} individual<br>"1293 + "download requests. Do you wish<br>to continue?</html>",1294 toDownload.size())), GBC.eol());1295 1296 if (toDownload.size() > 1) {1297 int ret = JOptionPane.showConfirmDialog(1298 Main.parent,1299 msg,1300 tr("Download from OSM along this track"),1301 JOptionPane.OK_CANCEL_OPTION,1302 JOptionPane.PLAIN_MESSAGE1303 );1304 switch(ret) {1305 case JOptionPane.CANCEL_OPTION:1306 case JOptionPane.CLOSED_OPTION:1307 return;1308 default:1309 // continue1310 }1311 }1312 final PleaseWaitProgressMonitor monitor = new PleaseWaitProgressMonitor(tr("Download data"));1313 final Future<?> future = new DownloadOsmTaskList().download(false, toDownload, monitor);1314 Main.worker.submit(1315 new Runnable() {1316 @Override1317 public void run() {1318 try {1319 future.get();1320 } catch(Exception e) {1321 e.printStackTrace();1322 return;1323 }1324 monitor.close();1325 }1326 }1327 );1328 }1329 }1330 1331 1332 public class DownloadWmsAlongTrackAction extends AbstractAction {1333 public DownloadWmsAlongTrackAction() {1334 super(tr("Precache imagery tiles along this track"), ImageProvider.get("downloadalongtrack"));1335 }1336 1337 public void actionPerformed(ActionEvent e) {1338 1339 final List<LatLon> points = new ArrayList<LatLon>();1340 1341 for (GpxTrack trk : data.tracks) {1342 for (GpxTrackSegment segment : trk.getSegments()) {1343 for (WayPoint p : segment.getWayPoints()) {1344 points.add(p.getCoor());1345 }1346 }1347 }1348 for (WayPoint p : data.waypoints) {1349 points.add(p.getCoor());1350 }1351 1352 1353 final WMSLayer layer = askWMSLayer();1354 if (layer != null) {1355 PleaseWaitRunnable task = new PleaseWaitRunnable(tr("Precaching WMS")) {1356 1357 private PrecacheTask precacheTask;1358 1359 @Override1360 protected void realRun() throws SAXException, IOException, OsmTransferException {1361 precacheTask = new PrecacheTask(progressMonitor);1362 layer.downloadAreaToCache(precacheTask, points, 0, 0);1363 while (!precacheTask.isFinished() && !progressMonitor.isCanceled()) {1364 synchronized (this) {1365 try {1366 wait(200);1367 } catch (InterruptedException e) {1368 e.printStackTrace();1369 }1370 }1371 }1372 }1373 1374 @Override1375 protected void finish() {1376 }1377 1378 @Override1379 protected void cancel() {1380 precacheTask.cancel();1381 }1382 1383 @Override1384 public ProgressTaskId canRunInBackground() {1385 return ProgressTaskIds.PRECACHE_WMS;1386 }1387 };1388 Main.worker.execute(task);1389 }1390 1391 1392 }1393 1394 protected WMSLayer askWMSLayer() {1395 List<WMSLayer> targetLayers = Main.map.mapView.getLayersOfType(WMSLayer.class);1396 1397 if (targetLayers.isEmpty()) {1398 warnNoImageryLayers();1399 return null;1400 }1401 1402 JosmComboBox layerList = new JosmComboBox(targetLayers.toArray());1403 layerList.setRenderer(new LayerListCellRenderer());1404 layerList.setSelectedIndex(0);1405 1406 JPanel pnl = new JPanel(new GridBagLayout());1407 pnl.add(new JLabel(tr("Please select the imagery layer.")), GBC.eol());1408 pnl.add(layerList, GBC.eol());1409 1410 ExtendedDialog ed = new ExtendedDialog(Main.parent,1411 tr("Select imagery layer"),1412 new String[] { tr("Download"), tr("Cancel") });1413 ed.setButtonIcons(new String[] { "dialogs/down", "cancel" });1414 ed.setContent(pnl);1415 ed.showDialog();1416 if (ed.getValue() != 1)1417 return null;1418 1419 return (WMSLayer) layerList.getSelectedItem();1420 }1421 1422 protected void warnNoImageryLayers() {1423 JOptionPane.showMessageDialog(Main.parent,1424 tr("There are no imagery layers."),1425 tr("No imagery layers"), JOptionPane.WARNING_MESSAGE);1426 }1427 }1428 1429 private static void addToDownload(Area a, Rectangle2D r, Collection<Rectangle2D> results, double max_area) {1430 Area tmp = new Area(r);1431 // intersect with sought-after area1432 tmp.intersect(a);1433 if (tmp.isEmpty())1434 return;1435 Rectangle2D bounds = tmp.getBounds2D();1436 if (bounds.getWidth() * bounds.getHeight() > max_area) {1437 // the rectangle gets too large; split it and make recursive call.1438 Rectangle2D r1;1439 Rectangle2D r2;1440 if (bounds.getWidth() > bounds.getHeight()) {1441 // rectangles that are wider than high are split into a left and right half,1442 r1 = new Rectangle2D.Double(bounds.getX(), bounds.getY(), bounds.getWidth() / 2, bounds.getHeight());1443 r2 = new Rectangle2D.Double(bounds.getX() + bounds.getWidth() / 2, bounds.getY(),1444 bounds.getWidth() / 2, bounds.getHeight());1445 } else {1446 // others into a top and bottom half.1447 r1 = new Rectangle2D.Double(bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight() / 2);1448 r2 = new Rectangle2D.Double(bounds.getX(), bounds.getY() + bounds.getHeight() / 2, bounds.getWidth(),1449 bounds.getHeight() / 2);1450 }1451 addToDownload(a, r1, results, max_area);1452 addToDownload(a, r2, results, max_area);1453 } else {1454 results.add(bounds);1455 }1456 }1457 1458 /**1459 * Makes a new marker layer derived from this GpxLayer containing at least one audio marker1460 * which the given audio file is associated with. Markers are derived from the following (a)1461 * explict waypoints in the GPX layer, or (b) named trackpoints in the GPX layer, or (d)1462 * timestamp on the wav file (e) (in future) voice recognised markers in the sound recording (f)1463 * a single marker at the beginning of the track1464 * @param wavFile : the file to be associated with the markers in the new marker layer1465 * @param markers : keeps track of warning messages to avoid repeated warnings1466 */1467 private void importAudio(File wavFile, MarkerLayer ml, double firstStartTime, Markers markers) {1468 URL url = null;1469 try {1470 url = wavFile.toURI().toURL();1471 } catch (MalformedURLException e) {1472 System.err.println("Unable to convert filename " + wavFile.getAbsolutePath() + " to URL");1473 }1474 Collection<WayPoint> waypoints = new ArrayList<WayPoint>();1475 boolean timedMarkersOmitted = false;1476 boolean untimedMarkersOmitted = false;1477 double snapDistance = Main.pref.getDouble("marker.audiofromuntimedwaypoints.distance", 1.0e-3); /*1478 * about1479 * 251480 * m1481 */1482 WayPoint wayPointFromTimeStamp = null;1483 1484 // determine time of first point in track1485 double firstTime = -1.0;1486 if (data.tracks != null && !data.tracks.isEmpty()) {1487 for (GpxTrack track : data.tracks) {1488 for (GpxTrackSegment seg : track.getSegments()) {1489 for (WayPoint w : seg.getWayPoints()) {1490 firstTime = w.time;1491 break;1492 }1493 if (firstTime >= 0.0) {1494 break;1495 }1496 }1497 if (firstTime >= 0.0) {1498 break;1499 }1500 }1501 }1502 if (firstTime < 0.0) {1503 JOptionPane.showMessageDialog(1504 Main.parent,1505 tr("No GPX track available in layer to associate audio with."),1506 tr("Error"),1507 JOptionPane.ERROR_MESSAGE1508 );1509 return;1510 }1511 1512 // (a) try explicit timestamped waypoints - unless suppressed1513 if (Main.pref.getBoolean("marker.audiofromexplicitwaypoints", true) && data.waypoints != null1514 && !data.waypoints.isEmpty()) {1515 for (WayPoint w : data.waypoints) {1516 if (w.time > firstTime) {1517 waypoints.add(w);1518 } else if (w.time > 0.0) {1519 timedMarkersOmitted = true;1520 }1521 }1522 }1523 1524 // (b) try explicit waypoints without timestamps - unless suppressed1525 if (Main.pref.getBoolean("marker.audiofromuntimedwaypoints", true) && data.waypoints != null1526 && !data.waypoints.isEmpty()) {1527 for (WayPoint w : data.waypoints) {1528 if (waypoints.contains(w)) {1529 continue;1530 }1531 WayPoint wNear = nearestPointOnTrack(w.getEastNorth(), snapDistance);1532 if (wNear != null) {1533 WayPoint wc = new WayPoint(w.getCoor());1534 wc.time = wNear.time;1535 if (w.attr.containsKey("name")) {1536 wc.attr.put("name", w.getString("name"));1537 }1538 waypoints.add(wc);1539 } else {1540 untimedMarkersOmitted = true;1541 }1542 }1543 }1544 1545 // (c) use explicitly named track points, again unless suppressed1546 if ((Main.pref.getBoolean("marker.audiofromnamedtrackpoints", false)) && data.tracks != null1547 && !data.tracks.isEmpty()) {1548 for (GpxTrack track : data.tracks) {1549 for (GpxTrackSegment seg : track.getSegments()) {1550 for (WayPoint w : seg.getWayPoints()) {1551 if (w.attr.containsKey("name") || w.attr.containsKey("desc")) {1552 waypoints.add(w);1553 }1554 }1555 }1556 }1557 }1558 1559 // (d) use timestamp of file as location on track1560 if ((Main.pref.getBoolean("marker.audiofromwavtimestamps", false)) && data.tracks != null1561 && !data.tracks.isEmpty()) {1562 double lastModified = wavFile.lastModified() / 1000.0; // lastModified is in1563 // milliseconds1564 double duration = AudioUtil.getCalibratedDuration(wavFile);1565 double startTime = lastModified - duration;1566 startTime = firstStartTime + (startTime - firstStartTime)1567 / Main.pref.getDouble("audio.calibration", "1.0" /* default, ratio */);1568 WayPoint w1 = null;1569 WayPoint w2 = null;1570 1571 for (GpxTrack track : data.tracks) {1572 for (GpxTrackSegment seg : track.getSegments()) {1573 for (WayPoint w : seg.getWayPoints()) {1574 if (startTime < w.time) {1575 w2 = w;1576 break;1577 }1578 w1 = w;1579 }1580 if (w2 != null) {1581 break;1582 }1583 }1584 }1585 1586 if (w1 == null || w2 == null) {1587 timedMarkersOmitted = true;1588 } else {1589 wayPointFromTimeStamp = new WayPoint(w1.getCoor().interpolate(w2.getCoor(),1590 (startTime - w1.time) / (w2.time - w1.time)));1591 wayPointFromTimeStamp.time = startTime;1592 String name = wavFile.getName();1593 int dot = name.lastIndexOf(".");1594 if (dot > 0) {1595 name = name.substring(0, dot);1596 }1597 wayPointFromTimeStamp.attr.put("name", name);1598 waypoints.add(wayPointFromTimeStamp);1599 }1600 }1601 1602 // (e) analyse audio for spoken markers here, in due course1603 1604 // (f) simply add a single marker at the start of the track1605 if ((Main.pref.getBoolean("marker.audiofromstart") || waypoints.isEmpty()) && data.tracks != null1606 && !data.tracks.isEmpty()) {1607 boolean gotOne = false;1608 for (GpxTrack track : data.tracks) {1609 for (GpxTrackSegment seg : track.getSegments()) {1610 for (WayPoint w : seg.getWayPoints()) {1611 WayPoint wStart = new WayPoint(w.getCoor());1612 wStart.attr.put("name", "start");1613 wStart.time = w.time;1614 waypoints.add(wStart);1615 gotOne = true;1616 break;1617 }1618 if (gotOne) {1619 break;1620 }1621 }1622 if (gotOne) {1623 break;1624 }1625 }1626 }1627 1628 /* we must have got at least one waypoint now */1629 1630 Collections.sort((ArrayList<WayPoint>) waypoints, new Comparator<WayPoint>() {1631 @Override1632 public int compare(WayPoint a, WayPoint b) {1633 return a.time <= b.time ? -1 : 1;1634 }1635 });1636 1637 firstTime = -1.0; /* this time of the first waypoint, not first trackpoint */1638 for (WayPoint w : waypoints) {1639 if (firstTime < 0.0) {1640 firstTime = w.time;1641 }1642 double offset = w.time - firstTime;1643 AudioMarker am = new AudioMarker(w.getCoor(), w, url, ml, w.time, offset);1644 /*1645 * timeFromAudio intended for future use to shift markers of this type on1646 * synchronization1647 */1648 if (w == wayPointFromTimeStamp) {1649 am.timeFromAudio = true;1650 }1651 ml.data.add(am);1652 }1653 1654 if (timedMarkersOmitted && !markers.timedMarkersOmitted) {1655 JOptionPane1656 .showMessageDialog(1657 Main.parent,1658 tr("Some waypoints with timestamps from before the start of the track or after the end were omitted or moved to the start."));1659 markers.timedMarkersOmitted = timedMarkersOmitted;1660 }1661 if (untimedMarkersOmitted && !markers.untimedMarkersOmitted) {1662 JOptionPane1663 .showMessageDialog(1664 Main.parent,1665 tr("Some waypoints which were too far from the track to sensibly estimate their time were omitted."));1666 markers.untimedMarkersOmitted = untimedMarkersOmitted;1667 }1668 }1669 1670 /**1671 * Makes a WayPoint at the projection of point P onto the track providing P is less than1672 * tolerance away from the track1673 *1674 * @param P : the point to determine the projection for1675 * @param tolerance : must be no further than this from the track1676 * @return the closest point on the track to P, which may be the first or last point if off the1677 * end of a segment, or may be null if nothing close enough1678 */1679 public WayPoint nearestPointOnTrack(EastNorth P, double tolerance) {1680 /*1681 * assume the coordinates of P are xp,yp, and those of a section of track between two1682 * trackpoints are R=xr,yr and S=xs,ys. Let N be the projected point.1683 *1684 * The equation of RS is Ax + By + C = 0 where A = ys - yr B = xr - xs C = - Axr - Byr1685 *1686 * Also, note that the distance RS^2 is A^2 + B^21687 *1688 * If RS^2 == 0.0 ignore the degenerate section of track1689 *1690 * PN^2 = (Axp + Byp + C)^2 / RS^2 that is the distance from P to the line1691 *1692 * so if PN^2 is less than PNmin^2 (initialized to tolerance) we can reject the line;1693 * otherwise... determine if the projected poijnt lies within the bounds of the line: PR^2 -1694 * PN^2 <= RS^2 and PS^2 - PN^2 <= RS^21695 *1696 * where PR^2 = (xp - xr)^2 + (yp-yr)^2 and PS^2 = (xp - xs)^2 + (yp-ys)^21697 *1698 * If so, calculate N as xn = xr + (RN/RS) B yn = y1 + (RN/RS) A1699 *1700 * where RN = sqrt(PR^2 - PN^2)1701 */1702 1703 double PNminsq = tolerance * tolerance;1704 EastNorth bestEN = null;1705 double bestTime = 0.0;1706 double px = P.east();1707 double py = P.north();1708 double rx = 0.0, ry = 0.0, sx, sy, x, y;1709 if (data.tracks == null)1710 return null;1711 for (GpxTrack track : data.tracks) {1712 for (GpxTrackSegment seg : track.getSegments()) {1713 WayPoint R = null;1714 for (WayPoint S : seg.getWayPoints()) {1715 EastNorth c = S.getEastNorth();1716 if (R == null) {1717 R = S;1718 rx = c.east();1719 ry = c.north();1720 x = px - rx;1721 y = py - ry;1722 double PRsq = x * x + y * y;1723 if (PRsq < PNminsq) {1724 PNminsq = PRsq;1725 bestEN = c;1726 bestTime = R.time;1727 }1728 } else {1729 sx = c.east();1730 sy = c.north();1731 double A = sy - ry;1732 double B = rx - sx;1733 double C = -A * rx - B * ry;1734 double RSsq = A * A + B * B;1735 if (RSsq == 0.0) {1736 continue;1737 }1738 double PNsq = A * px + B * py + C;1739 PNsq = PNsq * PNsq / RSsq;1740 if (PNsq < PNminsq) {1741 x = px - rx;1742 y = py - ry;1743 double PRsq = x * x + y * y;1744 x = px - sx;1745 y = py - sy;1746 double PSsq = x * x + y * y;1747 if (PRsq - PNsq <= RSsq && PSsq - PNsq <= RSsq) {1748 double RNoverRS = Math.sqrt((PRsq - PNsq) / RSsq);1749 double nx = rx - RNoverRS * B;1750 double ny = ry + RNoverRS * A;1751 bestEN = new EastNorth(nx, ny);1752 bestTime = R.time + RNoverRS * (S.time - R.time);1753 PNminsq = PNsq;1754 }1755 }1756 R = S;1757 rx = sx;1758 ry = sy;1759 }1760 }1761 if (R != null) {1762 EastNorth c = R.getEastNorth();1763 /* if there is only one point in the seg, it will do this twice, but no matter */1764 rx = c.east();1765 ry = c.north();1766 x = px - rx;1767 y = py - ry;1768 double PRsq = x * x + y * y;1769 if (PRsq < PNminsq) {1770 PNminsq = PRsq;1771 bestEN = c;1772 bestTime = R.time;1773 }1774 }1775 }1776 }1777 if (bestEN == null)1778 return null;1779 WayPoint best = new WayPoint(Main.getProjection().eastNorth2latlon(bestEN));1780 best.time = bestTime;1781 return best;1782 }1783 1784 private class CustomizeDrawing extends AbstractAction implements LayerAction, MultiLayerAction {1785 List<Layer> layers;1786 1787 public CustomizeDrawing(List<Layer> l) {1788 this();1789 layers = l;1790 }1791 1792 public CustomizeDrawing(Layer l) {1793 this();1794 layers = new LinkedList<Layer>();1795 layers.add(l);1796 }1797 1798 private CustomizeDrawing() {1799 super(tr("Customize track drawing"), ImageProvider.get("mapmode/addsegment"));1800 putValue("help", ht("/Action/GPXLayerCustomizeLineDrawing"));1801 }1802 1803 @Override1804 public boolean supportLayers(List<Layer> layers) {1805 for(Layer layer: layers) {1806 if(!(layer instanceof GpxLayer))1807 return false;1808 }1809 return true;1810 }1811 1812 @Override1813 public Component createMenuComponent() {1814 return new JMenuItem(this);1815 }1816 1817 @Override1818 public Action getMultiLayerAction(List<Layer> layers) {1819 return new CustomizeDrawing(layers);1820 }1821 1822 @Override1823 public void actionPerformed(ActionEvent e) {1824 boolean hasLocal = false, hasNonlocal = false;1825 for (Layer layer : layers) {1826 if (layer instanceof GpxLayer) {1827 if (((GpxLayer) layer).isLocalFile) {1828 hasLocal = true;1829 } else {1830 hasNonlocal = true;1831 }1832 }1833 }1834 GPXSettingsPanel panel=new GPXSettingsPanel(getName(), hasLocal, hasNonlocal);1835 JScrollPane scrollpane = new JScrollPane(panel,1836 JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,JScrollPane.HORIZONTAL_SCROLLBAR_NEVER );1837 scrollpane.setBorder(BorderFactory.createEmptyBorder( 0, 0, 0, 0 ));1838 int screenHeight = Toolkit.getDefaultToolkit().getScreenSize().height;1839 if (screenHeight < 700) { // to fit on screen 800x6001840 scrollpane.setPreferredSize(new Dimension(panel.getPreferredSize().width, Math.min(panel.getPreferredSize().height,450)));1841 }1842 int answer = JOptionPane.showConfirmDialog(Main.parent, scrollpane,1843 tr("Customize track drawing"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);1844 if (answer == JOptionPane.CANCEL_OPTION || answer == JOptionPane.CLOSED_OPTION) return;1845 for(Layer layer : layers) {1846 // save preferences for all layers1847 boolean f=false;1848 if (layer instanceof GpxLayer) {1849 f=((GpxLayer)layer).isLocalFile;1850 }1851 panel.savePreferences(layer.getName(),f);1852 }1853 Main.map.repaint();1854 }1855 }1856 1857 private class MarkersFromNamedPoins extends AbstractAction {1858 1859 public MarkersFromNamedPoins() {1860 super(tr("Markers From Named Points"), ImageProvider.get("addmarkers"));1861 putValue("help", ht("/Action/MarkersFromNamedPoints"));1862 }1863 1864 @Override1865 public void actionPerformed(ActionEvent e) {1866 GpxData namedTrackPoints = new GpxData();1867 for (GpxTrack track : data.tracks) {1868 for (GpxTrackSegment seg : track.getSegments()) {1869 for (WayPoint point : seg.getWayPoints())1870 if (point.attr.containsKey("name") || point.attr.containsKey("desc")) {1871 namedTrackPoints.waypoints.add(point);1872 }1873 }1874 }1875 1876 MarkerLayer ml = new MarkerLayer(namedTrackPoints, tr("Named Trackpoints from {0}", getName()),1877 getAssociatedFile(), GpxLayer.this);1878 if (ml.data.size() > 0) {1879 Main.main.addLayer(ml);1880 }1881 1882 }1883 }1884 1885 private class ImportAudio extends AbstractAction {1886 1887 public ImportAudio() {1888 super(tr("Import Audio"), ImageProvider.get("importaudio"));1889 putValue("help", ht("/Action/ImportAudio"));1890 }1891 1892 private void warnCantImportIntoServerLayer(GpxLayer layer) {1893 String msg = tr("<html>The data in the GPX layer ''{0}'' has been downloaded from the server.<br>"1894 + "Because its way points do not include a timestamp we cannot correlate them with audio data.</html>",1895 layer.getName()1896 );1897 HelpAwareOptionPane.showOptionDialog(1898 Main.parent,1899 msg,1900 tr("Import not possible"),1901 JOptionPane.WARNING_MESSAGE,1902 ht("/Action/ImportAudio#CantImportIntoGpxLayerFromServer")1903 );1904 }1905 1906 @Override1907 public void actionPerformed(ActionEvent e) {1908 if (GpxLayer.this.data.fromServer) {1909 warnCantImportIntoServerLayer(GpxLayer.this);1910 return;1911 }1912 FileFilter filter = new FileFilter() {1913 @Override1914 public boolean accept(File f) {1915 return f.isDirectory() || f.getName().toLowerCase().endsWith(".wav");1916 }1917 1918 @Override1919 public String getDescription() {1920 return tr("Wave Audio files (*.wav)");1921 }1922 };1923 JFileChooser fc = DiskAccessAction.createAndOpenFileChooser(true, true, null, filter, JFileChooser.FILES_ONLY, "markers.lastaudiodirectory");1924 if (fc != null) {1925 File sel[] = fc.getSelectedFiles();1926 // sort files in increasing order of timestamp (this is the end time, but so1927 // long as they don't overlap, that's fine)1928 if (sel.length > 1) {1929 Arrays.sort(sel, new Comparator<File>() {1930 @Override1931 public int compare(File a, File b) {1932 return a.lastModified() <= b.lastModified() ? -1 : 1;1933 }1934 });1935 }1936 1937 String names = null;1938 for (int i = 0; i < sel.length; i++) {1939 if (names == null) {1940 names = " (";1941 } else {1942 names += ", ";1943 }1944 names += sel[i].getName();1945 }1946 if (names != null) {1947 names += ")";1948 } else {1949 names = "";1950 }1951 MarkerLayer ml = new MarkerLayer(new GpxData(), tr("Audio markers from {0}", getName()) + names,1952 getAssociatedFile(), GpxLayer.this);1953 double firstStartTime = sel[0].lastModified() / 1000.0 /* ms -> seconds */1954 - AudioUtil.getCalibratedDuration(sel[0]);1955 1956 Markers m = new Markers();1957 for (int i = 0; i < sel.length; i++) {1958 importAudio(sel[i], ml, firstStartTime, m);1959 }1960 Main.main.addLayer(ml);1961 Main.map.repaint();1962 }1963 }1964 }1965 1966 private class ImportImages extends AbstractAction {1967 1968 public ImportImages() {1969 super(tr("Import images"), ImageProvider.get("dialogs/geoimage"));1970 putValue("help", ht("/Action/ImportImages"));1971 }1972 1973 private void warnCantImportIntoServerLayer(GpxLayer layer) {1974 String msg = tr("<html>The data in the GPX layer ''{0}'' has been downloaded from the server.<br>"1975 + "Because its way points do not include a timestamp we cannot correlate them with images.</html>",1976 layer.getName()1977 );1978 HelpAwareOptionPane.showOptionDialog(1979 Main.parent,1980 msg,1981 tr("Import not possible"),1982 JOptionPane.WARNING_MESSAGE,1983 ht("/Action/ImportImages#CantImportIntoGpxLayerFromServer")1984 );1985 }1986 1987 private void addRecursiveFiles(LinkedList<File> files, File[] sel) {1988 for (File f : sel) {1989 if (f.isDirectory()) {1990 addRecursiveFiles(files, f.listFiles());1991 } else if (f.getName().toLowerCase().endsWith(".jpg")) {1992 files.add(f);1993 }1994 }1995 }1996 1997 @Override1998 public void actionPerformed(ActionEvent e) {1999 2000 if (GpxLayer.this.data.fromServer) {2001 warnCantImportIntoServerLayer(GpxLayer.this);2002 return;2003 }2004 2005 JpgImporter importer = new JpgImporter(GpxLayer.this);2006 JFileChooser fc = new JFileChooserManager(true, "geoimage.lastdirectory", Main.pref.get("lastDirectory")).2007 createFileChooser(true, null, importer.filter, JFileChooser.FILES_AND_DIRECTORIES).openFileChooser();2008 if (fc != null) {2009 File[] sel = fc.getSelectedFiles();2010 if (sel != null && sel.length > 0) {2011 LinkedList<File> files = new LinkedList<File>();2012 addRecursiveFiles(files, sel);2013 importer.importDataHandleExceptions(files, NullProgressMonitor.INSTANCE);2014 }2015 }2016 }2017 }2018 2019 798 @Override 2020 799 public void projectionChanged(Projection oldValue, Projection newValue) { -
trunk/src/org/openstreetmap/josm/gui/layer/WMSLayer.java
r5557 r5715 84 84 } 85 85 86 boolean isFinished() { 86 public boolean isFinished() { 87 87 return totalCount == processedCount; 88 88 } -
trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/PlayHeadMarker.java
r4284 r5715 143 143 Point p = Main.map.mapView.getPoint(en); 144 144 EastNorth enPlus25px = Main.map.mapView.getEastNorth(p.x+dropTolerance, p.y); 145 cw = recent.parentLayer.fromLayer.nearestPointOnTrack(en, enPlus25px.east() - en.east()); 145 cw = recent.parentLayer.fromLayer.data.nearestPointOnTrack(en, enPlus25px.east() - en.east()); 146 146 } 147 147 … … 219 219 Point p = Main.map.mapView.getPoint(en); 220 220 EastNorth enPlus25px = Main.map.mapView.getEastNorth(p.x+dropTolerance, p.y); 221 WayPoint cw = recent.parentLayer.fromLayer.nearestPointOnTrack(en, enPlus25px.east() - en.east()); 221 WayPoint cw = recent.parentLayer.fromLayer.data.nearestPointOnTrack(en, enPlus25px.east() - en.east()); 222 222 if (cw == null) { 223 223 JOptionPane.showMessageDialog(
Note:
See TracChangeset
for help on using the changeset viewer.