Changeset 30737 in osm for applications/editors/josm/plugins/ElevationProfile/src/org/openstreetmap
- Timestamp:
- 2014-10-18T23:07:52+02:00 (10 years ago)
- Location:
- applications/editors/josm/plugins/ElevationProfile/src/org/openstreetmap/josm/plugins/elevation
- Files:
-
- 10 edited
Legend:
- Unmodified
- Added
- Removed
-
applications/editors/josm/plugins/ElevationProfile/src/org/openstreetmap/josm/plugins/elevation/ColorMap.java
r30344 r30737 16 16 */ 17 17 public class ColorMap { 18 19 20 18 private List<ColorMapEntry> colorList; 19 private String name; 20 private static HashMap<String, ColorMap> colorMaps; 21 21 22 23 colorMaps = new HashMap<String, ColorMap>();24 22 static { 23 colorMaps = new HashMap<>(); 24 } 25 25 26 27 28 26 // Private ctor to enforce use of create 27 private ColorMap() { 28 } 29 29 30 31 32 30 public String getName() { 31 return name; 32 } 33 33 34 35 36 34 public void setName(String name) { 35 this.name = name; 36 } 37 37 38 39 40 41 42 43 44 45 46 47 48 38 /** 39 * Gets the color according to the given elevation value. 40 * 41 * @param elevation the elevation 42 * @return the color 43 */ 44 public Color getColor(int elevation) { 45 // empty color map? 46 if (colorList == null || colorList.size() == 0) { 47 return Color.white; 48 } 49 49 50 51 52 53 50 // out of range? 51 if (elevation < colorList.get(0).ele) { 52 return colorList.get(0).getColor(); 53 } 54 54 55 56 57 58 55 int last = colorList.size() - 1; 56 if (elevation > colorList.get(last).ele) { 57 return colorList.get(last).getColor(); 58 } 59 59 60 61 62 63 60 // find elevation section 61 for (int i = 0; i < last; i++) { 62 ColorMapEntry e1 = colorList.get(i); 63 ColorMapEntry e2 = colorList.get(i + 1); 64 64 65 66 65 // elevation within range? 66 if (e1.getEle() <= elevation && e2.getEle() >= elevation) { 67 67 68 69 70 71 72 68 // interpolate color between both 69 double val = (elevation - e1.getEle()) / (double)(e2.getEle() - e1.getEle()); 70 return interpolate(e1.getColor(), e2.getColor(), val); 71 } 72 } 73 73 74 75 76 74 // here we should never end! 75 throw new RuntimeException("Inconsistent color map - found no entry for elevation " + elevation); 76 } 77 77 78 78 79 80 81 82 83 84 85 86 87 88 89 90 79 /** 80 * Gets the color map with the given name. 81 * 82 * @param name the name 83 * @return the map or <code>null</code>, if no such map exists 84 */ 85 public static ColorMap getMap(String name) { 86 if (colorMaps.containsKey(name)) { 87 return colorMaps.get(name); 88 } 89 return null; 90 } 91 91 92 93 94 95 96 97 98 99 92 /** 93 * Gets the number of available color maps. 94 * 95 * @return the int 96 */ 97 public static int size() { 98 return colorMaps != null ? colorMaps.size() : 0; 99 } 100 100 101 101 102 103 104 105 106 107 108 109 110 102 /** 103 * Gets the available color map names. 104 * 105 * @param name the name 106 * @return the map or <code>null</code>, if no such map exists 107 */ 108 public static String[] getNames() { 109 return colorMaps.keySet().toArray(new String[size()]); 110 } 111 111 112 113 114 115 112 private static void registerColorMap(ColorMap newMap) { 113 CheckParameterUtil.ensureParameterNotNull(newMap); 114 colorMaps.put(newMap.getName(), newMap); 115 } 116 116 117 118 119 120 121 117 public static void unregisterColorMap(String name) { 118 if (colorMaps.containsKey(name)) { 119 colorMaps.remove(name); 120 } 121 } 122 122 123 124 125 126 127 128 123 public static Color interpolate(java.awt.Color c1, java.awt.Color c2, double ratio) { 124 double r1 = 1 -ratio; 125 // clip 126 if (r1 < 0) r1 = 0d; 127 if (r1 > 1) r1 = 1d; 128 double r2 = 1 - r1; 129 129 130 131 132 133 134 130 int r = (int) Math.round((r1 * c1.getRed()) + (r2 * c2.getRed())); 131 int g = (int) Math.round((r1 * c1.getGreen()) + (r2 * c2.getGreen())); 132 int b = (int) Math.round((r1 * c1.getBlue()) + (r2 * c2.getBlue())); 133 return new Color(r, g, b); 134 } 135 135 136 137 138 139 140 141 142 143 144 145 146 147 136 /** 137 * Creates a color map using the given colors/elevation values. 138 * Both arrays must have same length. 139 * 140 * @param name the name of the color map 141 * @param colors the array containing the colors 142 * @param ele the elevation values 143 * @return the color map 144 */ 145 public static ColorMap create(String name, Color[] colors, int[] ele) { 146 CheckParameterUtil.ensureParameterNotNull(colors); 147 CheckParameterUtil.ensureParameterNotNull(ele); 148 148 149 150 151 149 if (colors.length != ele.length) { 150 throw new IllegalArgumentException("Arrays colors and ele must have same length: " + colors.length + " vs " + ele.length); 151 } 152 152 153 154 map.colorList = new ArrayList<ColorMap.ColorMapEntry>();155 156 157 158 153 ColorMap map = new ColorMap(); 154 map.colorList = new ArrayList<>(); 155 map.name = name; 156 for (int i = 0; i < ele.length; i++) { 157 map.colorList.add(map.new ColorMapEntry(colors[i], ele[i])); 158 } 159 159 160 161 160 // sort by elevation 161 Collections.sort(map.colorList); 162 162 163 164 165 163 registerColorMap(map); 164 return map; 165 } 166 166 167 167 168 169 170 168 class ColorMapEntry implements Comparable<ColorMapEntry> { 169 private final int ele; // limit 170 private final Color color; 171 171 172 173 174 175 176 172 public ColorMapEntry(Color color, int ele) { 173 super(); 174 this.color = color; 175 this.ele = ele; 176 } 177 177 178 179 180 178 public int getEle() { 179 return ele; 180 } 181 181 182 183 184 182 public Color getColor() { 183 return color; 184 } 185 185 186 187 188 189 190 186 @Override 187 public int compareTo(ColorMapEntry o) { 188 return this.ele - o.ele; 189 } 190 } 191 191 } -
applications/editors/josm/plugins/ElevationProfile/src/org/openstreetmap/josm/plugins/elevation/ElevationHelper.java
r30344 r30737 19 19 */ 20 20 public class ElevationHelper { 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 * 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 * 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 21 public static double METER_TO_FEET = 3.280948; 22 23 /* Countries which use the imperial system instead of the metric system. */ 24 private static String IMPERIAL_SYSTEM_COUNTRIES[] = { 25 "en_US", /* USA */ 26 "en_CA", /* Canada */ 27 "en_AU", /* Australia */ 28 "en_NZ", /* New Zealand */ 29 // "de_DE", /* for testing only */ 30 "en_ZA" /* South Africa */ 31 }; 32 33 /** The 'no elevation' data magic. */ 34 public static double NO_ELEVATION = Double.NaN; 35 36 /** 37 * The name of the elevation height of a way point. 38 */ 39 public static final String HEIGHT_ATTRIBUTE = "ele"; 40 41 private static UnitMode unitMode = UnitMode.NotSelected; 42 43 private static GeoidCorrectionKind geoidKind = GeoidCorrectionKind.None; 44 45 /** The HGT reader instance. */ 46 private static HgtReader hgt = new HgtReader(); 47 48 /** 49 * Gets the current mode of GEOID correction. 50 * @return 51 */ 52 public static GeoidCorrectionKind getGeoidKind() { 53 return geoidKind; 54 } 55 56 public static void setGeoidKind(GeoidCorrectionKind geoidKind) { 57 ElevationHelper.geoidKind = geoidKind; 58 } 59 60 /** 61 * Gets the current unit mode (metric or imperial). 62 * @return 63 */ 64 public static UnitMode getUnitMode() { 65 //TODO: Use this until /JOSM/src/org/openstreetmap/josm/gui/NavigatableComponent.java 66 // has a an appropriate method 67 68 // unit mode already determined? 69 if (unitMode != UnitMode.NotSelected) { 70 return unitMode; 71 } 72 73 // Set default 74 unitMode = UnitMode.Metric; 75 76 // Check if user could prefer imperial system 77 Locale l = Locale.getDefault(); 78 for (int i = 0; i < IMPERIAL_SYSTEM_COUNTRIES.length; i++) { 79 String ctry = l.toString(); 80 if (IMPERIAL_SYSTEM_COUNTRIES[i].equals(ctry)) { 81 unitMode = UnitMode.Imperial; 82 } 83 } 84 85 return unitMode; 86 } 87 88 /** 89 * Gets the unit string for elevation ("m" or "ft"). 90 * @return 91 */ 92 public static String getUnit() { 93 switch (getUnitMode()) { 94 case Metric: 95 return "m"; 96 case Imperial: 97 return "ft"; 98 default: 99 throw new RuntimeException("Invalid or unsupported unit mode: " + unitMode); 100 } 101 } 102 103 /** 104 * Checks if given value is a valid elevation value. 105 * 106 * @param ele the ele 107 * @return true, if is valid elevation 108 */ 109 public static boolean isValidElevation(double ele) { 110 return !Double.isNaN(ele); 111 } 112 113 /** 114 * Gets the elevation (Z coordinate) of a GPX way point in meter or feet (for 115 * US, UK, ZA, AU, NZ and CA). 116 * 117 * @param wpt 118 * The way point instance. 119 * @return The x coordinate or <code>NO_ELEVATION</code>, if the given way point is null or contains 120 * not height attribute. 121 */ 122 public static double getElevation(WayPoint wpt) { 123 if (wpt == null) return NO_ELEVATION; 124 125 // try to get elevation from HGT file 126 double eleInt = getSrtmElevation(wpt.getCoor()); 127 if (isValidElevation(eleInt)) { 128 return convert(eleInt); 129 } 130 131 // no HGT, check for elevation data in GPX 132 if (!wpt.attr.containsKey(HEIGHT_ATTRIBUTE)) { 133 // GPX has no elevation data :-( 134 return NO_ELEVATION; 135 } 136 137 // Parse elevation from GPX data 138 String height = wpt.getString(ElevationHelper.HEIGHT_ATTRIBUTE); 139 try { 140 double z = Double.parseDouble(height); 141 142 return convert(z); 143 } catch (NumberFormatException e) { 144 System.err.println(String.format( 145 "Cannot parse double from '%s': %s", height, e 146 .getMessage())); 147 return NO_ELEVATION; 148 } 149 } 150 151 152 private static double getElevation(LatLon ll) { 153 double ele = getSrtmElevation(ll); 154 //System.out.println("Get elevation " + ll + " => " + ele); 155 return convert(ele); 156 } 157 158 /** 159 * Converts the value to feet, if required. 160 * 161 * @param ele the elevation to convert 162 * @return the double 163 */ 164 private static double convert(double ele) { 165 if (isValidElevation(ele)) { 166 if (getUnitMode() == UnitMode.Imperial) { 167 // translate to feet 168 return meter2Feet(ele); 169 } else { 170 // keep 'as is' 171 return ele; 172 } 173 } 174 return NO_ELEVATION; 175 } 176 177 /** 178 * Computes the slope <b>in percent</b> between two way points. E. g. an elevation gain of 12m 179 * within a distance of 100m is equal to a slope of 12%. 180 * 181 * @param w1 the first way point 182 * @param w2 the second way point 183 * @return the slope in percent 184 */ 185 public static double computeSlope(LatLon w1, LatLon w2) { 186 // same coordinates? -> return 0, if yes 187 if (w1.equals(w2)) return 0; 188 189 // get distance in meters and divide it by 100 in advance 190 double distInMeter = convert(w1.greatCircleDistance(w2) / 100.0); 191 192 // get elevation (difference) - is converted automatically to feet 193 int ele1 = (int) ElevationHelper.getElevation(w1); 194 int ele2 = (int) ElevationHelper.getElevation(w2); 195 int dH = ele2 - ele1; 196 197 // Slope in percent is define as elevation gain/loss in meters related to a distance of 100m 198 return dH / distInMeter; 199 } 200 201 /** 202 * Converts meter into feet 203 * 204 * @param meter the meter 205 * @return the double 206 */ 207 public static double meter2Feet(double meter) { 208 return meter * METER_TO_FEET; 209 } 210 211 /** 212 * Gets the elevation string for a given elevation, e. g "300m" or "800ft". 213 * @param elevation 214 * @return 215 */ 216 public static String getElevationText(int elevation) { 217 return String.format("%d %s", elevation, getUnit()); 218 } 219 220 /** 221 * Gets the elevation string for a given elevation, e. g "300m" or "800ft". 222 * @param elevation 223 * @return 224 */ 225 public static String getElevationText(double elevation) { 226 return String.format("%d %s", (int)Math.round(elevation), getUnit()); 227 } 228 229 /** 230 * Gets the elevation string for a given way point, e. g "300m" or "800ft". 231 * 232 * @param wpt the way point 233 * @return the elevation text 234 */ 235 public static String getElevationText(WayPoint wpt) { 236 if (wpt == null) return "-"; 237 238 int elevation = (int)Math.round(ElevationHelper.getElevation(wpt)); 239 return String.format("%d %s", elevation, getUnit()); 240 } 241 242 /** 243 * Get the time string for a given way point. 244 * @param wpt 245 * @return 246 */ 247 public static String getTimeText(WayPoint wpt) { 248 if (wpt == null) return null; 249 250 int hour = ElevationHelper.getHourOfWayPoint(wpt); 251 int min = ElevationHelper.getMinuteOfWayPoint(wpt); 252 return String.format("%02d:%02d", hour, min); 253 } 254 255 /** 256 * Gets the SRTM elevation (Z coordinate) of the given coordinate. 257 * 258 * @param ll 259 * The coordinate. 260 * @return The z coordinate or {@link Double#NaN}, if elevation value could not be obtained 261 * not height attribute. 262 */ 263 public static double getSrtmElevation(LatLon ll) { 264 if (ll != null) { 265 // Try to read data from SRTM file 266 // TODO: Option to switch this off 267 double eleHgt = hgt.getElevationFromHgt(ll); 268 269 //System.out.println("Get elevation from HGT " + ll + " => " + eleHgt); 270 if (isValidElevation(eleHgt)) { 271 return eleHgt; 272 } 273 } 274 return NO_ELEVATION; 275 } 276 277 /** 278 * Checks given area for SRTM data. 279 * 280 * @param bounds the bounds/area to check 281 * @return true, if SRTM data are present; otherwise false 282 */ 283 public static boolean hasSrtmData(Bounds bounds) { 284 if (bounds == null) return false; 285 286 LatLon tl = bounds.getMin(); 287 LatLon br = bounds.getMax(); 288 289 return isValidElevation(getSrtmElevation(tl)) && 290 isValidElevation(getSrtmElevation(br)); 291 } 292 293 /* 294 * Gets the geoid height for the given way point. See also {@link 295 * GeoidData}. 296 */ 297 public static byte getGeoidCorrection(WayPoint wpt) { 298 /* 299 299 int lat = (int)Math.round(wpt.getCoor().lat()); 300 300 int lon = (int)Math.round(wpt.getCoor().lon()); … … 303 303 System.out.println( 304 304 String.format("Geoid(%d, %d) = %d", lat, lon, geoid)); 305 306 307 308 309 310 311 * 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 List<WayPoint> res = new ArrayList<WayPoint>(targetSize);336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 305 */ 306 return 0; 307 } 308 309 /** 310 * Reduces a given list of way points to the specified target size. 311 * 312 * @param origList 313 * The original list containing the way points. 314 * @param targetSize 315 * The desired target size of the list. The resulting list may 316 * contain fewer items, so targetSize should be considered as 317 * maximum. 318 * @return A list containing the reduced list. 319 */ 320 public static List<WayPoint> downsampleWayPoints(List<WayPoint> origList, 321 int targetSize) { 322 if (origList == null) 323 return null; 324 if (targetSize <= 0) 325 throw new IllegalArgumentException( 326 "targetSize must be greater than zero"); 327 328 int origSize = origList.size(); 329 if (origSize <= targetSize) { 330 return origList; 331 } 332 333 int delta = (int) Math.max(Math.ceil(origSize / targetSize), 2); 334 335 List<WayPoint> res = new ArrayList<>(targetSize); 336 for (int i = 0; i < origSize; i += delta) { 337 res.add(origList.get(i)); 338 } 339 340 return res; 341 } 342 343 /** 344 * Gets the hour value of a way point in 24h format. 345 * @param wpt 346 * @return 347 */ 348 public static int getHourOfWayPoint(WayPoint wpt) { 349 if (wpt == null) return -1; 350 351 Calendar calendar = GregorianCalendar.getInstance(); // creates a new calendar instance 352 calendar.setTime(wpt.getTime()); // assigns calendar to given date 353 return calendar.get(Calendar.HOUR_OF_DAY); 354 } 355 356 /** 357 * Gets the minute value of a way point in 24h format. 358 * @param wpt 359 * @return 360 */ 361 public static int getMinuteOfWayPoint(WayPoint wpt) { 362 if (wpt == null) return -1; 363 364 Calendar calendar = GregorianCalendar.getInstance(); // creates a new calendar instance 365 calendar.setTime(wpt.getTime()); // assigns calendar to given date 366 return calendar.get(Calendar.MINUTE); 367 } 368 368 } -
applications/editors/josm/plugins/ElevationProfile/src/org/openstreetmap/josm/plugins/elevation/HgtReader.java
r30344 r30737 17 17 /** 18 18 * Class HgtReader reads data from SRTM HGT files. Currently this class is restricted to a resolution of 3 arc seconds. 19 * 19 * 20 20 * SRTM data files are available at the <a href="http://dds.cr.usgs.gov/srtm/version2_1/SRTM3">NASA SRTM site</a> 21 21 * @author Oliver Wieland <oliver.wieland@online.de> 22 22 */ 23 23 public class HgtReader { 24 24 private static final int SECONDS_PER_MINUTE = 60; 25 25 26 26 public static final String HGT_EXT = ".hgt"; 27 27 28 29 30 31 28 // alter these values for different SRTM resolutions 29 public static final int HGT_RES = 3; // resolution in arc seconds 30 public static final int HGT_ROW_LENGTH = 1201; // number of elevation values per line 31 public static final int HGT_VOID = -32768; // magic number which indicates 'void data' in HGT file 32 32 33 private final HashMap<String, ShortBuffer> cache = new HashMap<String, ShortBuffer>();33 private final HashMap<String, ShortBuffer> cache = new HashMap<>(); 34 34 35 36 37 38 39 35 public double getElevationFromHgt(LatLon coor) { 36 try { 37 String file = getHgtFileName(coor); 38 // given area in cache? 39 if (!cache.containsKey(file)) { 40 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 41 // fill initial cache value. If no file is found, then 42 // we use it as a marker to indicate 'file has been searched 43 // but is not there' 44 cache.put(file, null); 45 // Try all resource directories 46 for (String location : Main.pref.getAllPossiblePreferenceDirs()) { 47 String fullPath = new File(location + File.separator + "elevation", file).getPath(); 48 File f = new File(fullPath); 49 if (f.exists()) { 50 // found something: read HGT file... 51 ShortBuffer data = readHgtFile(fullPath); 52 // ... and store result in cache 53 cache.put(file, data); 54 break; 55 } 56 } 57 } 58 58 59 60 61 62 63 64 65 66 67 68 69 70 71 59 // read elevation value 60 return readElevation(coor); 61 } catch (FileNotFoundException e) { 62 System.err.println("Get elevation from HGT " + coor + " failed: => " + e.getMessage()); 63 // no problem... file not there 64 return ElevationHelper.NO_ELEVATION; 65 } catch (Exception ioe) { 66 // oops... 67 ioe.printStackTrace(System.err); 68 // fallback 69 return ElevationHelper.NO_ELEVATION; 70 } 71 } 72 72 73 74 75 73 @SuppressWarnings("resource") 74 private ShortBuffer readHgtFile(String file) throws Exception { 75 CheckParameterUtil.ensureParameterNotNull(file); 76 76 77 78 79 80 81 82 77 FileChannel fc = null; 78 ShortBuffer sb = null; 79 try { 80 // Eclipse complains here about resource leak on 'fc' - even with 'finally' clause??? 81 fc = new FileInputStream(file).getChannel(); 82 // choose the right endianness 83 83 84 85 84 ByteBuffer bb = ByteBuffer.allocateDirect((int) fc.size()); 85 while (bb.remaining() > 0) fc.read(bb); 86 86 87 88 89 90 91 92 87 bb.flip(); 88 //sb = bb.order(ByteOrder.LITTLE_ENDIAN).asShortBuffer(); 89 sb = bb.order(ByteOrder.BIG_ENDIAN).asShortBuffer(); 90 } finally { 91 if (fc != null) fc.close(); 92 } 93 93 94 95 94 return sb; 95 } 96 96 97 98 99 100 101 102 103 104 105 97 /** 98 * Reads the elevation value for the given coordinate. 99 * 100 * See also <a href="http://gis.stackexchange.com/questions/43743/how-to-extract-elevation-from-hgt-file">stackexchange.com</a> 101 * @param coor the coordinate to get the elevation data for 102 * @return the elevation value or <code>Double.NaN</code>, if no value is present 103 */ 104 public double readElevation(LatLon coor) { 105 String tag = getHgtFileName(coor); 106 106 107 107 ShortBuffer sb = cache.get(tag); 108 108 109 110 111 109 if (sb == null) { 110 return ElevationHelper.NO_ELEVATION; 111 } 112 112 113 114 115 113 // see http://gis.stackexchange.com/questions/43743/how-to-extract-elevation-from-hgt-file 114 double fLat = frac(coor.lat()) * SECONDS_PER_MINUTE; 115 double fLon = frac(coor.lon()) * SECONDS_PER_MINUTE; 116 116 117 118 119 117 // compute offset within HGT file 118 int row = (int)Math.round(fLat * SECONDS_PER_MINUTE / HGT_RES); 119 int col = (int)Math.round(fLon * SECONDS_PER_MINUTE / HGT_RES); 120 120 121 122 121 row = HGT_ROW_LENGTH - row; 122 int cell = (HGT_ROW_LENGTH* (row - 1)) + col; 123 123 124 124 //System.out.println("Read SRTM elevation data from row/col/cell " + row + "," + col + ", " + cell + ", " + sb.limit()); 125 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 126 // valid position in buffer? 127 if (cell < sb.limit()) { 128 short ele = sb.get(cell); 129 //System.out.println("==> Read SRTM elevation data from row/col/cell " + row + "," + col + ", " + cell + " = " + ele); 130 // check for data voids 131 if (ele == HGT_VOID) { 132 return ElevationHelper.NO_ELEVATION; 133 } else { 134 return ele; 135 } 136 } else { 137 return ElevationHelper.NO_ELEVATION; 138 } 139 } 140 140 141 142 143 144 145 146 147 148 149 150 151 141 /** 142 * Gets the associated HGT file name for the given way point. Usually the 143 * format is <tt>[N|S]nn[W|E]mmm.hgt</tt> where <i>nn</i> is the integral latitude 144 * without decimals and <i>mmm</i> is the longitude. 145 * 146 * @param latLon the coordinate to get the filename for 147 * @return the file name of the HGT file 148 */ 149 public String getHgtFileName(LatLon latLon) { 150 int lat = (int) latLon.lat(); 151 int lon = (int) latLon.lon(); 152 152 153 154 153 String latPref = "N"; 154 if (lat < 0) latPref = "S"; 155 155 156 157 158 159 156 String lonPref = "E"; 157 if (lon < 0) { 158 lonPref = "W"; 159 } 160 160 161 162 161 return String.format("%s%02d%s%03d%s", latPref, lat, lonPref, lon, HGT_EXT); 162 } 163 163 164 165 166 164 public static double frac(double d) { 165 long iPart; 166 double fPart; 167 167 168 169 170 171 172 168 // Get user input 169 iPart = (long) d; 170 fPart = d - iPart; 171 return fPart; 172 } 173 173 } -
applications/editors/josm/plugins/ElevationProfile/src/org/openstreetmap/josm/plugins/elevation/gpx/ElevationModel.java
r30344 r30737 19 19 * Represents the top-level part of the elevation model. The elevation model 20 20 * breaks done into the tracks/routes of a GPX file. 21 * 21 * 22 22 * @see IElevationModelTrackListener 23 23 * @author Oliver Wieland <oliver.wieland@online.de> 24 24 */ 25 25 public class ElevationModel implements IGpxVisitor, IElevationModel { 26 27 28 29 30 31 private final List<IElevationModelListener> listeners = new ArrayList<IElevationModelListener>();32 private final List<WayPoint> buffer = new ArrayList<WayPoint>();33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 * 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 26 // private int sliceSize; 27 private int trackCounter; 28 private final GpxData gpxData; 29 private final String name; 30 private final WayPointMap profiles = new WayPointMap(); 31 private final List<IElevationModelListener> listeners = new ArrayList<>(); 32 private final List<WayPoint> buffer = new ArrayList<>(); 33 private int currentProfileIndex = 0; 34 private ElevationProfile curProfile = null; 35 36 /** 37 * Instantiates a new elevation model. 38 */ 39 public ElevationModel() { 40 this("", null); 41 } 42 43 /** 44 * Instantiates a new elevation model. 45 * 46 * @param name the name of the model 47 * @param data the GPX data 48 */ 49 public ElevationModel(String name, GpxData data) { 50 gpxData = data; 51 this.name = name; 52 GpxIterator.visit(data, this); 53 } 54 55 /** 56 * Gets the GPX data instance used by this model. 57 * 58 * @return 59 */ 60 public GpxData getGpxData() { 61 return gpxData; 62 } 63 64 /** 65 * @return the tracks 66 */ 67 protected WayPointMap getTracks() { 68 return profiles; 69 } 70 71 /** 72 * Fires the 'model changed' event to all listeners. 73 */ 74 protected void fireModelChanged() { 75 for (IElevationModelListener listener : listeners) { 76 if (profiles != null && profiles.size() > 0) 77 listener.elevationProfileChanged(getCurrentProfile()); 78 } 79 } 80 81 @Override 82 public void addModelListener(IElevationModelListener listener) { 83 this.listeners.add(listener); 84 } 85 86 @Override 87 public void removeModelListener(IElevationModelListener listener) { 88 this.listeners.remove(listener); 89 } 90 91 @Override 92 public void removeAllListeners() { 93 this.listeners.clear(); 94 } 95 96 @Override 97 public List<IElevationProfile> getProfiles() { 98 return profiles; 99 } 100 101 @Override 102 public IElevationProfile getCurrentProfile() { 103 if (currentProfileIndex < 0 || currentProfileIndex >= profileCount()) return null; 104 105 return profiles.get(currentProfileIndex); 106 } 107 108 @Override 109 public void setCurrentProfile(IElevationProfile newProfile) { 110 CheckParameterUtil.ensureParameterNotNull(newProfile); 111 112 if (!profiles.contains(newProfile)) { 113 profiles.add(newProfile); 114 } 115 116 setCurrentProfile(profiles.indexOf(newProfile)); 117 } 118 119 @Override 120 public void setCurrentProfile(int index) { 121 if (index < 0 || index >= profileCount()) throw new RuntimeException("Invalid arg for setCurrentProfile: " + index + ", value must be 0.." + profileCount()); 122 123 currentProfileIndex = index; 124 fireModelChanged(); 125 } 126 127 @Override 128 public int profileCount() { 129 return profiles != null ? profiles.size() : 0; 130 } 131 132 // Visitor stuff starts here... 133 134 @Override 135 public void beginWayPoints() { 136 // we ignore single way points (elevation profile is quite meaningless...) 137 } 138 139 @Override 140 public void endWayPoints() { 141 // we ignore single way points (elevation profile is quite meaningless...) 142 } 143 144 @Override 145 public void visitWayPoint(WayPoint wp) { 146 // we ignore single way points (elevation profile is quite meaningless...) 147 } 148 149 150 @Override 151 public void beginTrack(GpxTrack track) { 152 createProfile(track); 153 } 154 155 @Override 156 public void endTrack(GpxTrack track) { 157 if (curProfile == null) throw new RuntimeException("Internal error: No elevation profile"); 158 159 curProfile.setDistance(track.length()); 160 commitProfile(); 161 } 162 163 @Override 164 public void beginTrackSegment(GpxTrack track, GpxTrackSegment segment) { 165 // Nothing to do here for now 166 } 167 168 @Override 169 public void endTrackSegment(GpxTrack track, GpxTrackSegment segment) { 170 // Nothing to do here for now 171 } 172 173 @Override 174 public void visitTrackPoint(WayPoint wp, GpxTrack track, 175 GpxTrackSegment segment) { 176 177 processWayPoint(wp); 178 } 179 180 @Override 181 public void beginRoute(GpxRoute route) { 182 createProfile(route); 183 } 184 185 @Override 186 public void endRoute(GpxRoute route) { 187 if (curProfile == null) throw new RuntimeException("Internal error: No elevation profile"); 188 // a GpxRoute has no 'length' property 189 curProfile.setDistance(0); 190 commitProfile(); 191 } 192 193 @Override 194 public void visitRoutePoint(WayPoint wp, GpxRoute route) { 195 processWayPoint(wp); 196 } 197 198 /** 199 * Creates a new profile. 200 * 201 * @param trackOrRoute the track or route 202 */ 203 private void createProfile(IWithAttributes trackOrRoute) { 204 // check GPX data 205 String trackName = (String) trackOrRoute.get("name"); 206 207 if (trackName == null) { 208 trackName = (String) trackOrRoute.get(GpxData.META_NAME); 209 if (trackName == null) { 210 // no name given, build artificial one 211 trackName = name + "." + trackCounter; 212 } 213 } 214 215 curProfile = new ElevationProfile(trackName); 216 } 217 218 /** 219 * Adds a track or route to the internal track list. 220 * 221 * @param trackName the track name 222 */ 223 private void commitProfile() { 224 if (buffer.size() > 0) { 225 // assign way points to profile... 226 curProfile.setWayPoints(buffer); 227 // ... and add to profile list 228 profiles.add(curProfile); 229 buffer.clear(); 230 } 231 } 232 233 /** 234 * Adds the given way point to the current buffer. 235 * 236 * @param wp the wp 237 */ 238 private void processWayPoint(WayPoint wp) { 239 if (wp == null) { 240 throw new RuntimeException("WPT must not be null!"); 241 } 242 243 buffer.add(wp); 244 } 245 245 } -
applications/editors/josm/plugins/ElevationProfile/src/org/openstreetmap/josm/plugins/elevation/gpx/ElevationProfile.java
r30344 r30737 17 17 * full way point set and then reduces the number of way points to a given 18 18 * amount, if necessary. 19 * 19 * 20 20 * The computation is done via implementing {@link IGpxWaypointVisitor}, 21 21 * subclasses may override the {@link ElevationProfile#visitWayPoint(WayPoint)} 22 22 * method to compute own values or run specific actions. The computation is 23 23 * triggered by calling {@link ElevationProfile#updateValues()}. 24 * 24 * 25 25 * Elevation profiles can break down into further child profiles. This is 26 26 * intended to show different levels of details, if the number of way points 27 27 * exceed the display space (which is usually the case). 28 * 28 * 29 29 * {@link IElevationProfile} {@link IGpxWaypointVisitor} {@link GpxIterator} 30 * 30 * 31 31 * @author Oliver Wieland <oliver.wieland@online.de> 32 * 32 * 33 33 */ 34 34 public class ElevationProfile implements IElevationProfile, 35 35 IGpxWaypointVisitor { 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 * 62 63 64 65 66 67 68 69 70 * 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 * 220 221 222 223 224 this.wayPoints = new ArrayList<WayPoint>(wayPoints);225 226 227 228 229 230 231 232 233 * 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 * 281 282 283 284 285 286 287 288 289 290 * 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 * 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 36 public static final int WAYPOINT_START = 0; 37 public static final int WAYPOINT_END = 1; 38 public static final int WAYPOINT_MIN = 2; 39 public static final int WAYPOINT_MAX = 3; 40 41 private String name; 42 private int minHeight; 43 private int maxHeight; 44 private int avrgHeight; 45 private double dist; 46 private Date start = new Date(); 47 private Date end = new Date(); 48 private final WayPoint[] importantWayPoints = new WayPoint[4]; 49 private IElevationProfile parent; 50 private int sumEle; // temp var for average height 51 private List<WayPoint> wayPoints; 52 private int numWayPoints; // cached value 53 private int gain; 54 private int lastEle; 55 private Bounds bounds; 56 57 private static boolean ignoreZeroHeight = true; 58 59 /** 60 * Creates a name elevation profile without any way points. 61 * 62 * @param name 63 */ 64 public ElevationProfile(String name) { 65 this(name, null, null, 0); 66 } 67 68 /** 69 * Creates a name elevation profile with a given set of way points. 70 * 71 * @param name 72 * The name of the profile. 73 * @param parent 74 * The (optional) parent profile. 75 * @param wayPoints 76 * The list containing the way points of the profile. 77 * @param sliceSize 78 * The requested target size of the profile. 79 */ 80 public ElevationProfile(String name, IElevationProfile parent, 81 List<WayPoint> wayPoints, int sliceSize) { 82 super(); 83 this.name = name; 84 this.parent = parent; 85 86 setWayPoints(wayPoints); 87 } 88 89 /** 90 * Checks if zero elevation should be ignored or not. 91 * 92 * @return true, if is ignore zero height 93 */ 94 public static boolean isIgnoreZeroHeight() { 95 return ignoreZeroHeight; 96 } 97 98 /** 99 * Sets the ignore zero height. 100 * 101 * @param ignoreZeroHeight the new ignore zero height 102 */ 103 public static void setIgnoreZeroHeight(boolean ignoreZeroHeight) { 104 ElevationProfile.ignoreZeroHeight = ignoreZeroHeight; 105 } 106 107 @Override 108 public void updateElevationData() { 109 updateValues(); 110 } 111 112 /** 113 * Revisits all way points and recomputes the characteristic values like 114 * min/max elevation. 115 */ 116 protected void updateValues() { 117 if (wayPoints == null) 118 return; 119 120 int n = this.wayPoints.size(); 121 if (n == 0) 122 return; 123 124 start = new Date(); 125 end = new Date(0L); 126 this.minHeight = Integer.MAX_VALUE; 127 this.maxHeight = Integer.MIN_VALUE; 128 sumEle = 0; 129 gain = 0; 130 lastEle = 0; 131 132 for (WayPoint wayPoint : this.wayPoints) { 133 visitWayPoint(wayPoint); 134 } 135 136 if (this.minHeight == Integer.MAX_VALUE && this.maxHeight == Integer.MIN_VALUE) { 137 // file does not contain elevation data at all 138 minHeight = 0; 139 maxHeight = 0; 140 setMinWayPoint(wayPoints.get(0)); 141 setMaxWayPoint(wayPoints.get(n-1)); 142 } 143 144 //if (start.after(end) || start.equals(end)) { 145 // GPX does not contain time stamps -> use sequential order 146 setStart(wayPoints.get(0)); 147 setEnd(wayPoints.get(n-1)); 148 //} 149 150 avrgHeight = sumEle / n; 151 } 152 153 /** 154 * Gets the name of the profile. 155 */ 156 @Override 157 public String getName() { 158 return name; 159 } 160 161 /** 162 * Sets the name of the profile. 163 * @param name The new name of the profile. 164 */ 165 public void setName(String name) { 166 this.name = name; 167 } 168 169 /** 170 * Sets the way point with the lowest elevation. 171 * @param wp The way point instance having the lowest elevation. 172 */ 173 protected void setMinWayPoint(WayPoint wp) { 174 importantWayPoints[WAYPOINT_MIN] = wp; 175 this.minHeight = (int) ElevationHelper.getElevation(wp); 176 } 177 178 /** 179 * Sets the way point with the highest elevation. 180 * @param wp The way point instance having the highest elevation. 181 */ 182 protected void setMaxWayPoint(WayPoint wp) { 183 importantWayPoints[WAYPOINT_MAX] = wp; 184 this.maxHeight = (int) ElevationHelper.getElevation(wp); 185 } 186 187 /** 188 * Sets the average height. 189 * @param avrgHeight 190 */ 191 protected void setAvrgHeight(int avrgHeight) { 192 this.avrgHeight = avrgHeight; 193 } 194 195 /** 196 * Sets the very first way point. 197 * @param wp 198 */ 199 protected void setStart(WayPoint wp) { 200 importantWayPoints[WAYPOINT_START] = wp; 201 this.start = wp.getTime(); 202 } 203 204 /** 205 * Sets the very last way point. 206 * @param wp 207 */ 208 protected void setEnd(WayPoint wp) { 209 importantWayPoints[WAYPOINT_END] = wp; 210 this.end = wp.getTime(); 211 } 212 213 public void setParent(IElevationProfile parent) { 214 this.parent = parent; 215 } 216 217 /** 218 * Sets the way points of this profile. 219 * 220 * @param wayPoints 221 */ 222 public void setWayPoints(List<WayPoint> wayPoints) { 223 if (this.wayPoints != wayPoints) { 224 this.wayPoints = new ArrayList<>(wayPoints); 225 numWayPoints = wayPoints != null ? wayPoints.size() : 0; 226 updateValues(); 227 228 } 229 } 230 231 /** 232 * Checks if the given index is valid or not. 233 * 234 * @param index 235 * The index to check. 236 * @return true, if the given index is valid; otherwise false. 237 */ 238 protected boolean checkIndex(int index) { 239 return index >= 0 && index < getNumberOfWayPoints(); 240 } 241 242 @Override 243 public int elevationValueAt(int i) { 244 if (checkIndex(i)) { 245 return (int) ElevationHelper.getElevation(wayPoints.get(i)); 246 } else { 247 throw new IndexOutOfBoundsException(String.format( 248 "Invalid index: %d, expected 0..%d", i, 249 getNumberOfWayPoints())); 250 } 251 } 252 253 @Override 254 public int getAverageHeight() { 255 return avrgHeight; 256 } 257 258 @Override 259 public List<IElevationProfile> getChildren() { 260 return null; 261 } 262 263 @Override 264 public Date getEnd() { 265 return end; 266 } 267 268 @Override 269 public int getMaxHeight() { 270 return maxHeight; 271 } 272 273 @Override 274 public int getMinHeight() { 275 return minHeight; 276 } 277 278 /** 279 * Gets the difference between min and max elevation. 280 * 281 * @return 282 */ 283 @Override 284 public int getHeightDifference() { 285 return maxHeight - minHeight; 286 } 287 288 /** 289 * Gets the elevation gain. 290 * 291 * @return 292 */ 293 @Override 294 public int getGain() { 295 return gain; 296 } 297 298 @Override 299 public double getDistance() { 300 return dist; // dist is in meters 301 } 302 303 /** 304 * Sets the distance of the elevation profile. 305 * @param dist 306 */ 307 protected void setDistance(double dist) { 308 this.dist = dist; 309 } 310 311 /** 312 * Returns the time between start and end of the track. 313 * @return 314 */ 315 @Override 316 public long getTimeDifference() { 317 WayPoint wp1 = getStartWayPoint(); 318 WayPoint wp2 = getEndWayPoint(); 319 320 if (wp1 != null && wp2 != null) { 321 long diff = wp2.getTime().getTime() - wp1.getTime().getTime(); 322 return diff; 323 } 324 325 return 0L; 326 } 327 328 @Override 329 public IElevationProfile getParent() { 330 return parent; 331 } 332 333 @Override 334 public Date getStart() { 335 return start; 336 } 337 338 @Override 339 public WayPoint getEndWayPoint() { 340 return importantWayPoints[WAYPOINT_END]; 341 } 342 343 @Override 344 public WayPoint getMaxWayPoint() { 345 return importantWayPoints[WAYPOINT_MAX]; 346 } 347 348 @Override 349 public WayPoint getMinWayPoint() { 350 return importantWayPoints[WAYPOINT_MIN]; 351 } 352 353 @Override 354 public WayPoint getStartWayPoint() { 355 return importantWayPoints[WAYPOINT_START]; 356 } 357 358 @Override 359 public List<WayPoint> getWayPoints() { 360 return wayPoints; 361 } 362 363 @Override 364 public int getNumberOfWayPoints() { 365 return numWayPoints;// wayPoints != null ? wayPoints.size() : 0; 366 } 367 368 /** 369 * Gets the coordinate bounds of this profile. See {@link Bounds} for details. 370 * 371 * @return the bounds of this elevation profile 372 */ 373 @Override 374 public Bounds getBounds() { 375 return bounds; 376 } 377 378 /** 379 * Gets a flag indicating whether the associated way points contained 380 * elevation data or not. This is the case if min and max height or both 381 * zero. 382 * 383 * @return 384 */ 385 @Override 386 public boolean hasElevationData() { 387 return minHeight != maxHeight; 388 } 389 390 /** 391 * Visits a way point in order to update statistical values about the given 392 * way point list. 393 */ 394 @Override 395 public void visitWayPoint(WayPoint wp) { 396 if (wp.getTime().after(end)) { 397 setEnd(wp); 398 } 399 400 if (wp.getTime().before(start)) { 401 setStart(wp); 402 } 403 404 // update boundaries 405 if (bounds == null) { 406 bounds = new Bounds(wp.getCoor()); 407 } else { 408 bounds.extend(wp.getCoor()); 409 } 410 411 int ele = (int) ElevationHelper.getElevation(wp); 412 413 if (!isIgnoreZeroHeight() || ele > 0) { 414 if (ele > maxHeight) { 415 setMaxWayPoint(wp); 416 } 417 if (ele < minHeight) { 418 setMinWayPoint(wp); 419 } 420 421 if (ele > lastEle) { 422 gain += ele - lastEle; 423 } 424 425 sumEle += ele; 426 lastEle = ele; 427 } 428 } 429 430 @Override 431 public String toString() { 432 return name; /*"ElevationProfileBase [start=" + getStart() + ", end=" + getEnd() 433 433 + ", minHeight=" + getMinHeight() + ", maxHeight=" 434 434 + getMaxHeight() + "]";*/ 435 435 } 436 436 } -
applications/editors/josm/plugins/ElevationProfile/src/org/openstreetmap/josm/plugins/elevation/grid/EleVertex.java
r30701 r30737 87 87 System.out.println(newP); 88 88 */ 89 List<EleVertex> res = new ArrayList< EleVertex>();89 List<EleVertex> res = new ArrayList<>(); 90 90 res.add(new EleVertex(pI, pK, newP)); 91 91 res.add(new EleVertex(pJ, pK, newP)); -
applications/editors/josm/plugins/ElevationProfile/src/org/openstreetmap/josm/plugins/elevation/grid/ElevationGridTile.java
r30344 r30737 25 25 26 26 public class ElevationGridTile extends Tile { 27 private final BlockingDeque<EleVertex> toDo = new LinkedBlockingDeque<EleVertex>();28 private final BlockingDeque<EleVertex> vertices = new LinkedBlockingDeque<EleVertex>();27 private final BlockingDeque<EleVertex> toDo = new LinkedBlockingDeque<>(); 28 private final BlockingDeque<EleVertex> vertices = new LinkedBlockingDeque<>(); 29 29 30 30 private Bounds box; 31 31 32 33 32 public ElevationGridTile(TileSource source, int xtile, int ytile, int zoom) { 33 super(source, xtile, ytile, zoom); 34 34 35 36 37 35 box = tile2Bounds(xtile, ytile, zoom); 36 initQueue(); 37 } 38 38 39 40 41 39 public ElevationGridTile(TileSource source, int xtile, int ytile, int zoom, 40 BufferedImage image) { 41 super(source, xtile, ytile, zoom, image); 42 42 43 43 44 44 } 45 45 46 47 48 49 46 @Override 47 public void loadPlaceholderFromCache(TileCache cache) { 48 // TODO Auto-generated method stub 49 super.loadPlaceholderFromCache(cache); 50 50 51 52 51 //System.out.println("loadPlaceholderFromCache"); 52 } 53 53 54 55 56 57 58 54 @Override 55 public String getUrl() throws IOException { 56 // TODO Auto-generated method stub 57 return super.getUrl(); 58 } 59 59 60 61 62 63 64 65 60 /** 61 * Use {@link ElevationGridTile#paintTile(Graphics2D, MapView, IVertexRenderer)} to render the tile as grid. This method just issues a debug text. 62 */ 63 @Override 64 public void paint(Graphics g, int x, int y) { 65 super.paint(g, x, y); 66 66 67 68 69 67 //g.drawString(String.format("EGT %d/%d ", getXtile(), getYtile()), x, y); 68 g.drawString(getStatus(), x, y); 69 } 70 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 71 /** 72 * Paints the vertices of this tile. 73 * 74 * @param g the graphics context 75 * @param mv the map view 76 * @param vertexRenderer the vertex renderer 77 */ 78 public void paintTile(Graphics2D g, MapView mv, IVertexRenderer vertexRenderer) { 79 BlockingDeque<EleVertex> list = getVertices(); 80 for (EleVertex eleVertex : list) { 81 Point p0 = mv.getPoint(eleVertex.get(0)); 82 Point p1 = mv.getPoint(eleVertex.get(1)); 83 Point p2 = mv.getPoint(eleVertex.get(2)); 84 Triangle shape = new Triangle(p0, p1, p2); 85 85 86 87 88 89 90 91 86 // obtain vertex color 87 g.setColor(vertexRenderer.getElevationColor(eleVertex)); 88 // TODO: Move to renderer 89 g.fill(shape); 90 } 91 } 92 92 93 94 95 93 @Override 94 public void loadImage(InputStream input) throws IOException { 95 if (isLoaded()) return; 96 96 97 97 // TODO: Save 98 98 99 100 101 102 99 // We abuse the loadImage method to render the vertices... 100 // 101 while (toDo.size() > 0) { 102 EleVertex vertex = toDo.poll(); 103 103 104 105 106 107 108 109 110 111 112 113 114 104 if (vertex.isFinished()) { 105 vertices.add(vertex); 106 } else { 107 List<EleVertex> newV = vertex.divide(); 108 for (EleVertex eleVertex : newV) { 109 toDo.add(eleVertex); 110 } 111 } 112 } 113 setLoaded(true); 114 } 115 115 116 117 118 116 public BlockingDeque<EleVertex> getVertices() { 117 return vertices; 118 } 119 119 120 121 122 123 124 125 126 127 128 129 130 120 /** 121 * See also <a href="http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Tile_bounding_box">OSM Wiki</a> 122 * @param x the x 123 * @param y the y 124 * @param zoom the zoom 125 * @return the bounds 126 */ 127 private Bounds tile2Bounds(final int x, final int y, final int zoom) { 128 Bounds bb = new Bounds( 129 new LatLon(source.tileYToLat(y, zoom), source.tileXToLon(x, zoom)), 130 new LatLon(source.tileYToLat(y + 1, zoom), source.tileXToLon(x + 1, zoom))); 131 131 132 133 132 return bb; 133 } 134 134 135 136 137 138 139 140 135 /** 136 * Inits the 'todo' queue with the initial vertices. 137 */ 138 private void initQueue() { 139 LatLon min = box.getMin(); 140 LatLon max = box.getMax(); 141 141 142 143 144 142 // compute missing coordinates 143 LatLon h1 = new LatLon(min.lat(), max.lon()); 144 LatLon h2 = new LatLon(max.lat(), min.lon()); 145 145 146 147 146 double eleMin = ElevationHelper.getSrtmElevation(min); 147 double eleMax = ElevationHelper.getSrtmElevation(max); 148 148 149 150 151 152 153 149 // SRTM files present? 150 if (!ElevationHelper.isValidElevation(eleMax) || !ElevationHelper.isValidElevation(eleMin)) { 151 setError(tr("No SRTM data")); 152 return; 153 } 154 154 155 156 157 158 159 155 // compute elevation coords 156 EleCoordinate p0 = new EleCoordinate(min, eleMin); 157 EleCoordinate p1 = new EleCoordinate(h1, ElevationHelper.getSrtmElevation(h1)); 158 EleCoordinate p2 = new EleCoordinate(max, eleMax); 159 EleCoordinate p3 = new EleCoordinate(h2, ElevationHelper.getSrtmElevation(h2)); 160 160 161 162 163 164 165 166 167 161 // compute initial vertices 162 EleVertex v1 = new EleVertex(p0, p1, p2); 163 EleVertex v2 = new EleVertex(p2, p3, p0); 164 // enqueue vertices 165 toDo.add(v1); 166 toDo.add(v2); 167 } 168 168 169 170 171 172 173 169 @Override 170 public String toString() { 171 return "ElevationGridTile [box=" + box + ", xtile=" + xtile 172 + ", ytile=" + ytile + "]"; 173 } 174 174 175 175 -
applications/editors/josm/plugins/ElevationProfile/src/org/openstreetmap/josm/plugins/elevation/gui/DefaultElevationProfileRenderer.java
r30344 r30737 33 33 IElevationProfileRenderer { 34 34 35 36 37 * 38 39 40 41 * 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 private final List<Rectangle> forbiddenRects = new ArrayList<Rectangle>();60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 * 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 * 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 * 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 * 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 * 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 * 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 * 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 * 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 * 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 35 private static final int ROUND_RECT_RADIUS = 6; 36 /** 37 * 38 */ 39 private static final int TRIANGLE_BASESIZE = 24; 40 /** 41 * 42 */ 43 private static final int BASIC_WPT_RADIUS = 1; 44 private static final int BIG_WPT_RADIUS = BASIC_WPT_RADIUS * 16; 45 46 // predefined colors 47 private static final Color HIGH_COLOR = ElevationColors.EPMidBlue; 48 private static final Color LOW_COLOR = ElevationColors.EPMidBlue; 49 private static final Color START_COLOR = Color.GREEN; 50 private static final Color END_POINT = Color.RED; 51 private static final Color LEVEL_GAIN_COLOR = Color.GREEN; 52 private static final Color LEVEL_LOSS_COLOR = Color.RED; 53 private static final Color MARKER_POINT = Color.YELLOW; 54 // Predefined radians 55 private static final double RAD_180 = Math.PI; 56 // private static final double RAD_270 = Math.PI * 1.5; 57 private static final double RAD_90 = Math.PI * 0.5; 58 59 private final List<Rectangle> forbiddenRects = new ArrayList<>(); 60 61 @Override 62 public Color getColorForWaypoint(IElevationProfile profile, WayPoint wpt, 63 ElevationWayPointKind kind) { 64 65 if (wpt == null || profile == null) { 66 System.err.println(String.format( 67 "Cannot determine color: prof=%s, wpt=%s", profile, wpt)); 68 return null; 69 } 70 71 switch (kind) { 72 case Plain: 73 return Color.LIGHT_GRAY; 74 case ElevationLevelLoss: 75 return LEVEL_LOSS_COLOR; 76 case ElevationLevelGain: 77 return LEVEL_GAIN_COLOR; 78 case Highlighted: 79 return Color.ORANGE; 80 case ElevationGainHigh: 81 return Color.getHSBColor(0.3f, 1.0f, 1.0f); // green 82 case ElevationLossHigh: 83 return Color.getHSBColor(0, 1.0f, 1.0f); // red 84 case ElevationGainLow: 85 return Color.getHSBColor(0.3f, 0.5f, 1.0f); // green with low sat 86 case ElevationLossLow: 87 return Color.getHSBColor(0, 0.5f, 1.0f); // red with low sat 88 case FullHour: 89 return MARKER_POINT; 90 case MaxElevation: 91 return HIGH_COLOR; 92 case MinElevation: 93 return LOW_COLOR; 94 case StartPoint: 95 return START_COLOR; 96 case EndPoint: 97 return END_POINT; 98 default: 99 break; 100 } 101 102 throw new RuntimeException("Unknown way point kind: " + kind); 103 } 104 105 @Override 106 public void renderWayPoint(Graphics g, IElevationProfile profile, 107 MapView mv, WayPoint wpt, ElevationWayPointKind kind) { 108 109 CheckParameterUtil.ensureParameterNotNull(g, "graphics"); 110 CheckParameterUtil.ensureParameterNotNull(profile, "profile"); 111 CheckParameterUtil.ensureParameterNotNull(mv, "map view"); 112 113 if (wpt == null) { 114 System.err.println(String.format( 115 "Cannot paint: mv=%s, prof=%s, wpt=%s", mv, profile, wpt)); 116 return; 117 } 118 119 switch (kind) { 120 case MinElevation: 121 case MaxElevation: 122 renderMinMaxPoint(g, profile, mv, wpt, kind); 123 break; 124 case EndPoint: 125 case StartPoint: 126 renderStartEndPoint(g, profile, mv, wpt, kind); 127 break; 128 default: 129 renderRegularWayPoint(g, profile, mv, wpt, kind); 130 break; 131 } 132 } 133 134 @Override 135 public void renderLine(Graphics g, IElevationProfile profile, 136 MapView mv, WayPoint wpt1, WayPoint wpt2, ElevationWayPointKind kind) { 137 138 CheckParameterUtil.ensureParameterNotNull(g, "graphics"); 139 CheckParameterUtil.ensureParameterNotNull(profile, "profile"); 140 CheckParameterUtil.ensureParameterNotNull(mv, "map view"); 141 142 if (wpt1 == null || wpt2 == null) { 143 System.err.println(String.format( 144 "Cannot paint line: mv=%s, prof=%s, kind = %s", mv, profile, kind)); 145 return; 146 } 147 148 // obtain and set color 149 g.setColor(getColorForWaypoint(profile, wpt2, kind)); 150 151 // transform to view 152 Point pnt1 = mv.getPoint(wpt1.getEastNorth()); 153 Point pnt2 = mv.getPoint(wpt2.getEastNorth()); 154 155 // use thick line, if possible 156 if (g instanceof Graphics2D) { 157 Graphics2D g2 = (Graphics2D) g; 158 Stroke oldS = g2.getStroke(); 159 try { 160 g2.setStroke(new BasicStroke(3, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); 161 g2.drawLine(pnt1.x, pnt1.y, pnt2.x, pnt2.y); 162 } finally { 163 // must be restored; otherwise other layers may using this style, too 164 g2.setStroke(oldS); 165 } 166 } else { 167 // only poor man's graphics 168 g.drawLine(pnt1.x, pnt1.y, pnt2.x, pnt2.y); 169 } 170 } 171 172 /** 173 * Renders a regular way point. 174 * 175 * @param g 176 * The graphics context. 177 * @param profile 178 * The elevation profile. 179 * @param mv 180 * The map view instance. 181 * @param wpt 182 * The way point to render. 183 * @param kind 184 * The way point kind (start, end, max,...). 185 */ 186 private void renderRegularWayPoint(Graphics g, IElevationProfile profile, 187 MapView mv, WayPoint wpt, ElevationWayPointKind kind) { 188 189 Color c = getColorForWaypoint(profile, wpt, kind); 190 Point pnt = mv.getPoint(wpt.getEastNorth()); 191 192 /* Paint full hour label */ 193 if (kind == ElevationWayPointKind.FullHour) { 194 int hour = ElevationHelper.getHourOfWayPoint(wpt); 195 drawLabel(String.format("%02d:00", hour), pnt.x, pnt.y 196 + g.getFontMetrics().getHeight(), g); 197 } 198 199 /* Paint label for elevation levels */ 200 if (kind == ElevationWayPointKind.ElevationLevelGain || kind == ElevationWayPointKind.ElevationLevelLoss) { 201 int ele = ((int) Math.rint(ElevationHelper.getElevation(wpt) / 100.0)) * 100; 202 drawLabelWithTriangle(ElevationHelper.getElevationText(ele), pnt.x, pnt.y 203 + g.getFontMetrics().getHeight(), g, Color.darkGray, 8, 204 getColorForWaypoint(profile, wpt, kind), 205 kind == ElevationWayPointKind.ElevationLevelGain ? TriangleDir.Up : TriangleDir.Down); 206 } 207 208 /* Paint cursor labels */ 209 if (kind == ElevationWayPointKind.Highlighted) { 210 drawSphere(g, Color.WHITE, c, pnt.x, pnt.y, BIG_WPT_RADIUS); 211 drawLabel(ElevationHelper.getTimeText(wpt), pnt.x, pnt.y 212 - g.getFontMetrics().getHeight() - 5, g); 213 drawLabel(ElevationHelper.getElevationText(wpt), pnt.x, pnt.y 214 + g.getFontMetrics().getHeight() + 5, g); 215 } 216 } 217 218 /** 219 * Renders a min/max point 220 * 221 * @param g 222 * The graphics context. 223 * @param profile 224 * The elevation profile. 225 * @param mv 226 * The map view instance. 227 * @param wpt 228 * The way point to render. 229 * @param kind 230 * The way point kind (start, end, max,...). 231 */ 232 private void renderMinMaxPoint(Graphics g, IElevationProfile profile, 233 MapView mv, WayPoint wpt, ElevationWayPointKind kind) { 234 235 Color c = getColorForWaypoint(profile, wpt, kind); 236 int eleH = (int) ElevationHelper.getElevation(wpt); 237 Point pnt = mv.getPoint(wpt.getEastNorth()); 238 239 TriangleDir td = TriangleDir.Up; 240 241 switch (kind) { 242 case MaxElevation: 243 td = TriangleDir.Up; 244 break; 245 case MinElevation: 246 td = TriangleDir.Down; 247 break; 248 case EndPoint: 249 td = TriangleDir.Left; 250 break; 251 case StartPoint: 252 td = TriangleDir.Right; 253 break; 254 default: 255 return; // nothing to do 256 } 257 258 drawRegularTriangle(g, c, td, pnt.x, pnt.y, 259 DefaultElevationProfileRenderer.TRIANGLE_BASESIZE); 260 261 drawLabel(ElevationHelper.getElevationText(eleH), pnt.x, pnt.y 262 + g.getFontMetrics().getHeight(), g, c); 263 } 264 265 /** 266 * Draws a regular triangle. 267 * 268 * @param g 269 * The graphics context. 270 * @param c 271 * The fill color of the triangle. 272 * @param dir 273 * The direction of the triangle 274 * @param x 275 * The x coordinate in the graphics context. 276 * @param y 277 * The y coordinate in the graphics context. 278 * @param baseLength 279 * The side length in pixel of the triangle. 280 */ 281 private void drawRegularTriangle(Graphics g, Color c, TriangleDir dir, 282 int x, int y, int baseLength) { 283 if (baseLength < 2) 284 return; // cannot render triangle 285 286 int b2 = baseLength >> 1; 287 288 // coordinates for upwards directed triangle 289 Point p[] = new Point[3]; 290 291 for (int i = 0; i < p.length; i++) { 292 p[i] = new Point(); 293 } 294 295 p[0].x = -b2; 296 p[0].y = b2; 297 298 p[1].x = b2; 299 p[1].y = b2; 300 301 p[2].x = 0; 302 p[2].y = -b2; 303 304 Triangle t = new Triangle(p[0], p[1], p[2]); 305 306 // rotation angle in rad 307 double theta = 0.0; 308 309 switch (dir) { 310 case Up: 311 theta = 0.0; 312 break; 313 case Down: 314 theta = RAD_180; 315 break; 316 case Left: 317 theta = -RAD_90; 318 break; 319 case Right: 320 theta = RAD_90; 321 break; 322 } 323 324 // rotate shape 325 AffineTransform at = AffineTransform.getRotateInstance(theta); 326 Shape tRot = at.createTransformedShape(t); 327 // translate shape 328 AffineTransform at2 = AffineTransform.getTranslateInstance(x, y); 329 Shape ts = at2.createTransformedShape(tRot); 330 331 // draw the shape 332 Graphics2D g2 = (Graphics2D) g; 333 if (g2 != null) { 334 Color oldC = g2.getColor(); 335 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 336 RenderingHints.VALUE_ANTIALIAS_ON); 337 g2.setColor(c); 338 g2.fill(ts); 339 g2.setColor(oldC); 340 } 341 } 342 343 /** 344 * Renders a start/end point. 345 * 346 * @param g 347 * The graphics context. 348 * @param profile 349 * The elevation profile. 350 * @param mv 351 * The map view instance. 352 * @param wpt 353 * The way point to render. 354 * @param kind 355 * The way point kind (start, end, max,...). 356 */ 357 private void renderStartEndPoint(Graphics g, IElevationProfile profile, 358 MapView mv, WayPoint wpt, ElevationWayPointKind kind) { 359 360 Color c = getColorForWaypoint(profile, wpt, kind); 361 Point pnt = mv.getPoint(wpt.getEastNorth()); 362 drawSphere(g, Color.WHITE, c, pnt.x, pnt.y, BIG_WPT_RADIUS); 363 } 364 365 /** 366 * Draws a shaded sphere. 367 * 368 * @param g 369 * The graphics context. 370 * @param firstCol 371 * The focus color (usually white). 372 * @param secondCol 373 * The sphere color. 374 * @param x 375 * The x coordinate of the sphere center. 376 * @param y 377 * The y coordinate of the sphere center. 378 * @param radius 379 * The radius of the sphere. 380 */ 381 private void drawSphere(Graphics g, Color firstCol, Color secondCol, int x, 382 int y, int radius) { 383 Point2D center = new Point2D.Float(x, y); 384 Point2D focus = new Point2D.Float(x - (radius * 0.6f), y 385 - (radius * 0.6f)); 386 float[] dist = { 0.1f, 0.2f, 1.0f }; 387 Color[] colors = { firstCol, secondCol, Color.DARK_GRAY }; 388 RadialGradientPaint p = new RadialGradientPaint(center, radius, focus, 389 dist, colors, CycleMethod.NO_CYCLE); 390 391 Graphics2D g2 = (Graphics2D) g; 392 if (g2 != null) { 393 g2.setPaint(p); 394 int r2 = radius / 2; 395 g2.fillOval(x - r2, y - r2, radius, radius); 396 } 397 } 398 399 /** 400 * Draws a label within a filled rounded rectangle with standard gradient colors. 401 * 402 * @param s 403 * The text to draw. 404 * @param x 405 * The x coordinate of the label. 406 * @param y 407 * The y coordinate of the label. 408 * @param g 409 * The graphics context. 410 */ 411 private void drawLabel(String s, int x, int y, Graphics g) { 412 drawLabel(s, x, y, g, Color.GRAY); 413 } 414 415 /** 416 * Draws a label within a filled rounded rectangle with the specified second gradient color (first color is <tt>Color.WHITE<tt>). 417 * 418 * @param s 419 * The text to draw. 420 * @param x 421 * The x coordinate of the label. 422 * @param y 423 * The y coordinate of the label. 424 * @param g 425 * The graphics context. 426 * @param secondGradColor 427 * The second color of the gradient. 428 */ 429 private void drawLabel(String s, int x, int y, Graphics g, 430 Color secondGradColor) { 431 Graphics2D g2d = (Graphics2D) g; 432 433 int width = g.getFontMetrics(g.getFont()).stringWidth(s) + 10; 434 int height = g.getFont().getSize() + g.getFontMetrics().getLeading() 435 + 5; 436 437 Rectangle r = new Rectangle(x - (width / 2), y - (height / 2), width, 438 height); 439 440 if (isForbiddenArea(r)) { 441 return; // no space left, skip this label 442 } 443 444 g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 445 RenderingHints.VALUE_ANTIALIAS_ON); 446 GradientPaint gradient = new GradientPaint(x, y, Color.WHITE, x, y 447 + (height / 2), secondGradColor, false); 448 g2d.setPaint(gradient); 449 450 g2d.fillRoundRect(r.x, r.y, r.width, r.height, ROUND_RECT_RADIUS, 451 ROUND_RECT_RADIUS); 452 453 g2d.setColor(Color.BLACK); 454 455 g2d.drawRoundRect(r.x, r.y, r.width, r.height, ROUND_RECT_RADIUS, 456 ROUND_RECT_RADIUS); 457 g2d.drawString(s, x - (width / 2) + 5, y + (height / 2) - 3); 458 459 forbiddenRects.add(r); 460 } 461 462 /** 463 * Draws a label with an additional triangle on the left side. 464 * 465 * @param s 466 * The text to draw. 467 * @param x 468 * The x coordinate of the label. 469 * @param y 470 * The y coordinate of the label. 471 * @param g 472 * The graphics context. 473 * @param secondGradColor 474 * The second color of the gradient. 475 * @param baseLength 476 * The base length of the triangle in pixels. 477 * @param triangleColor 478 * The color of the triangle. 479 * @param triangleDir 480 * The direction of the triangle. 481 */ 482 private void drawLabelWithTriangle(String s, int x, int y, Graphics g, 483 Color secondGradColor, int baseLength, Color triangleColor, 484 TriangleDir triangleDir) { 485 Graphics2D g2d = (Graphics2D) g; 486 487 int width = g.getFontMetrics(g.getFont()).stringWidth(s) + 10 + baseLength + 5; 488 int height = g.getFont().getSize() + g.getFontMetrics().getLeading() + 5; 489 490 Rectangle r = new Rectangle(x - (width / 2), y - (height / 2), width, height); 491 492 if (isForbiddenArea(r)) { 493 return; // no space left, skip this label 494 } 495 496 g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 497 RenderingHints.VALUE_ANTIALIAS_ON); 498 GradientPaint gradient = new GradientPaint(x, y, Color.WHITE, x, y 499 + (height / 2), secondGradColor, false); 500 g2d.setPaint(gradient); 501 502 g2d.fillRoundRect(r.x, r.y, r.width, r.height, ROUND_RECT_RADIUS, 503 ROUND_RECT_RADIUS); 504 505 g2d.setColor(Color.BLACK); 506 507 g2d.drawRoundRect(r.x, r.y, r.width, r.height, ROUND_RECT_RADIUS, 508 ROUND_RECT_RADIUS); 509 g2d.drawString(s, x - (width / 2) + 8 + baseLength, y + (height / 2) - 3); 510 drawRegularTriangle(g2d, triangleColor, triangleDir, r.x + baseLength, 511 r.y + baseLength, baseLength); 512 513 forbiddenRects.add(r); 514 } 515 516 /** 517 * Checks, if the rectangle has been 'reserved' by an previous draw action. 518 * 519 * @param r 520 * The area to check for. 521 * @return true, if area is already occupied by another rectangle. 522 */ 523 private boolean isForbiddenArea(Rectangle r) { 524 525 for (Rectangle rTest : forbiddenRects) { 526 if (r.intersects(rTest)) 527 return true; 528 } 529 return false; 530 } 531 532 @Override 533 public void beginRendering() { 534 forbiddenRects.clear(); 535 } 536 537 @Override 538 public void finishRendering() { 539 // nothing to do currently 540 } 541 541 542 542 -
applications/editors/josm/plugins/ElevationProfile/src/org/openstreetmap/josm/plugins/elevation/gui/ElevationProfileDialog.java
r30532 r30737 47 47 public class ElevationProfileDialog extends ToggleDialog implements LayerChangeListener, ComponentListener { 48 48 49 50 51 52 53 54 55 private final HashMap<GpxLayer, ElevationModel> layerMap = new HashMap<GpxLayer, ElevationModel>();56 57 58 59 60 61 62 63 64 65 66 67 68 69 private final List<IElevationModelListener> listeners = new ArrayList<IElevationModelListener>();70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 * 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 * 325 326 327 328 329 330 331 332 333 334 * 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 listeners = new ArrayList<ListDataListener>();418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 49 private static final String EMPTY_DATA_STRING = "-"; 50 private static final long serialVersionUID = -868463893732535577L; 51 /* Elevation profile instance */ 52 private IElevationModel model; 53 /* GPX data */ 54 private GpxLayer activeLayer = null; 55 private final HashMap<GpxLayer, ElevationModel> layerMap = new HashMap<>(); 56 57 /* UI elements */ 58 private final ElevationProfilePanel profPanel; 59 private final JLabel minHeightLabel; 60 private final JLabel maxHeightLabel; 61 private final JLabel avrgHeightLabel; 62 private final JLabel elevationGainLabel; 63 private final JLabel totalTimeLabel; 64 private final JLabel distLabel; 65 private final JComboBox<IElevationProfile> trackCombo; 66 private final JButton zoomButton; 67 68 /* Listener to the elevation model */ 69 private final List<IElevationModelListener> listeners = new ArrayList<>(); 70 71 /** 72 * Corresponding layer instance within map view. 73 */ 74 private ElevationProfileLayer profileLayer; 75 76 /** 77 * Default constructor 78 */ 79 public ElevationProfileDialog() { 80 this(tr("Elevation Profile"), "elevation", 81 tr("Open the elevation profile window."), null, 200, true); 82 } 83 84 /** 85 * Constructor (see below) 86 */ 87 public ElevationProfileDialog(String name, String iconName, String tooltip, 88 Shortcut shortcut, int preferredHeight) { 89 this(name, iconName, tooltip, shortcut, preferredHeight, false); 90 } 91 92 /** 93 * Constructor 94 * 95 * @param name 96 * the name of the dialog 97 * @param iconName 98 * the name of the icon to be displayed 99 * @param tooltip 100 * the tool tip 101 * @param shortcut 102 * the shortcut 103 * @param preferredHeight 104 * the preferred height for the dialog 105 * @param defShow 106 * if the dialog should be shown by default, if there is no 107 * preference 108 */ 109 public ElevationProfileDialog(String name, String iconName, String tooltip, 110 Shortcut shortcut, int preferredHeight, boolean defShow) { 111 super(name, iconName, tooltip, shortcut, preferredHeight, defShow); 112 113 // create model 114 model = new ElevationModel(); 115 116 // top panel 117 JPanel rootPanel = new JPanel(); 118 GridLayout gridLayout1 = new GridLayout(2, 1); 119 rootPanel.setLayout(gridLayout1); 120 121 // statistics panel 122 JPanel statPanel = new JPanel(); 123 GridLayout gridLayoutStat = new GridLayout(2, 6); 124 statPanel.setLayout(gridLayoutStat); 125 126 // first row: Headlines with bold font 127 String[] labels = new String[]{tr("Min"), tr("Avrg"), tr("Max"), tr("Dist"), tr("Gain"), tr("Time")}; 128 for (int i = 0; i < labels.length; i++) { 129 JLabel lbl = new JLabel(labels[i]); 130 lbl.setFont(getFont().deriveFont(Font.BOLD)); 131 statPanel.add(lbl); 132 } 133 134 // second row 135 minHeightLabel = new JLabel("0 m"); 136 statPanel.add(minHeightLabel); 137 avrgHeightLabel = new JLabel("0 m"); 138 statPanel.add(avrgHeightLabel); 139 maxHeightLabel = new JLabel("0 m"); 140 statPanel.add(maxHeightLabel); 141 distLabel = new JLabel("0 km"); 142 statPanel.add(distLabel); 143 elevationGainLabel = new JLabel("0 m"); 144 statPanel.add(elevationGainLabel); 145 totalTimeLabel = new JLabel("0"); 146 statPanel.add(totalTimeLabel); 147 148 // track selection panel 149 JPanel trackPanel = new JPanel(); 150 FlowLayout fl = new FlowLayout(FlowLayout.LEFT); 151 trackPanel.setLayout(fl); 152 153 JLabel lbTrack = new JLabel(tr("Tracks")); 154 lbTrack.setFont(getFont().deriveFont(Font.BOLD)); 155 trackPanel.add(lbTrack); 156 157 zoomButton = new JButton(tr("Zoom")); 158 zoomButton.addActionListener(new ActionListener() { 159 @Override 160 public void actionPerformed(ActionEvent arg0) { 161 if (model != null) { 162 IElevationProfile profile = model.getCurrentProfile(); 163 if (profile != null) { 164 Main.map.mapView.zoomTo(profile.getBounds()); 165 } 166 } 167 168 } 169 }); 170 zoomButton.setEnabled(false); 171 172 trackCombo = new JComboBox<>(new TrackModel()); 173 trackCombo.setPreferredSize(new Dimension(200, 24)); // HACK! 174 trackCombo.setEnabled(false); // we have no model on startup 175 176 trackPanel.add(trackCombo); 177 trackPanel.add(zoomButton); 178 179 // assemble root panel 180 rootPanel.add(statPanel); 181 rootPanel.add(trackPanel); 182 183 JPanel mainPanel = new JPanel(new BorderLayout()); 184 mainPanel.add(rootPanel, BorderLayout.PAGE_END); 185 186 // add chart component 187 profPanel = new ElevationProfilePanel(null); 188 mainPanel.add(profPanel, BorderLayout.CENTER); 189 profPanel.addComponentListener(this); 190 191 createLayout(mainPanel, true, null); 192 } 193 194 @Override 195 public void showNotify() { 196 MapView.addLayerChangeListener(this); 197 if (Main.isDisplayingMapView()) { 198 Layer layer = Main.map.mapView.getActiveLayer(); 199 if (layer instanceof GpxLayer) { 200 setActiveLayer((GpxLayer) layer); 201 } 202 } 203 } 204 205 @Override 206 public void hideNotify() { 207 MapView.removeLayerChangeListener(this); 208 } 209 210 /** 211 * Gets the elevation model instance. 212 * @return 213 */ 214 public IElevationModel getModel() { 215 return model; 216 } 217 218 /** 219 * Sets the elevation model instance. 220 * @param model The new model. 221 */ 222 public void setModel(IElevationModel model) { 223 if (this.model != model) { 224 this.model = model; 225 profPanel.setElevationModel(model); 226 updateView(); 227 } 228 } 229 230 /** 231 * Gets the associated layer instance of the elevation profile. 232 * @return 233 */ 234 public ElevationProfileLayer getProfileLayer() { 235 return profileLayer; 236 } 237 238 /** 239 * Sets the associated layer instance of the elevation profile. 240 * @param profileLayer The elevation profile layer. 241 */ 242 public void setProfileLayer(ElevationProfileLayer profileLayer) { 243 if (this.profileLayer != profileLayer) { 244 if (this.profileLayer != null) { 245 profPanel.removeSelectionListener(this.profileLayer); 246 } 247 this.profileLayer = profileLayer; 248 profPanel.addSelectionListener(this.profileLayer); 249 } 250 } 251 252 /** 253 * Refreshes the dialog when model data have changed and notifies clients 254 * that the model has changed. 255 */ 256 private void updateView() { 257 if (model == null) { 258 disableView(); 259 return; 260 } 261 262 IElevationProfile profile = model.getCurrentProfile(); 263 if (profile != null) { 264 // Show name of profile in title 265 setTitle(String.format("%s: %s", tr("Elevation Profile"), profile.getName())); 266 267 if (profile.hasElevationData()) { 268 // Show elevation data 269 minHeightLabel.setText( 270 ElevationHelper.getElevationText(profile.getMinHeight())); 271 maxHeightLabel.setText( 272 ElevationHelper.getElevationText(profile.getMaxHeight())); 273 avrgHeightLabel.setText( 274 ElevationHelper.getElevationText(profile.getAverageHeight())); 275 elevationGainLabel.setText( 276 ElevationHelper.getElevationText(profile.getGain())); 277 } 278 279 // compute values for time and distance 280 long diff = profile.getTimeDifference(); 281 long minutes = diff / (1000 * 60); 282 long hours = minutes / 60; 283 minutes = minutes % 60; 284 285 double dist = profile.getDistance(); 286 287 totalTimeLabel.setText(String.format("%d:%02d h", hours, minutes)); 288 distLabel.setText(NavigatableComponent.getSystemOfMeasurement().getDistText(dist)); 289 trackCombo.setEnabled(model.profileCount() > 1); 290 trackCombo.setModel(new TrackModel()); 291 zoomButton.setEnabled(true); 292 } else { // no elevation data, -> switch back to empty view 293 disableView(); 294 } 295 296 fireModelChanged(); 297 repaint(); 298 } 299 300 private void disableView() { 301 setTitle(String.format("%s: (No data)", tr("Elevation Profile"))); 302 303 minHeightLabel.setText(EMPTY_DATA_STRING); 304 maxHeightLabel.setText(EMPTY_DATA_STRING); 305 avrgHeightLabel.setText(EMPTY_DATA_STRING); 306 elevationGainLabel.setText(EMPTY_DATA_STRING); 307 totalTimeLabel.setText(EMPTY_DATA_STRING); 308 distLabel.setText(EMPTY_DATA_STRING); 309 trackCombo.setEnabled(false); 310 zoomButton.setEnabled(false); 311 } 312 313 /** 314 * Fires the 'model changed' event to all listeners. 315 */ 316 protected void fireModelChanged() { 317 for (IElevationModelListener listener : listeners) { 318 listener.elevationProfileChanged(getModel().getCurrentProfile()); 319 } 320 } 321 322 /** 323 * Adds a model listener to this instance. 324 * 325 * @param listener 326 * The listener to add. 327 */ 328 public void addModelListener(IElevationModelListener listener) { 329 this.listeners.add(listener); 330 } 331 332 /** 333 * Removes a model listener from this instance. 334 * 335 * @param listener 336 * The listener to remove. 337 */ 338 public void removeModelListener(IElevationModelListener listener) { 339 this.listeners.remove(listener); 340 } 341 342 /** 343 * Removes all listeners from this instance. 344 */ 345 public void removeAllListeners() { 346 this.listeners.clear(); 347 } 348 349 @Override 350 public void activeLayerChange(Layer oldLayer, Layer newLayer) { 351 if (newLayer instanceof GpxLayer) { 352 setActiveLayer((GpxLayer) newLayer); 353 } 354 } 355 356 private void setActiveLayer(GpxLayer newLayer) { 357 if (activeLayer != newLayer) { 358 activeLayer = newLayer; 359 360 // layer does not exist -> create 361 if (!layerMap.containsKey(newLayer)) { 362 GpxData gpxData = newLayer.data; 363 ElevationModel newEM = new ElevationModel(newLayer.getName(), 364 gpxData); 365 layerMap.put(newLayer, newEM); 366 } 367 368 ElevationModel em = layerMap.get(newLayer); 369 setModel(em); 370 } 371 } 372 373 @Override 374 public void layerAdded(Layer newLayer) { 375 if (newLayer instanceof GpxLayer) { 376 GpxLayer gpxLayer = (GpxLayer) newLayer; 377 setActiveLayer(gpxLayer); 378 } 379 } 380 381 @Override 382 public void layerRemoved(Layer oldLayer) { 383 if (layerMap.containsKey(oldLayer)) { 384 layerMap.remove(oldLayer); 385 } 386 387 if (layerMap.size() == 0) { 388 setModel(null); 389 if (profileLayer != null) { 390 profileLayer.setProfile(null); 391 } 392 } 393 } 394 395 @Override 396 public void componentHidden(ComponentEvent e) { 397 } 398 399 @Override 400 public void componentMoved(ComponentEvent e) { 401 } 402 403 @Override 404 public void componentResized(ComponentEvent e) { 405 } 406 407 @Override 408 public void componentShown(ComponentEvent e) { 409 } 410 411 class TrackModel implements ComboBoxModel<IElevationProfile> { 412 private Collection<ListDataListener> listeners; 413 414 @Override 415 public void addListDataListener(ListDataListener arg0) { 416 if (listeners == null) { 417 listeners = new ArrayList<>(); 418 } 419 listeners.add(arg0); 420 } 421 422 @Override 423 public IElevationProfile getElementAt(int index) { 424 if (model == null) return null; 425 426 IElevationProfile ep = model.getProfiles().get(index); 427 return ep; 428 } 429 430 @Override 431 public int getSize() { 432 if (model == null) return 0; 433 434 return model.profileCount(); 435 } 436 437 @Override 438 public void removeListDataListener(ListDataListener listener) { 439 if (listeners == null) return; 440 441 listeners.remove(listener); 442 } 443 444 @Override 445 public IElevationProfile getSelectedItem() { 446 if (model == null) return null; 447 448 return model.getCurrentProfile(); 449 } 450 451 @Override 452 public void setSelectedItem(Object selectedObject) { 453 if (model != null && selectedObject instanceof IElevationProfile) { 454 model.setCurrentProfile((IElevationProfile) selectedObject); 455 profileLayer.setProfile(model.getCurrentProfile()); 456 457 repaint(); 458 } 459 } 460 } 461 461 } -
applications/editors/josm/plugins/ElevationProfile/src/org/openstreetmap/josm/plugins/elevation/gui/ElevationProfilePanel.java
r30344 r30737 36 36 */ 37 37 public class ElevationProfilePanel extends JPanel implements ComponentListener, MouseMotionListener { 38 39 40 41 42 43 44 45 46 47 48 private final List<IElevationProfileSelectionListener> selectionChangedListeners = new ArrayList<IElevationProfileSelectionListener>();49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 38 /** 39 * Serial version UID 40 */ 41 private static final long serialVersionUID = -7343429725259575319L; 42 private static final int BOTTOM_TEXT_Y_OFFSET = 7; 43 44 private IElevationModel model; 45 private Rectangle plotArea; 46 private final IElevationProfileRenderer renderer = new DefaultElevationProfileRenderer(); 47 private int selectedIndex = -1; 48 private final List<IElevationProfileSelectionListener> selectionChangedListeners = new ArrayList<>(); 49 private boolean isPainting; 50 private int step = 0; 51 52 /** 53 * Constructs a new ElevationProfilePanel with the given elevation profile. 54 * @param profile The elevation profile to show in the panel. 55 */ 56 public ElevationProfilePanel(IElevationModel profile) { 57 super(); 58 this.model = profile; 59 setDoubleBuffered(true); 60 setBackground(Color.WHITE); 61 createOrUpdatePlotArea(); 62 addComponentListener(this); 63 addMouseMotionListener(this); 64 65 Font lFont = getFont().deriveFont(9.0f); 66 setFont(lFont); 67 } 68 69 /** 70 * Gets the elevation profile instance. 71 * @return 72 */ 73 public IElevationModel getProfile() { 74 return model; 75 } 76 77 /** 78 * Sets the new elevation profile instance. 79 * @param model 80 */ 81 public void setElevationModel(IElevationModel model) { 82 if (this.model != model) { 83 this.model = model; 84 invalidate(); 85 } 86 } 87 88 /** 89 * Gets the plot area coordinates. 90 * @return 91 */ 92 public Rectangle getPlotArea() { 93 return plotArea; 94 } 95 96 /** 97 * Sets the plot area coordinates. 98 * @param plotArea 99 */ 100 public void setPlotArea(Rectangle plotArea) { 101 this.plotArea = plotArea; 102 } 103 104 /** 105 * Gets the selected index of the bar. 106 * @return 107 */ 108 public int getSelectedIndex() { 109 return selectedIndex; 110 } 111 112 /** 113 * Sets the selected index of the bar. 114 * @param selectedIndex 115 */ 116 public void setSelectedIndex(int selectedIndex) { 117 this.selectedIndex = selectedIndex; 118 119 if (model != null) { 120 model.setCurrentProfile(selectedIndex); 121 } 122 } 123 124 /** 125 * Gets the selected (highlighted) way point. 126 * @return The selected way point or null, if no way point is selected. 127 */ 128 public WayPoint getSelectedWayPoint() { 129 if (model == null) return null; 130 131 IElevationProfile profile = model.getCurrentProfile(); 132 133 int selWp = this.selectedIndex * step; 134 if (profile != null && profile.getWayPoints() != null && selWp > 0 && profile.getWayPoints().size() > selWp) { 135 return profile.getWayPoints().get(selWp); 136 } else { 137 return null; 138 } 139 } 140 141 /** 142 * Adds a selection listener. 143 * @param listener The listener instance to add. 144 */ 145 public void addSelectionListener(IElevationProfileSelectionListener listener) { 146 if (listener == null) return; 147 148 selectionChangedListeners.add(listener); 149 } 150 151 /** 152 * Removes a selection listener from the list. 153 * @param listener The listener instance to remove. 154 */ 155 public void removeSelectionListener(IElevationProfileSelectionListener listener) { 156 if (listener == null) return; 157 158 selectionChangedListeners.remove(listener); 159 } 160 161 /** 162 * Removes all selection listeners. 163 */ 164 public void removeAllSelectionListeners() { 165 selectionChangedListeners.clear(); 166 } 167 168 protected void fireSelectionChanged(WayPoint selWayPoint) { 169 for (IElevationProfileSelectionListener listener : selectionChangedListeners) { 170 listener.selectedWayPointChanged(selWayPoint); 171 } 172 } 173 174 @Override 175 public void paint(Graphics g) { 176 isPainting = true; 177 178 try { 179 super.paint(g); 180 createOrUpdatePlotArea(); 181 int y1 = getPlotBottom(); 182 183 g.setColor(Color.DARK_GRAY); 184 g.drawLine(plotArea.x, plotArea.y, plotArea.x, plotArea.y 185 + plotArea.height); 186 g.drawLine(plotArea.x, plotArea.y + plotArea.height, plotArea.x 187 + plotArea.width, plotArea.y + plotArea.height); 188 189 190 if (model != null) { 191 IElevationProfile profile = model.getCurrentProfile(); 192 if (profile != null && profile.hasElevationData()) { 193 // Draw start and end date 194 drawAlignedString(formatDate(profile.getStart()), 5, y1 + BOTTOM_TEXT_Y_OFFSET, 195 TextAlignment.Left, g); 196 drawAlignedString(formatDate(profile.getEnd()), 197 getPlotRight(), y1 + BOTTOM_TEXT_Y_OFFSET, TextAlignment.Right, g); 198 199 // Show SRTM indicator 200 if (ElevationHelper.hasSrtmData(profile.getBounds())) { 201 String txt = "SRTM"; 202 drawAlignedString(txt, getPlotHCenter(), y1 + BOTTOM_TEXT_Y_OFFSET, TextAlignment.Centered, g); 203 } 204 drawProfile(g); 205 drawElevationLines(g); 206 } else { 207 // No profile or profile supports no elevation data 208 drawAlignedString(tr("(No elevation data)"), getPlotHCenter(), 209 getPlotVCenter(), TextAlignment.Centered, g); 210 } 211 } 212 } finally { 213 isPainting = false; 214 } 215 } 216 217 /** 218 * Draw a string with a specified alignment. 219 * @param s The text to display. 220 * @param x The x coordinate. 221 * @param y The y coordinate. 222 * @param align The text alignment. 223 * @param g The graphics context. 224 * @return The resulting rectangle of the drawn string. 225 */ 226 private Rectangle drawAlignedString(String s, int x, int y, 227 TextAlignment align, Graphics g) { 228 FontMetrics fm = g.getFontMetrics(); 229 int w = fm.stringWidth(s); 230 int h = fm.getHeight(); 231 232 int xoff = w / 2; 233 int yoff = h / 2; 234 235 if (align == TextAlignment.Left) { 236 xoff = 0; 237 } 238 if (align == TextAlignment.Right) { 239 xoff = w; 240 } 241 242 g.drawString(s, x - xoff, y + yoff); 243 244 return new Rectangle(x - xoff, y - yoff, w, h); 245 } 246 247 /** 248 * Draw a string which is horizontally centered around (x,y). 249 * @param s The text to display. 250 * @param x The x coordinate. 251 * @param y The y coordinate. 252 * @param g The graphics context. 253 * @return The resulting rectangle of the drawn string. 254 254 255 255 private void drawHCenteredString(String s, int x, int y, Graphics g) { … … 257 257 }*/ 258 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 * 324 325 326 327 328 329 330 331 332 * 333 334 335 336 337 338 339 340 341 * 342 343 344 345 346 347 348 349 350 * 351 352 353 354 355 356 357 358 359 * 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 * 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 * 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 259 /** 260 * Formats the date in a predefined manner: "21. Oct 2010, 12:10". 261 * @param date 262 * @return 263 */ 264 private String formatDate(Date date) { 265 Format formatter = new SimpleDateFormat("d MMM yy, HH:mm"); 266 267 return formatter.format(date); 268 } 269 270 /** 271 * Helper function to draw elevation axes. 272 * @param g 273 */ 274 private void drawElevationLines(Graphics g) { 275 IElevationProfile profile = model.getCurrentProfile(); 276 277 double diff = profile.getHeightDifference(); 278 279 if (diff == 0.0) { 280 return; 281 } 282 283 double z10 = Math.floor(Math.log10(diff)); 284 double scaleUnit = Math.pow(10, z10); // scale unit, e. g. 100 for 285 // values below 1000 286 287 int upperLimit = (int) (Math.round(Math.ceil(profile.getMaxHeight() 288 / scaleUnit)) * scaleUnit); 289 int lowerLimit = (int) (Math.round(Math.floor(profile.getMinHeight() 290 / scaleUnit)) * scaleUnit); 291 int su = (int) scaleUnit; 292 293 for (int i = lowerLimit; i <= upperLimit; i += su) { 294 int yLine = getYForEelevation(i); 295 296 // check bounds 297 if (yLine <= getPlotBottom() && yLine >= getPlotTop()) { 298 String txt = ElevationHelper.getElevationText(i); 299 300 Rectangle r = drawAlignedString(txt, getPlotHCenter(), yLine - 2, 301 TextAlignment.Right, g); 302 r.grow(2, 2); 303 304 // Draw left and right line segment 305 g.drawLine(getPlotLeftAxis(), yLine, r.x, 306 yLine); 307 g.drawLine(r.x + r.width, yLine, getPlotRight(), 308 yLine); 309 // Draw label with shadow 310 g.setColor(Color.WHITE); 311 drawAlignedString(txt, getPlotHCenter() + 1, yLine - 1, 312 TextAlignment.Right, g); 313 g.setColor(Color.BLACK); 314 drawAlignedString(txt, getPlotHCenter(), yLine - 2, 315 TextAlignment.Right, g); 316 } 317 } 318 } 319 320 /** 321 * Gets the x value of the left border for axes (slightly smaller than the 322 * left x). 323 * 324 * @return 325 */ 326 private int getPlotLeftAxis() { 327 return plotArea.x - 3; 328 } 329 330 /** 331 * Gets the x value of the left border. 332 * 333 * @return 334 */ 335 private int getPlotLeft() { 336 return plotArea.x + 1; 337 } 338 339 /** 340 * Gets the horizontal center coordinate (mid between left and right x). 341 * 342 * @return 343 */ 344 private int getPlotHCenter() { 345 return (getPlotLeft() + getPlotRight()) / 2; 346 } 347 348 /** 349 * Gets the vertical center coordinate (mid between top and bottom y). 350 * 351 * @return 352 */ 353 private int getPlotVCenter() { 354 return (getPlotTop() + getPlotBottom()) / 2; 355 } 356 357 /** 358 * Gets the x value of the right border. 359 * 360 * @return 361 */ 362 private int getPlotRight() { 363 return plotArea.x + plotArea.width - 1; 364 } 365 366 private int getPlotBottom() { 367 return plotArea.y + plotArea.height - 1; 368 } 369 370 private int getPlotTop() { 371 return plotArea.y + 1; 372 } 373 374 /** 375 * Gets for an elevation value the according y coordinate in the plot area. 376 * 377 * @param elevation 378 * @return The y coordinate in the plot area. 379 */ 380 private int getYForEelevation(int elevation) { 381 int y1 = getPlotBottom(); 382 383 IElevationProfile profile = model.getCurrentProfile(); 384 385 if (!profile.hasElevationData()) { 386 return y1; 387 } 388 389 double diff = profile.getHeightDifference(); 390 391 return y1 - (int) Math.round(((elevation - profile.getMinHeight()) / diff * plotArea.height)); 392 } 393 394 /** 395 * Draws the elevation profile 396 * 397 * @param g 398 */ 399 private void drawProfile(Graphics g) { 400 IElevationProfile profile = model.getCurrentProfile(); 401 402 int nwp = profile.getNumberOfWayPoints(); 403 int n = Math.min(plotArea.width, nwp); 404 405 if (n == 0) return; // nothing to draw 406 // compute step size in panel (add 1 to make sure that 407 // the complete range fits into panel 408 step = (nwp / n) + 1; 409 410 int yBottom = getPlotBottom(); 411 Color oldC = g.getColor(); 412 413 for (int i = 0, ip = 0; i < n && ip < nwp; i++, ip += step) { 414 WayPoint wpt = profile.getWayPoints().get(ip); 415 int eleVal = (int) ElevationHelper.getElevation(wpt); 416 Color c = renderer.getColorForWaypoint(profile, wpt, 417 ElevationWayPointKind.Plain); 418 419 // draw cursor 420 if (i == this.selectedIndex) { 421 g.setColor(Color.BLACK); 422 drawAlignedString(ElevationHelper.getElevationText(eleVal), 423 (getPlotRight() + getPlotLeft()) / 2, 424 getPlotBottom() + 6, 425 TextAlignment.Centered, 426 g); 427 428 c = renderer.getColorForWaypoint(profile, wpt, ElevationWayPointKind.Highlighted); 429 } 430 431 int yEle = getYForEelevation(eleVal); 432 int x = getPlotLeft() + i; 433 434 g.setColor(c); 435 g.drawLine(x, yBottom, x, yEle); 436 g.setColor(ElevationColors.EPLightBlue); 437 } 438 439 g.setColor(oldC); 440 } 441 442 @Override 443 protected void paintBorder(Graphics g) { 444 super.paintBorder(g); 445 446 Border loweredbevel = BorderFactory.createLoweredBevelBorder(); 447 this.setBorder(loweredbevel); 448 } 449 450 /** 451 * Determines the size of the plot area depending on the panel size. 452 */ 453 private void createOrUpdatePlotArea() { 454 Dimension caSize = getSize(); 455 456 if (plotArea == null) { 457 plotArea = new Rectangle(0, 0, caSize.width, caSize.height); 458 } else { 459 plotArea.width = caSize.width; 460 plotArea.height = caSize.height; 461 } 462 463 plotArea.setLocation(0, 0); 464 plotArea.grow(-10, -15); 465 } 466 467 @Override 468 public void componentHidden(ComponentEvent arg0) { 469 // TODO Auto-generated method stub 470 } 471 472 @Override 473 public void componentMoved(ComponentEvent arg0) { 474 // TODO Auto-generated method stub 475 } 476 477 @Override 478 public void componentResized(ComponentEvent arg0) { 479 createOrUpdatePlotArea(); 480 } 481 482 @Override 483 public void componentShown(ComponentEvent arg0) { 484 // TODO Auto-generated method stub 485 } 486 487 @Override 488 public void mouseDragged(MouseEvent arg0) { 489 // TODO Auto-generated method stub 490 491 } 492 493 @Override 494 public void mouseMoved(MouseEvent arg0) { 495 if (isPainting || arg0.isControlDown() || arg0.isAltDown() || arg0.isShiftDown()) arg0.consume(); 496 497 int x = arg0.getX(); 498 int l = this.getX(); 499 int pl = this.getPlotLeft(); 500 int newIdx = x - l - pl; 501 502 if (newIdx != this.selectedIndex && newIdx >= 0) { 503 this.selectedIndex = newIdx; 504 this.repaint(); 505 fireSelectionChanged(getSelectedWayPoint()); 506 } 507 } 508 509 @Override 510 public String getToolTipText() { 511 WayPoint wpt = getSelectedWayPoint(); 512 if (wpt != null) { 513 return String.format("%s: %s", ElevationHelper.getTimeText(wpt), ElevationHelper.getElevationText(wpt)); 514 } 515 516 return super.getToolTipText(); 517 } 518 518 }
Note:
See TracChangeset
for help on using the changeset viewer.