Ticket #22596: JOSM_ElevationProfile_18494.diff
| File JOSM_ElevationProfile_18494.diff, 58.5 KB (added by , 3 years ago) |
|---|
-
build.xml
diff --git a/build.xml b/build.xml index 572c9df..c74d3ef 100644
a b 8 8 <!-- Configure these properties (replace "..." accordingly). 9 9 See https://josm.openstreetmap.de/wiki/DevelopersGuide/DevelopingPlugins 10 10 --> 11 <property name="plugin.author" value="Oliver Wieland "/>11 <property name="plugin.author" value="Oliver Wieland, Harald Hetzner"/> 12 12 <property name="plugin.class" value="org.openstreetmap.josm.plugins.elevation.ElevationProfilePlugin"/> 13 <property name="plugin.description" value="Shows the elevation profile and some statistical data of a GPX track."/>13 <property name="plugin.description" value="Shows the elevation at the location on the map as well as the elevation profile and some statistical data of a GPX track."/> 14 14 <property name="plugin.icon" value="images/elevation.png"/> 15 15 <property name="plugin.link" value="https://wiki.openstreetmap.org/wiki/JOSM/Plugins/ElevationProfile"/> 16 16 -
new file images/preferences/elevation.svg
diff --git a/images/preferences/elevation.svg b/images/preferences/elevation.svg new file mode 100644 index 0000000..4115718
- + 1 <?xml version="1.0" encoding="UTF-8"?> 2 <svg 3 width="24" 4 height="24" 5 viewBox="0 0 24 24"> 6 <g 7 transform="translate(0,-1037.3622)"> 8 <path 9 style="fill:#89a02c;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" 10 d="M 23,1055.3622 H 1 l 5,-7 5,4 6,-10 6,10 z" /> 11 <path 12 style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" 13 d="m 1,1055.3622 5,-7 5,4 6,-10 6,10" /> 14 </g> 15 </svg> -
new file images/statusline/ele.svg
diff --git a/images/statusline/ele.svg b/images/statusline/ele.svg new file mode 100644 index 0000000..969f2d2
- + 1 <?xml version="1.0" encoding="UTF-8"?> 2 <svg 3 version="1.1" 4 width="18px" 5 height="18px" 6 viewBox="0 0 18 18" 7 fill="none" > 8 <path 9 d="M 9,1 V 17" 10 stroke="#ee4422" 11 stroke-width="1.88562" /> 12 <path 13 style="fill:none;stroke:#000000;stroke-width:0.93133px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" 14 d="M 0.99621412,16.121325 9,2.3402182 17.003786,16.121325 Z" /> 15 </svg> -
src/org/openstreetmap/josm/plugins/elevation/ElevationHelper.java
diff --git a/src/org/openstreetmap/josm/plugins/elevation/ElevationHelper.java b/src/org/openstreetmap/josm/plugins/elevation/ElevationHelper.java index 6c4de87..990f540 100644
a b public final class ElevationHelper { 168 168 if (ll != null) { 169 169 // Try to read data from SRTM file 170 170 // TODO: Option to switch this off 171 double eleHgt = HgtReader.get ElevationFromHgt(ll);171 double eleHgt = HgtReader.getInstance().getElevationFromHgt(ll); 172 172 173 173 if (isValidElevation(eleHgt)) { 174 174 return eleHgt; … … public final class ElevationHelper { 184 184 */ 185 185 public static Optional<Bounds> getBounds(ILatLon location) { 186 186 if (location != null) { 187 return HgtReader.get Bounds(location);187 return HgtReader.getInstance().getBounds(location); 188 188 } 189 189 return Optional.empty(); 190 190 } -
new file src/org/openstreetmap/josm/plugins/elevation/ElevationPreferences.java
diff --git a/src/org/openstreetmap/josm/plugins/elevation/ElevationPreferences.java b/src/org/openstreetmap/josm/plugins/elevation/ElevationPreferences.java new file mode 100644 index 0000000..ba9d166
- + 1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.plugins.elevation; 3 4 import java.io.File; 5 import java.nio.file.Paths; 6 7 import org.openstreetmap.josm.data.Preferences; 8 9 /** 10 * Property keys and default values for elevation data preferences. 11 * 12 * @author Harald Hetzner 13 * 14 */ 15 public class ElevationPreferences { 16 17 /** Property key for enabling or disabling use of elevation data. */ 18 public static final String ELEVATION_ENABLED = "elevation.enabled"; 19 20 /** Property key for enabling or disabling elevation profile layer. */ 21 public static final String ELEVATION_PROFILE_ENABLED = "elevation.profile.enabled"; 22 23 /** Property key for enabling or disabling automatic download of elevation data. */ 24 public static final String ELEVATION_AUTO_DOWNLOAD_ENABLED = "elevation.autodownload"; 25 26 /** 27 * Property key for authentication bearer token for SRTM HGT server. 28 * @see HgtDownloader 29 */ 30 public static final String ELEVATION_SERVER_AUTH_BEARER = "elevation.hgt.server.auth.bearer"; 31 32 /** Default property value for enabling use of elevation data: {@code false}. */ 33 public static final boolean DEFAULT_ELEVATION_ENABLED = false; 34 35 /** Default property value for enabling the elevation profile layer: {@code false}. */ 36 public static final boolean DEFAULT_ELEVATION_PROFILE_ENABLED = false; 37 38 /** Default property value for enabling automatic download of elevation data: {@code false}. */ 39 public static final boolean DEFAULT_ELEVATION_AUTO_DOWNLOAD_ENABLED = false; 40 41 /** Default property value for authentication bearer token for SRTM HGT server: Empty {@code String}. */ 42 public static final String DEFAULT_ELEVATION_SERVER_AUTH_BEARER = ""; 43 44 /** Default path, where SRTM3 HGT files need to be placed, respectively to which they will be downloaded. */ 45 public static final File DEFAULT_HGT_DIRECTORY = Paths.get(Preferences.main().getDirs().getCacheDirectory(true).toString(), "elevation", "SRTM3").toFile(); 46 47 /** 48 * URL of <a href="https://urs.earthdata.nasa.gov/users/new/">NASA Earthdata Login User Registration</a> 49 * where users need to register and create an authorization bearer token in order to download elevation 50 * data from {@link ElevationPreferences#HGT_SERVER_BASE_URL}. 51 */ 52 public static final String HGT_SERVER_REGISTRATION_URL = "https://urs.earthdata.nasa.gov/users/new/"; 53 54 /** 55 * URL of 56 * <a href="https://e4ftl01.cr.usgs.gov/MEASURES/SRTMGL3.003/2000.02.11/">NASA's Land Processes Distributed Active Archive Center (LP DAAC)</a> 57 * where SRTM3 HGT files can be downloaded. 58 * 59 * Requires registration at {@link ElevationPreferences#HGT_SERVER_REGISTRATION_URL}. 60 */ 61 public static final String HGT_SERVER_BASE_URL = "https://e4ftl01.cr.usgs.gov/MEASURES/SRTMGL3.003/2000.02.11/"; 62 63 /** 64 * Prefix of compressed as-downloaded HGT files. 65 */ 66 public static final String SRTM3_HGT_ZIP_FILE_PREFIX = ".SRTMGL3.hgt.zip"; 67 68 private ElevationPreferences() {} 69 } -
src/org/openstreetmap/josm/plugins/elevation/ElevationProfilePlugin.java
diff --git a/src/org/openstreetmap/josm/plugins/elevation/ElevationProfilePlugin.java b/src/org/openstreetmap/josm/plugins/elevation/ElevationProfilePlugin.java index d43cae7..d9a3100 100644
a b import org.openstreetmap.josm.gui.IconToggleButton; 10 10 import org.openstreetmap.josm.gui.MainApplication; 11 11 import org.openstreetmap.josm.gui.MainMenu; 12 12 import org.openstreetmap.josm.gui.MapFrame; 13 import org.openstreetmap.josm.gui.preferences.PreferenceSetting; 13 14 import org.openstreetmap.josm.plugins.Plugin; 14 15 import org.openstreetmap.josm.plugins.PluginInformation; 15 16 import org.openstreetmap.josm.plugins.elevation.actions.AddElevationLayerAction; 17 import org.openstreetmap.josm.plugins.elevation.gui.ElevationPreference; 16 18 import org.openstreetmap.josm.plugins.elevation.gui.ElevationProfileDialog; 17 19 import org.openstreetmap.josm.plugins.elevation.gui.ElevationProfileLayer; 20 import org.openstreetmap.josm.plugins.elevation.gui.LocalElevationLabel; 21 import org.openstreetmap.josm.spi.preferences.Config; 18 22 19 23 /** 20 24 * Plugin class for displaying an elevation profile of the tracks. 21 25 * @author Oliver Wieland <oliver.wieland@online.de> 26 * @author Harald Hetzner 22 27 * 23 28 */ 24 29 public class ElevationProfilePlugin extends Plugin { 25 30 26 31 private static ElevationProfileLayer currentLayer; 27 32 33 private ElevationPreference preference = null; 34 35 private boolean elevationEnabled = Config.getPref().getBoolean(ElevationPreferences.ELEVATION_ENABLED, ElevationPreferences.DEFAULT_ELEVATION_ENABLED); 36 37 private LocalElevationLabel localElevationLabel = null; 38 39 private static ElevationProfilePlugin instance = null; 40 41 public static ElevationProfilePlugin getInstance() { 42 return instance; 43 } 44 28 45 /** 29 46 * Initializes the plugin. 30 47 * @param info Context information about the plugin. … … public class ElevationProfilePlugin extends Plugin { 37 54 38 55 // TODO: Disable this view as long as it is not stable 39 56 MainMenu.add(MainApplication.getMenu().imagerySubMenu, new AddElevationLayerAction(), false, 0); 57 58 instance = this; 40 59 } 41 60 42 61 /** … … public class ElevationProfilePlugin extends Plugin { 47 66 @Override 48 67 public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame) { 49 68 super.mapFrameInitialized(oldFrame, newFrame); 69 setElevationEnabled(elevationEnabled, newFrame); 70 } 50 71 51 if (newFrame != null) { 52 ElevationMapMode eleMode = new ElevationMapMode("Elevation profile"); 53 newFrame.addMapMode(new IconToggleButton(eleMode)); 54 ElevationProfileDialog eleProfileDlg = new ElevationProfileDialog(); 55 eleProfileDlg.addModelListener(eleMode); 56 eleProfileDlg.setProfileLayer(getCurrentLayer()); 57 newFrame.addToggleDialog(eleProfileDlg); 58 } 72 /** 73 * Called in the preferences dialog to create a preferences page for the plugin, 74 * if any available. 75 * @return the preferences dialog, or {@code null} 76 */ 77 @Override 78 public PreferenceSetting getPreferenceSetting() { 79 if (preference == null) 80 preference = new ElevationPreference(); 81 return preference; 59 82 } 60 83 61 84 /** … … public class ElevationProfilePlugin extends Plugin { 124 147 } 125 148 ); 126 149 } 150 151 public boolean isElevationEnabled() { 152 return elevationEnabled; 153 } 154 155 /** 156 * Enable or disable displaying elevation at the position of the mouse pointer. 157 * @param enabled If {@code true} displaying of elevation is enabled, else disabled. 158 * @see HgtReader 159 */ 160 public void setElevationEnabled(boolean enabled) { 161 setElevationEnabled(enabled, MainApplication.getMap()); 162 } 163 164 private void setElevationEnabled(boolean enabled, MapFrame mapFrame) { 165 if (localElevationLabel == null && mapFrame != null) { 166 localElevationLabel = new LocalElevationLabel(mapFrame); 167 localElevationLabel.setVisible(enabled); 168 } 169 if (enabled) { 170 // Elevation profile 171 boolean elevationProfileEnabled = Config.getPref().getBoolean(ElevationPreferences.ELEVATION_PROFILE_ENABLED, 172 ElevationPreferences.DEFAULT_ELEVATION_PROFILE_ENABLED); 173 if (elevationProfileEnabled) { 174 ElevationMapMode eleMode = new ElevationMapMode("Elevation profile"); 175 mapFrame.addMapMode(new IconToggleButton(eleMode)); 176 ElevationProfileDialog eleProfileDlg = new ElevationProfileDialog(); 177 eleProfileDlg.addModelListener(eleMode); 178 eleProfileDlg.setProfileLayer(getCurrentLayer()); 179 mapFrame.addToggleDialog(eleProfileDlg); 180 } 181 182 // Auto-download of HGT files 183 boolean elevationAutoDownloadEnabled = Config.getPref().getBoolean(ElevationPreferences.ELEVATION_AUTO_DOWNLOAD_ENABLED, 184 ElevationPreferences.DEFAULT_ELEVATION_AUTO_DOWNLOAD_ENABLED); 185 // If enabled, HgtDownloader used by HgtReader will by itself read the authentication bearer token from the preferences 186 HgtReader.getInstance().setAutoDownloadEnabled(elevationAutoDownloadEnabled); 187 } 188 else { 189 if (localElevationLabel != null) { 190 localElevationLabel.destroy(); 191 localElevationLabel = null; 192 } 193 HgtReader.destroyInstance(); 194 } 195 elevationEnabled = enabled; 196 } 197 127 198 } -
new file src/org/openstreetmap/josm/plugins/elevation/HgtDownloadListener.java
diff --git a/src/org/openstreetmap/josm/plugins/elevation/HgtDownloadListener.java b/src/org/openstreetmap/josm/plugins/elevation/HgtDownloadListener.java new file mode 100644 index 0000000..5b2f924
- + 1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.plugins.elevation; 3 4 import java.io.File; 5 6 import org.openstreetmap.josm.data.coor.ILatLon; 7 8 /** 9 * 10 * @author Harald Hetzner 11 * 12 */ 13 public interface HgtDownloadListener { 14 15 /** 16 * Informs the implementing class that downloading of HGT data for the given 17 * coordinates has started. 18 * 19 * To be called by the thread downloading as soon as downloading actually started. 20 * 21 * @param latLon The coordinates for which the HGT data is now being downloaded. 22 */ 23 public void hgtFileDownloading(ILatLon latLon); 24 25 /** 26 * Informs the implementing class that HGT data for the given coordinates was 27 * successfully downloaded. 28 * 29 * To be called by the thread downloading as soon as the download finished. 30 * 31 * @param latLon The coordinates for which the download of HGT data succeeded. 32 * @param hgtFile The downloaded HGT file. 33 */ 34 public void hgtFileDownloadSucceeded(ILatLon latLon, File hgtFile); 35 36 /** 37 * Informs the implementing class that downloading HGT data for the given 38 * coordinates failed. 39 * 40 * To be called by the thread downloading as soon as downloading fails. 41 * 42 * @param latLon The coordinates for which the download of HGT data failed. 43 */ 44 public void hgtFileDownloadFailed(ILatLon latLon); 45 46 } -
new file src/org/openstreetmap/josm/plugins/elevation/HgtDownloader.java
diff --git a/src/org/openstreetmap/josm/plugins/elevation/HgtDownloader.java b/src/org/openstreetmap/josm/plugins/elevation/HgtDownloader.java new file mode 100644 index 0000000..9dc55fd
- + 1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.plugins.elevation; 3 4 import java.io.File; 5 import java.io.IOException; 6 import java.io.InputStream; 7 import java.net.MalformedURLException; 8 import java.net.URL; 9 import java.nio.file.Files; 10 import java.nio.file.Path; 11 import java.nio.file.Paths; 12 import java.nio.file.StandardCopyOption; 13 import java.util.LinkedList; 14 import java.util.concurrent.ExecutorService; 15 import java.util.concurrent.Executors; 16 17 import org.openstreetmap.josm.data.coor.ILatLon; 18 import org.openstreetmap.josm.gui.io.DownloadFileTask; 19 import org.openstreetmap.josm.spi.preferences.Config; 20 import org.openstreetmap.josm.tools.HttpClient; 21 import org.openstreetmap.josm.tools.Logging; 22 23 /** 24 * Class {@code HgtDownloader} downloads SRTM HGT (Shuttle Radar Topography Mission Height) files with elevation data. 25 * Currently this class is restricted to a resolution of 3 arc seconds (SRTM3). 26 * 27 * SRTM3 HGT files are available at 28 * <a href="https://e4ftl01.cr.usgs.gov/MEASURES/SRTMGL3.003/2000.02.11/">NASA's Land Processes Distributed Active Archive Center (LP DAAC)</a>. 29 * 30 * In order to access these files, registration at <a href="https://urs.earthdata.nasa.gov/users/new/">NASA Earthdata Login User Registration</a> 31 * and creating an authorization bearer token on this site are required. 32 * 33 * @author Harald Hetzner 34 * @see HgtReader 35 * 36 */ 37 public class HgtDownloader { 38 39 private final URL baseUrl; 40 private File hgtDirectory; 41 42 private String authHeader; 43 44 private static final ExecutorService EXECUTOR = Executors.newSingleThreadExecutor(); 45 46 private final LinkedList<HgtDownloadListener> downloadListeners = new LinkedList<HgtDownloadListener>(); 47 48 private boolean uncompressDownloadedFiles = false; 49 50 public HgtDownloader(File hgtDirectory, String url, String bearer) throws MalformedURLException { 51 // May throw MalformedURLException 52 this.baseUrl = new URL(url); 53 // https://stackoverflow.com/questions/38085964/authorization-bearer-token-in-httpclient 54 if (!bearer.equals("")) 55 authHeader = "Bearer " + bearer; 56 else 57 authHeader = null; 58 this.hgtDirectory = hgtDirectory; 59 } 60 61 public HgtDownloader(File hgtDirectory) throws MalformedURLException { 62 this(hgtDirectory, ElevationPreferences.HGT_SERVER_BASE_URL, 63 Config.getPref().get(ElevationPreferences.ELEVATION_SERVER_AUTH_BEARER, ElevationPreferences.DEFAULT_ELEVATION_SERVER_AUTH_BEARER)); 64 } 65 66 public void setHgtDirectory(File hgtDirectory) { 67 this.hgtDirectory = hgtDirectory; 68 } 69 70 public void setUncompressDownloadedFiles(boolean b) { 71 uncompressDownloadedFiles = b; 72 } 73 74 public void addDownloadListener(HgtDownloadListener listener) { 75 if (!downloadListeners.contains(listener)) 76 downloadListeners.add(listener); 77 } 78 79 public void downloadHgtFile(ILatLon latLon) { 80 EXECUTOR.submit(new DownloadHgtFileTask(latLon)); 81 } 82 83 /** 84 * Gets the associated HGT file name for the given coordinate. Usually the 85 * format is <tt>[N|S]nn[W|E]mmm.hgt</tt> where <i>nn</i> is the integral latitude 86 * without decimals and <i>mmm</i> is the longitude. 87 * 88 * @param latLon The coordinate to get the filename for 89 * @return The file name of the HGT file. 90 */ 91 public static String getSrtm3HgtZipFileName(ILatLon latLon) { 92 return HgtReader.getHgtPrefix(latLon) + ElevationPreferences.SRTM3_HGT_ZIP_FILE_PREFIX; 93 } 94 95 private class DownloadHgtFileTask implements Runnable { 96 97 private final ILatLon latLon; 98 99 100 public DownloadHgtFileTask(ILatLon latLon) { 101 this.latLon = latLon; 102 } 103 104 @Override 105 public void run() { 106 downloading(); 107 String hgtDirectoryPath = HgtDownloader.this.hgtDirectory.toString(); 108 String hgtZipFileName = HgtDownloader.getSrtm3HgtZipFileName(latLon); 109 File hgtFile = null; 110 111 URL url = null; 112 try { 113 url = new URL(HgtDownloader.this.baseUrl + hgtZipFileName); 114 } catch (MalformedURLException e) { 115 downloadFailed(); 116 return; 117 } 118 HttpClient httpClient = HttpClient.create(url); 119 if (authHeader != null) 120 httpClient.setHeader("Authorization", authHeader); 121 HttpClient.Response response = null; 122 try { 123 response = httpClient.connect(); 124 //Logging.info("Elevation: HGT server responded: " + response.getResponseCode() + " " + response.getResponseMessage()); 125 // https://urs.earthdata.nasa.gov/documentation/for_users/data_access/java 126 if (response.getResponseCode() != 200) { 127 downloadFailed(); 128 return; 129 } 130 InputStream in = response.getContent(); 131 Path downloadedZipFile = Paths.get(hgtDirectoryPath, hgtZipFileName); 132 Files.copy(in, downloadedZipFile, StandardCopyOption.REPLACE_EXISTING); 133 if (uncompressDownloadedFiles) { 134 DownloadFileTask.unzipFileRecursively(downloadedZipFile.toFile(), hgtDirectoryPath); 135 Files.delete(downloadedZipFile); 136 // Determine the file, which was uncompressed 137 hgtFile = HgtReader.getInstance().getHgtFile(latLon); 138 } 139 else { 140 hgtFile = downloadedZipFile.toFile(); 141 } 142 } catch (IOException e) { 143 if (response != null) 144 Logging.error("Elevation: HGT server responded: " + response.getResponseCode() + " " + response.getResponseMessage()); 145 Logging.error("Elevation: Downloading HGT file " + hgtZipFileName + " failed: " + e.toString()); 146 downloadFailed(); 147 return; 148 } 149 150 // This would happen, if the downloaded file was uncompressed, but it does not contain an appropriately named file (with HGT prefix) 151 if (hgtFile == null) { 152 Logging.error("Elevation: Downloaded compressed file " + hgtZipFileName + " did not contain a file with the expected HGT prefix!"); 153 downloadFailed(); 154 return; 155 } 156 157 Logging.info("Elevation: Successfully downloaded HGT file " + hgtFile.getName() + " to HGT directory: " + HgtDownloader.this.hgtDirectory.toString()); 158 downloadSucceeded(hgtFile); 159 } 160 161 private void downloading() { 162 for (HgtDownloadListener listener : HgtDownloader.this.downloadListeners) 163 listener.hgtFileDownloading(latLon); 164 } 165 166 private void downloadSucceeded(File hgtFile) { 167 for (HgtDownloadListener listener : HgtDownloader.this.downloadListeners) 168 listener.hgtFileDownloadSucceeded(latLon, hgtFile); 169 } 170 171 private void downloadFailed() { 172 for (HgtDownloadListener listener : HgtDownloader.this.downloadListeners) 173 listener.hgtFileDownloadFailed(latLon); 174 } 175 } 176 } -
src/org/openstreetmap/josm/plugins/elevation/HgtFileImporter.java
diff --git a/src/org/openstreetmap/josm/plugins/elevation/HgtFileImporter.java b/src/org/openstreetmap/josm/plugins/elevation/HgtFileImporter.java index b74d19c..72dbd6a 100644
a b public class HgtFileImporter extends FileImporter { 25 25 26 26 @Override 27 27 public void importData(File file, ProgressMonitor progressMonitor) throws IOException, IllegalDataException { 28 Bounds bounds = HgtReader. read(file);28 Bounds bounds = HgtReader.getInstance().read(file); 29 29 if (bounds != null && MainApplication.getMap() != null && MainApplication.getMap().mapView != null) 30 30 MainApplication.getMap().mapView.zoomTo(bounds); 31 31 } -
src/org/openstreetmap/josm/plugins/elevation/HgtReader.java
diff --git a/src/org/openstreetmap/josm/plugins/elevation/HgtReader.java b/src/org/openstreetmap/josm/plugins/elevation/HgtReader.java index 97a565c..3f31753 100644
a b import java.io.File; 5 5 import java.io.FileNotFoundException; 6 6 import java.io.IOException; 7 7 import java.io.InputStream; 8 import java.net.MalformedURLException; 8 9 import java.nio.ByteBuffer; 9 10 import java.nio.ByteOrder; 11 import java.nio.file.Files; 10 12 import java.nio.file.Paths; 11 import java.util.Arrays;12 13 import java.util.HashMap; 13 import java.util.List;14 14 import java.util.Optional; 15 import java.util.Set; 15 16 import java.util.regex.Matcher; 16 17 import java.util.regex.Pattern; 18 import java.util.stream.Collectors; 19 import java.util.stream.Stream; 17 20 18 21 import org.apache.commons.compress.utils.IOUtils; 19 22 import org.openstreetmap.josm.data.Bounds; 20 import org.openstreetmap.josm.data.Preferences;21 23 import org.openstreetmap.josm.data.coor.ILatLon; 22 24 import org.openstreetmap.josm.io.Compression; 23 25 import org.openstreetmap.josm.tools.CheckParameterUtil; 24 26 import org.openstreetmap.josm.tools.Logging; 25 27 26 28 /** 27 * Class HgtReader reads data from SRTM HGT files. Currently this class is restricted to a resolution of 3 arc seconds. 29 * Class {@code HgtReader} reads elevation data from SRTM HGT (Shuttle Radar Topography Mission Height) files. 30 * Currently this class is restricted to a resolution of 3 arc seconds (SRTM3). 31 * 32 * SRTM3 HGT files are available at 33 * <a href="https://e4ftl01.cr.usgs.gov/MEASURES/SRTMGL3.003/2000.02.11/">NASA's Land Processes Distributed Active Archive Center (LP DAAC)</a>. 34 * 35 * In order to access these files, registration at <a href="https://urs.earthdata.nasa.gov/users/new/">NASA Earthdata Login User Registration</a> 36 * and creating an authorization bearer token on this site are required. 28 37 * 29 * SRTM data files are available at the <a href="http://dds.cr.usgs.gov/srtm/version2_1/SRTM3">NASA SRTM site</a>30 38 * @author Oliver Wieland <oliver.wieland@online.de> 39 * @author Harald Hetzner 40 * @see HgtDownloader 31 41 */ 32 public class HgtReader {42 public class HgtReader implements HgtDownloadListener { 33 43 private static final int SRTM_EXTENT = 1; // degree 34 private static final List<String> COMPRESSION_EXT = Arrays.asList("xz", "gzip", "zip", "bz", "bz2");35 44 36 45 public static final String HGT_EXT = ".hgt"; 37 46 47 public static final Pattern HGT_PREFIX_PATTERN = Pattern.compile("^(([NS])(\\d{2})([EW])(\\d{3})).+"); 48 38 49 // alter these values for different SRTM resolutions 39 50 public static final int HGT_VOID = Short.MIN_VALUE; // magic number which indicates 'void data' in HGT file 40 51 41 private static final HashMap<String, short[][]> cache = new HashMap<>();52 private final HashMap<String, HgtCacheData> cache = new HashMap<>(); 42 53 43 public static double getElevationFromHgt(ILatLon coor) { 44 try { 45 String file = getHgtFileName(coor); 46 // given area in cache? 47 if (!cache.containsKey(file)) { 48 49 // fill initial cache value. If no file is found, then 50 // we use it as a marker to indicate 'file has been searched 51 // but is not there' 52 cache.put(file, null); 53 // Try all resource directories 54 for (String location : Preferences.getAllPossiblePreferenceDirs()) { 55 String fullPath = new File(location + File.separator + "elevation", file).getPath(); 56 File f = new File(fullPath); 57 if (!f.exists()) { 58 for (String ext : COMPRESSION_EXT) { 59 f = new File(fullPath + "." + ext); 60 if (f.exists()) break; 61 } 54 private static HgtReader hgtReader = null; 55 56 private File hgtDirectory = null; 57 private boolean autoDownloadEnabled = false; 58 private HgtDownloader hgtDownloader = null; 59 60 61 public static HgtReader getInstance() { 62 if (hgtReader == null) 63 hgtReader = new HgtReader(); 64 return hgtReader; 65 } 66 67 public static void destroyInstance() { 68 hgtReader = null; 69 } 70 71 private HgtReader() { 72 this(ElevationPreferences.DEFAULT_HGT_DIRECTORY); 73 } 74 75 private HgtReader(File hgtDirectory) { 76 setHgtDirectory(hgtDirectory); 77 } 78 79 public File getHgtDirectory() { 80 return hgtDirectory; 81 } 82 83 public void setHgtDirectory(File hgtDirectory) { 84 if (!hgtDirectory.exists() && hgtDirectory.mkdirs()) 85 Logging.info("Elevation: Created directory for HGT files: " + hgtDirectory.toString()); 86 if (hgtDirectory.isDirectory()) { 87 this.hgtDirectory = hgtDirectory; 88 Logging.info("Elevation: Set directory for HGT files to: " + hgtDirectory.toString()); 89 } 90 else { 91 Logging.error("Elevation: Could not create directory for HGT files: " + hgtDirectory.toString()); 92 hgtDirectory = null; 93 } 94 if (hgtDownloader != null) 95 hgtDownloader.setHgtDirectory(hgtDirectory); 96 } 97 98 public void setAutoDownloadEnabled(boolean enabled) { 99 if (autoDownloadEnabled == enabled) 100 return; 101 if (enabled) { 102 if (hgtDirectory != null) { 103 if (hgtDownloader == null) 104 try { 105 hgtDownloader = new HgtDownloader(hgtDirectory); 106 hgtDownloader.addDownloadListener(this); 107 } catch (MalformedURLException e) { 108 autoDownloadEnabled = false; 109 Logging.error("Elevation: Cannot enable auto-downloading: " + e.toString()); 110 return; 62 111 } 63 if (f.exists()) { 64 read(f); 65 break; 112 else 113 hgtDownloader.setHgtDirectory(hgtDirectory); 114 autoDownloadEnabled = true; 115 Logging.info("Elevation: Enabled auto-downloading of HGT files to " + hgtDirectory.toString()); 116 } 117 else { 118 hgtDownloader = null; 119 autoDownloadEnabled = false; 120 Logging.error("Elevation: Cannot enable auto-downloading as directory for HGT files was not set"); 121 } 122 } 123 else { 124 hgtDownloader = null; 125 autoDownloadEnabled = false; 126 Logging.info("Elevation: Disabled auto-downloading of HGT files"); 127 } 128 } 129 130 /** 131 * Returns the elevation at the location of the provided coordinate. 132 * If there is not HGT file with elevation data for this location and 133 * <code>autoDownload</code> is enabled, it will be attempted to download 134 * the HGT file. 135 * 136 * @param latLon The location at which the elevation is of interest. 137 * @return The elevation at the provided location or {@link ElevationHelper#NO_ELEVATION ElevationHelper.NO_ELEVATION} 138 * if no HGT file elevation data for the location is available at present. 139 */ 140 public double getElevationFromHgt(ILatLon latLon) { 141 String hgtPrefix = getHgtPrefix(latLon); 142 HgtCacheData hgtCacheData; 143 144 synchronized(cache) { 145 hgtCacheData = cache.get(hgtPrefix); 146 // data not in cache 147 if (hgtCacheData == null) { 148 File hgtFile = getHgtFile(latLon); 149 // If a HGT file with the data exists locally, read it in 150 if (hgtFile != null) { 151 Logging.info("Elevation: Caching data for HGT prefix " + hgtPrefix + " from file " + hgtFile.getAbsolutePath()); 152 short[][] data = null; 153 try { 154 data = readHgtFile(hgtFile.toString()); 155 } catch (FileNotFoundException e) { 156 Logging.error("Elevation: Getting elevation from HGT file " + hgtFile.getAbsolutePath() + " failed: => " + e.getMessage()); 157 // no problem... file not there 158 return ElevationHelper.NO_ELEVATION; 159 } catch (IOException ioe) { 160 // oops... 161 Logging.error(ioe); 162 // fallback 163 return ElevationHelper.NO_ELEVATION; 66 164 } 165 hgtCacheData = new HgtCacheData(data); 166 cache.put(hgtPrefix, hgtCacheData); 167 } 168 // Otherwise, put an empty data set with status "missing" into the cache 169 else { 170 hgtCacheData = new HgtCacheData(); 171 cache.put(hgtPrefix, hgtCacheData); 67 172 } 68 173 } 69 70 // read elevation value 71 return readElevation(coor, file); 72 } catch (FileNotFoundException e) { 73 Logging.error("Get elevation from HGT " + coor + " failed: => " + e.getMessage()); 74 // no problem... file not there 75 return ElevationHelper.NO_ELEVATION; 76 } catch (Exception ioe) { 77 // oops... 78 Logging.error(ioe); 79 // fallback 80 return ElevationHelper.NO_ELEVATION; 174 // Read elevation value if HGT data is available 175 else if (hgtCacheData.getStatus() == HgtCacheData.Status.VALID) { 176 return readElevationFromCache(latLon); 177 } 178 // If the HGT file with the relevant elevation data is missing and auto-downloading is enabled, try to download it 179 else if (hgtCacheData.getStatus() == HgtCacheData.Status.MISSING && autoDownloadEnabled) { 180 hgtCacheData.setDownloadScheduled(); 181 hgtDownloader.downloadHgtFile(latLon); 182 } 81 183 } 184 // If not valid elevation data could be returned, return no elevation 185 return ElevationHelper.NO_ELEVATION; 82 186 } 83 187 84 public static Bounds read(File file) throws IOException { 85 String location = file.getName(); 86 for (String ext : COMPRESSION_EXT) { 87 location = location.replaceAll("\\." + ext + "$", ""); 88 } 89 short[][] sb = readHgtFile(file.getPath()); 90 // Overwrite the cache file (assume that is desired) 91 cache.put(location, sb); 92 Pattern pattern = Pattern.compile("([NS])(\\d{2})([EW])(\\d{3})"); 93 Matcher matcher = pattern.matcher(location); 94 if (matcher.lookingAt()) { 95 int lat = ("S".equals(matcher.group(1)) ? -1 : 1) * Integer.parseInt(matcher.group(2)); 96 int lon = ("W".equals(matcher.group(3)) ? -1 : 1) * Integer.parseInt(matcher.group(4)); 188 public Bounds read(File file) throws IOException { 189 Matcher matcher = HGT_PREFIX_PATTERN.matcher(file.getName()); 190 if (matcher.matches()) { 191 String hgtPrefix = matcher.group(1); 192 short[][] data = readHgtFile(file.getPath()); 193 HgtCacheData hgtCacheData = new HgtCacheData(data); 194 // Overwrite the cache file (assume that is desired) 195 synchronized (cache) { 196 cache.put(hgtPrefix, hgtCacheData); 197 } 198 int lat = ("S".equals(matcher.group(2)) ? -1 : 1) * Integer.parseInt(matcher.group(3)); 199 int lon = ("W".equals(matcher.group(4)) ? -1 : 1) * Integer.parseInt(matcher.group(5)); 97 200 return new Bounds(lat, lon, lat + 1, lon + 1); 98 201 } 99 202 return null; 100 203 } 101 204 102 private static short[][] readHgtFile(String file ) throws IOException {103 CheckParameterUtil.ensureParameterNotNull(file );205 private static short[][] readHgtFile(String fileName) throws IOException { 206 CheckParameterUtil.ensureParameterNotNull(fileName); 104 207 105 208 short[][] data = null; 106 209 107 try (InputStream fis = Compression.getUncompressedFileInputStream(Paths.get(file ))) {210 try (InputStream fis = Compression.getUncompressedFileInputStream(Paths.get(fileName))) { 108 211 // choose the right endianness 109 212 ByteBuffer bb = ByteBuffer.wrap(IOUtils.toByteArray(fis)); 110 213 //System.out.println(Arrays.toString(bb.array())); … … public class HgtReader { 130 233 * Reads the elevation value for the given coordinate. 131 234 * 132 235 * See also <a href="http://gis.stackexchange.com/questions/43743/how-to-extract-elevation-from-hgt-file">stackexchange.com</a> 133 * @param coorthe coordinate to get the elevation data for236 * @param latLon the coordinate to get the elevation data for 134 237 * @return the elevation value or <code>Double.NaN</code>, if no value is present 135 238 */ 136 public static double readElevation(ILatLon coor) { 137 String tag = getHgtFileName(coor); 138 return readElevation(coor, tag); 139 } 239 private double readElevationFromCache(ILatLon latLon) { 240 String hgtPrefix = getHgtPrefix(latLon); 241 HgtCacheData hgtCacheData; 140 242 141 /** 142 * Reads the elevation value for the given coordinate. 143 * 144 * See also <a href="http://gis.stackexchange.com/questions/43743/how-to-extract-elevation-from-hgt-file">stackexchange.com</a> 145 * @param coor the coordinate to get the elevation data for 146 * @param fileName The expected filename 147 * @return the elevation value or <code>Double.NaN</code>, if no value is present 148 */ 149 public static double readElevation(ILatLon coor, String fileName) { 243 synchronized(cache) { 244 hgtCacheData = cache.get(hgtPrefix); 245 } 150 246 151 short[][] sb = cache.get(fileName); 247 if (hgtCacheData == null || hgtCacheData.getStatus() != HgtCacheData.Status.VALID) 248 return ElevationHelper.NO_ELEVATION; 152 249 153 if (sb == null) { 250 short[][] data = hgtCacheData.getData(); 251 if (data == null) 154 252 return ElevationHelper.NO_ELEVATION; 155 }156 253 157 int[] index = getIndex( coor, sb.length);158 short ele = sb[index[0]][index[1]];254 int[] index = getIndex(latLon, data.length); 255 short ele; 159 256 160 if (ele == HGT_VOID) { 257 try { 258 ele = data[index[0]][index[1]]; 259 } catch (ArrayIndexOutOfBoundsException e) { 260 Logging.error("Elevation: Error reading elevation data for prefix " + hgtPrefix + ":" + e.toString()); 161 261 return ElevationHelper.NO_ELEVATION; 162 262 } 263 264 if (ele == HGT_VOID) 265 return ElevationHelper.NO_ELEVATION; 163 266 return ele; 164 267 } 165 268 166 public static Optional<Bounds> getBounds(ILatLon location) { 167 final String fileName = getHgtFileName(location); 168 final short[][] sb = cache.get(fileName); 269 public Optional<Bounds> getBounds(ILatLon location) { 270 String hgtPrefix = getHgtPrefix(location); 271 short[][] data = null; 272 273 synchronized(cache) { 274 data = cache.get(hgtPrefix).getData(); 275 } 169 276 170 if ( sb== null) {277 if (data == null) { 171 278 return Optional.empty(); 172 279 } 173 280 174 281 final double latDegrees = location.lat(); 175 282 final double lonDegrees = location.lon(); 176 283 177 final float fraction = ((float) SRTM_EXTENT) / sb.length;284 final float fraction = ((float) SRTM_EXTENT) / data.length; 178 285 final int latitude = (int) Math.floor(latDegrees) + (latDegrees < 0 ? 1 : 0); 179 286 final int longitude = (int) Math.floor(lonDegrees) + (lonDegrees < 0 ? 1 : 0); 180 287 181 final int[] index = getIndex(location, sb.length);288 final int[] index = getIndex(location, data.length); 182 289 final int latSign = latitude > 0 ? 1 : -1; 183 290 final int lonSign = longitude > 0 ? 1 : -1; 184 291 final double minLat = latitude + latSign * fraction * index[0]; … … public class HgtReader { 217 324 return new int[] { latitude, longitude }; 218 325 } 219 326 327 /** 328 * Returns the HGT file, which contains elevation data for a given location, found in the given HGT directory. 329 * Note: This method only checks if there is a file name with appropriate name. It does not check if the file content is appropriate. 330 * @param latLon The location for which the SRTM3 HGT file with the elevation data is of interest. 331 * @return The HGT file with the elevation data for the given location or <code>null</code> 332 * if there is no file with an appropriate name. 333 */ 334 public File getHgtFile(ILatLon latLon) { 335 String hgtPrefixFromLatLon = getHgtPrefix(latLon); 336 // https://www.baeldung.com/java-list-directory-files 337 Set<File> files = Stream.of(hgtDirectory.listFiles()).filter(file -> !file.isDirectory()).collect(Collectors.toSet()); 338 for (File file : files) { 339 String hgtPrefixFromFile = getHgtPrefix(file.getName()); 340 if (hgtPrefixFromFile != null && hgtPrefixFromFile.equals(hgtPrefixFromLatLon)) 341 return file; 342 } 343 344 return null; 345 } 346 220 347 /** 221 348 * Gets the associated HGT file name for the given way point. Usually the 222 349 * format is <tt>[N|S]nn[W|E]mmm.hgt</tt> where <i>nn</i> is the integral latitude … … public class HgtReader { 225 352 * @param latLon the coordinate to get the filename for 226 353 * @return the file name of the HGT file 227 354 */ 228 public static String getHgtFileName(ILatLon latLon) { 355 /*public static String getHgtFileName(ILatLon latLon) { 356 return getHgtPrefix(latLon) + HGT_EXT; 357 }*/ 358 359 public static String getHgtPrefix(ILatLon latLon) { 229 360 int lat = (int) Math.floor(latLon.lat()); 230 361 int lon = (int) Math.floor(latLon.lon()); 231 362 … … public class HgtReader { 241 372 lon = Math.abs(lon); 242 373 } 243 374 244 return String.format("%s%2d%s%03d" + HGT_EXT, latPref, lat, lonPref, lon); 375 return String.format("%s%2d%s%03d", latPref, lat, lonPref, lon); 376 } 377 378 public static String getHgtPrefix(String fileName) { 379 Matcher matcher = HGT_PREFIX_PATTERN.matcher(fileName); 380 if (matcher.matches()) 381 return matcher.group(1); 382 return null; 245 383 } 246 384 247 385 public static double frac(double d) { … … public class HgtReader { 254 392 return fPart; 255 393 } 256 394 257 public static void clearCache() { 258 cache.clear(); 395 public void clearCache() { 396 synchronized(cache) { 397 cache.clear(); 398 } 399 } 400 401 @Override 402 public void hgtFileDownloading(ILatLon latLon) { 403 String hgtPrefix = getHgtPrefix(latLon); 404 405 synchronized (cache) { 406 HgtCacheData hgtCacheData = cache.get(hgtPrefix); 407 // Should not happen 408 if (hgtCacheData == null) { 409 hgtCacheData = new HgtCacheData(); 410 cache.put(hgtPrefix, hgtCacheData); 411 } 412 hgtCacheData.setDownloading(); 413 } 414 } 415 416 @Override 417 public void hgtFileDownloadSucceeded(ILatLon latLon, File hgtFile) { 418 short[][] data = null; 419 try { 420 data = readHgtFile(hgtFile.getAbsolutePath()); 421 } catch (Exception e) { 422 Logging.error("Elevation: Error reading HGT file " + hgtFile.getAbsolutePath() + ": " + e.toString()); 423 } 424 425 String hgtPrefix = getHgtPrefix(latLon); 426 HgtCacheData hgtCacheData; 427 synchronized (cache) { 428 hgtCacheData = cache.get(hgtPrefix); 429 if (hgtCacheData != null) 430 hgtCacheData.setData(data); 431 // Should not happen 432 else { 433 hgtCacheData = new HgtCacheData(data); 434 cache.put(hgtPrefix, hgtCacheData); 435 } 436 } 437 438 // In case that the downloaded file is corrupt, try to delete it 439 if (data == null) { 440 hgtCacheData.setDownloadFailed(); 441 Logging.info("Elevation: Deleting downloaded, but corrupt HGT file: " + hgtFile.getAbsolutePath()); 442 try { 443 Files.delete(Paths.get(hgtFile.getAbsolutePath())); 444 } catch (IOException e) { 445 Logging.error("Elevation: Error deleting downloaded, but corrupt HGT file: " + e.toString()); 446 } 447 } 448 } 449 450 @Override 451 public void hgtFileDownloadFailed(ILatLon latLon) { 452 String hgtPrefix = getHgtPrefix(latLon); 453 454 synchronized (cache) { 455 HgtCacheData hgtCacheData = cache.get(hgtPrefix); 456 // Should not happen 457 if (hgtCacheData == null) { 458 hgtCacheData = new HgtCacheData(); 459 cache.put(hgtPrefix, hgtCacheData); 460 } 461 hgtCacheData.setDownloadFailed(); 462 } 463 464 } 465 466 private static class HgtCacheData { 467 468 private short[][] data; 469 private Status status; 470 471 enum Status { 472 VALID, 473 MISSING, 474 DOWNLOAD_SCHEDULED, 475 DOWNLOADING, 476 DOWNLOAD_FAILED 477 } 478 479 public HgtCacheData() { 480 this(null); 481 } 482 483 public HgtCacheData(short[][] data) { 484 setData(data); 485 } 486 487 public short[][] getData() { 488 return data; 489 } 490 491 public Status getStatus() { 492 return status; 493 } 494 495 public void setDownloadScheduled() { 496 status = Status.DOWNLOAD_SCHEDULED; 497 } 498 499 public void setDownloading() { 500 status = Status.DOWNLOADING; 501 } 502 503 public void setDownloadFailed() { 504 status = Status.DOWNLOAD_FAILED; 505 data = null; 506 } 507 508 public void setData(short[][] data) { 509 if (data == null) 510 status = Status.MISSING; 511 else 512 status = Status.VALID; 513 this.data = data; 514 } 259 515 } 260 516 } -
src/org/openstreetmap/josm/plugins/elevation/grid/ElevationGridLayer.java
diff --git a/src/org/openstreetmap/josm/plugins/elevation/grid/ElevationGridLayer.java b/src/org/openstreetmap/josm/plugins/elevation/grid/ElevationGridLayer.java index da76bf4..68c3393 100644
a b import org.openstreetmap.josm.gui.Notification; 34 34 import org.openstreetmap.josm.gui.layer.Layer; 35 35 import org.openstreetmap.josm.gui.util.GuiHelper; 36 36 import org.openstreetmap.josm.plugins.elevation.ElevationHelper; 37 import org.openstreetmap.josm.plugins.elevation.HgtReader;38 37 import org.openstreetmap.josm.plugins.elevation.IVertexRenderer; 39 38 import org.openstreetmap.josm.tools.ImageProvider; 40 39 import org.openstreetmap.josm.tools.Logging; … … public class ElevationGridLayer extends Layer implements TileLoaderListener, Mou 58 57 59 58 public ElevationGridLayer(String name) { 60 59 super(name); 61 HgtReader.clearCache();62 60 MainApplication.getMap().mapView.addMouseListener(this); 63 61 64 62 setOpacity(0.8); … … public class ElevationGridLayer extends Layer implements TileLoaderListener, Mou 283 281 } 284 282 285 283 @Override 286 public void destroy() { 287 super.destroy(); 288 HgtReader.clearCache(); 284 public void finalize() throws Throwable { 285 super.finalize(); 289 286 MainApplication.getMap().mapView.removeMouseListener(this); 290 287 } 291 288 } -
new file src/org/openstreetmap/josm/plugins/elevation/gui/ElevationPreference.java
diff --git a/src/org/openstreetmap/josm/plugins/elevation/gui/ElevationPreference.java b/src/org/openstreetmap/josm/plugins/elevation/gui/ElevationPreference.java new file mode 100644 index 0000000..fac1f86
- + 1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.plugins.elevation.gui; 3 4 import static org.openstreetmap.josm.tools.I18n.tr; 5 6 import javax.swing.Box; 7 8 import org.openstreetmap.josm.gui.preferences.DefaultTabPreferenceSetting; 9 import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane; 10 import org.openstreetmap.josm.plugins.elevation.ElevationPreferences; 11 import org.openstreetmap.josm.plugins.elevation.ElevationProfilePlugin; 12 import org.openstreetmap.josm.spi.preferences.Config; 13 import org.openstreetmap.josm.tools.GBC; 14 15 /** 16 * Elevation data sub-preferences in preferences. 17 * @author Harald Hetzner 18 * 19 */ 20 public final class ElevationPreference extends DefaultTabPreferenceSetting { 21 22 private ElevationPreferencesPanel pnlHgtPreferences; 23 24 public ElevationPreference() { 25 super("elevation", tr("Elevation Data"), tr("Elevation preferences and connection settings for the HGT server.")); 26 } 27 28 @Override 29 public void addGui(PreferenceTabbedPane gui) { 30 pnlHgtPreferences = new ElevationPreferencesPanel(); 31 pnlHgtPreferences.add(Box.createVerticalGlue(), GBC.eol().fill()); 32 gui.createPreferenceTab(this).add(pnlHgtPreferences, GBC.eol().fill()); 33 } 34 35 /** 36 * Saves the values to the preferences and applies them. 37 */ 38 @Override 39 public boolean ok() { 40 // Save to preferences file 41 pnlHgtPreferences.saveToPreferences(); 42 43 // Apply preferences 44 boolean elevationEnabled = Config.getPref().getBoolean(ElevationPreferences.ELEVATION_ENABLED, ElevationPreferences.DEFAULT_ELEVATION_ENABLED); 45 ElevationProfilePlugin.getInstance().setElevationEnabled(elevationEnabled); 46 47 return false; 48 } 49 50 @Override 51 public String getHelpContext() { 52 return null; 53 } 54 } -
new file src/org/openstreetmap/josm/plugins/elevation/gui/ElevationPreferencesPanel.java
diff --git a/src/org/openstreetmap/josm/plugins/elevation/gui/ElevationPreferencesPanel.java b/src/org/openstreetmap/josm/plugins/elevation/gui/ElevationPreferencesPanel.java new file mode 100644 index 0000000..eb865a0
- + 1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.plugins.elevation.gui; 3 4 import static org.openstreetmap.josm.tools.I18n.tr; 5 6 import java.awt.Desktop; 7 import java.awt.Dimension; 8 import java.awt.GridBagConstraints; 9 import java.awt.GridBagLayout; 10 import java.awt.Insets; 11 import java.io.IOException; 12 import java.net.URI; 13 14 import javax.swing.BorderFactory; 15 import javax.swing.JCheckBox; 16 import javax.swing.JLabel; 17 import javax.swing.JPanel; 18 import javax.swing.event.HyperlinkEvent; 19 20 import org.openstreetmap.josm.gui.widgets.JMultilineLabel; 21 import org.openstreetmap.josm.gui.widgets.JosmTextField; 22 import org.openstreetmap.josm.gui.widgets.VerticallyScrollablePanel; 23 import org.openstreetmap.josm.plugins.elevation.ElevationPreferences; 24 import org.openstreetmap.josm.spi.preferences.Config; 25 import org.openstreetmap.josm.spi.preferences.IPreferences; 26 import org.openstreetmap.josm.tools.GBC; 27 import org.openstreetmap.josm.tools.Logging; 28 29 /** 30 * Component allowing input of HGT (elevation) server settings. 31 */ 32 public class ElevationPreferencesPanel extends VerticallyScrollablePanel { 33 34 static final class AutoSizePanel extends JPanel { 35 AutoSizePanel() { 36 super(new GridBagLayout()); 37 } 38 39 @Override 40 public Dimension getMinimumSize() { 41 return getPreferredSize(); 42 } 43 } 44 45 private final JCheckBox cbEnableElevation = new JCheckBox(tr("Enable Use of Elevation Data")); 46 private final JMultilineLabel lblpElevationData = 47 new JMultilineLabel(tr("<html>STRM3 HGT files can be downloaded from <a href=\"{0}\">{0}</a>.</html>", ElevationPreferences.HGT_SERVER_BASE_URL)); 48 private final JCheckBox cbEnableElevationProfile = new JCheckBox(tr("Enable Elevation Profile Layer")); 49 private final JCheckBox cbEnableAutoDownload = new JCheckBox(tr("Enable Automatic Downloading of Elevation Data")); 50 private final JLabel lblAuthBearer = new JLabel(tr("Authorization Bearer Token:")); 51 private final JosmTextField tfAuthBearer = new JosmTextField(); 52 private final JMultilineLabel lblAuthBearerNotes = 53 new JMultilineLabel(tr("<html>You need to register at <a href=\"{0}\">{0}</a> to create the authorization bearer token.</html>", 54 ElevationPreferences.HGT_SERVER_REGISTRATION_URL)); 55 56 /** 57 * Builds the panel for the elevation preferences. 58 * 59 * @return Panel with elevation preferences 60 */ 61 protected final JPanel buildHgtPreferencesPanel() { 62 cbEnableElevation.setToolTipText(tr("STRM3 HGT files need to be placed in {0}", ElevationPreferences.DEFAULT_HGT_DIRECTORY.getAbsolutePath())); 63 cbEnableElevation.addItemListener(event -> updateEnabledState()); 64 65 lblpElevationData.setEditable(false); 66 lblpElevationData.addHyperlinkListener(event -> browseHyperlink(event)); 67 68 cbEnableElevationProfile.addItemListener(event -> updateEnabledState()); 69 70 cbEnableAutoDownload.setToolTipText(tr("STRM3 HGT files will be downloaded from {0}", ElevationPreferences.HGT_SERVER_BASE_URL)); 71 cbEnableAutoDownload.addItemListener(event -> updateEnabledState()); 72 73 lblAuthBearerNotes.setEditable(false); 74 lblAuthBearerNotes.addHyperlinkListener(event -> browseHyperlink(event)); 75 76 JPanel pnl = new AutoSizePanel(); 77 GridBagConstraints gc = new GridBagConstraints(); 78 79 gc.anchor = GridBagConstraints.LINE_START; 80 gc.insets = new Insets(5, 5, 0, 0); 81 gc.fill = GridBagConstraints.HORIZONTAL; 82 gc.gridwidth = 2; 83 gc.weightx = 1.0; 84 pnl.add(cbEnableElevation, gc); 85 86 gc.gridy = 1; 87 pnl.add(lblpElevationData, gc); 88 89 gc.gridy = 2; 90 pnl.add(cbEnableElevationProfile, gc); 91 92 gc.gridy = 3; 93 pnl.add(cbEnableAutoDownload, gc); 94 95 gc.gridy = 4; 96 gc.fill = GridBagConstraints.NONE; 97 gc.gridwidth = 1; 98 gc.weightx = 0.0; 99 pnl.add(lblAuthBearer, gc); 100 101 gc.gridx = 1; 102 gc.fill = GridBagConstraints.HORIZONTAL; 103 gc.weightx = 1.0; 104 pnl.add(tfAuthBearer, gc); 105 106 gc.gridy = 5; 107 gc.gridx = 0; 108 gc.gridwidth = 2; 109 gc.weightx = 1.0; 110 pnl.add(lblAuthBearerNotes, gc); 111 112 // add an extra spacer, otherwise the layout is broken 113 gc.gridy = 6; 114 gc.gridwidth = 2; 115 gc.fill = GridBagConstraints.BOTH; 116 gc.weighty = 1.0; 117 pnl.add(new JPanel(), gc); 118 return pnl; 119 } 120 121 122 /** 123 * Initializes the panel with the values from the preferences. 124 */ 125 public final void initFromPreferences() { 126 IPreferences pref = Config.getPref(); 127 128 cbEnableElevation.setSelected(pref.getBoolean(ElevationPreferences.ELEVATION_ENABLED, ElevationPreferences.DEFAULT_ELEVATION_ENABLED)); 129 cbEnableElevationProfile.setSelected(pref.getBoolean(ElevationPreferences.ELEVATION_PROFILE_ENABLED, ElevationPreferences.DEFAULT_ELEVATION_PROFILE_ENABLED)); 130 cbEnableAutoDownload.setSelected(pref.getBoolean(ElevationPreferences.ELEVATION_AUTO_DOWNLOAD_ENABLED, ElevationPreferences.DEFAULT_ELEVATION_AUTO_DOWNLOAD_ENABLED)); 131 tfAuthBearer.setText(pref.get(ElevationPreferences.ELEVATION_SERVER_AUTH_BEARER, ElevationPreferences.DEFAULT_ELEVATION_SERVER_AUTH_BEARER)); 132 } 133 134 private final void updateEnabledState() { 135 if (cbEnableElevation.isSelected()) { 136 lblpElevationData.setEnabled(true); 137 cbEnableElevationProfile.setEnabled(true); 138 cbEnableAutoDownload.setEnabled(true); 139 lblAuthBearer.setEnabled(cbEnableAutoDownload.isSelected()); 140 tfAuthBearer.setEnabled(cbEnableAutoDownload.isSelected()); 141 lblAuthBearerNotes.setEnabled(cbEnableAutoDownload.isSelected()); 142 } 143 else { 144 lblpElevationData.setEnabled(false); 145 cbEnableElevationProfile.setEnabled(false); 146 cbEnableAutoDownload.setEnabled(false); 147 lblAuthBearer.setEnabled(false); 148 tfAuthBearer.setEnabled(false); 149 lblAuthBearerNotes.setEnabled(false); 150 } 151 } 152 153 // https://stackoverflow.com/questions/14101000/hyperlink-to-open-in-browser-in-java 154 // https://www.codejava.net/java-se/swing/how-to-create-hyperlink-with-jlabel-in-java-swing 155 private final void browseHyperlink(HyperlinkEvent event) { 156 if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { 157 String url = event.getURL().toString(); 158 try { 159 Desktop.getDesktop().browse(URI.create(url)); 160 } catch (IOException e) { 161 Logging.error(e.toString()); 162 } 163 } 164 } 165 166 /** 167 * Constructs a new {@code HgtPreferencesPanel}. 168 */ 169 public ElevationPreferencesPanel() { 170 setLayout(new GridBagLayout()); 171 setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); 172 add(buildHgtPreferencesPanel(), GBC.eop().anchor(GridBagConstraints.NORTHWEST).fill(GridBagConstraints.BOTH)); 173 174 initFromPreferences(); 175 updateEnabledState(); 176 } 177 178 /** 179 * Saves the current values to the preferences 180 */ 181 public void saveToPreferences() { 182 IPreferences pref = Config.getPref(); 183 pref.putBoolean(ElevationPreferences.ELEVATION_ENABLED, cbEnableElevation.isSelected()); 184 pref.putBoolean(ElevationPreferences.ELEVATION_PROFILE_ENABLED, cbEnableElevationProfile.isSelected()); 185 pref.putBoolean(ElevationPreferences.ELEVATION_AUTO_DOWNLOAD_ENABLED, cbEnableAutoDownload.isSelected()); 186 pref.put(ElevationPreferences.ELEVATION_SERVER_AUTH_BEARER, tfAuthBearer.getText()); 187 } 188 } -
new file src/org/openstreetmap/josm/plugins/elevation/gui/LocalElevationLabel.java
diff --git a/src/org/openstreetmap/josm/plugins/elevation/gui/LocalElevationLabel.java b/src/org/openstreetmap/josm/plugins/elevation/gui/LocalElevationLabel.java new file mode 100644 index 0000000..6c56016
- + 1 package org.openstreetmap.josm.plugins.elevation.gui; 2 3 import static org.openstreetmap.josm.tools.I18n.tr; 4 5 import java.awt.event.MouseEvent; 6 import java.awt.event.MouseMotionListener; 7 import java.text.DecimalFormat; 8 9 import org.openstreetmap.josm.data.coor.ILatLon; 10 import org.openstreetmap.josm.gui.MapFrame; 11 import org.openstreetmap.josm.gui.MapStatus; 12 import org.openstreetmap.josm.gui.widgets.ImageLabel; 13 import org.openstreetmap.josm.plugins.elevation.ElevationHelper; 14 import org.openstreetmap.josm.plugins.elevation.HgtReader; 15 import org.openstreetmap.josm.tools.GBC; 16 17 public class LocalElevationLabel extends ImageLabel implements MouseMotionListener { 18 19 private final MapFrame mapFrame; 20 21 private final DecimalFormat ELEVATION_FORMAT = new DecimalFormat("0 m"); 22 23 public LocalElevationLabel(MapFrame mapFrame) { 24 super("ele", tr("The terrain elevation at the mouse pointer."), 10, MapStatus.PROP_BACKGROUND_COLOR.get()); 25 26 mapFrame.mapView.addMouseMotionListener(this); 27 28 setForeground(MapStatus.PROP_FOREGROUND_COLOR.get()); 29 // Add after the longitude ImageLabel at index = 2 30 // or at index 0 or 1, if index = 2 should be out of range 31 int index = Math.min(mapFrame.statusLine.getComponentCount(), 2); 32 mapFrame.statusLine.add(this, GBC.std().insets(3, 0, 0, 0), index); 33 34 this.mapFrame = mapFrame; 35 } 36 37 private void updateEleText(ILatLon coor) { 38 double ele = HgtReader.getInstance().getElevationFromHgt(coor); 39 if (ElevationHelper.isValidElevation(ele)) 40 setText(ELEVATION_FORMAT.format(ele)); 41 else 42 setText(tr("No data")); 43 } 44 45 public void destroy() { 46 mapFrame.mapView.removeMouseMotionListener(this); 47 } 48 49 @Override 50 public void mouseDragged(MouseEvent e) { 51 mouseMoved(e); 52 } 53 54 @Override 55 public void mouseMoved(MouseEvent e) { 56 if (mapFrame.mapView.getCenter() == null) 57 return; 58 // Do not update the view if ctrl or right button is pressed. 59 if ((e.getModifiersEx() & (MouseEvent.CTRL_DOWN_MASK | MouseEvent.BUTTON3_DOWN_MASK)) == 0) 60 updateEleText(mapFrame.mapView.getLatLon(e.getX(), e.getY())); 61 62 } 63 }
