Ignore:
Timestamp:
2016-06-18T23:40:12+02:00 (9 years ago)
Author:
donvip
Message:

remove tabs

Location:
applications/editors/josm/plugins/ElevationProfile/src/org/openstreetmap/josm/plugins/elevation
Files:
15 edited

Legend:

Unmodified
Added
Removed
  • applications/editors/josm/plugins/ElevationProfile/src/org/openstreetmap/josm/plugins/elevation/ColorMap.java

    r30737 r32315  
    1616 */
    1717public class ColorMap {
    18         private List<ColorMapEntry> colorList;
    19         private String name;
    20         private static HashMap<String, ColorMap> colorMaps;
     18    private List<ColorMapEntry> colorList;
     19    private String name;
     20    private static HashMap<String, ColorMap> colorMaps;
    2121
    22         static {
    23                 colorMaps = new HashMap<>();
    24         }
     22    static {
     23        colorMaps = new HashMap<>();
     24    }
    2525
    26         // Private ctor to enforce use of create
    27         private ColorMap() {
    28         }
     26    // Private ctor to enforce use of create
     27    private ColorMap() {
     28    }
    2929
    30         public String getName() {
    31                 return name;
    32         }
     30    public String getName() {
     31        return name;
     32    }
    3333
    34         public void setName(String name) {
    35                 this.name = name;
    36         }
     34    public void setName(String name) {
     35        this.name = name;
     36    }
    3737
    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                 }
     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        }
    4949
    50                 // out of range?
    51                 if (elevation < colorList.get(0).ele) {
    52                         return colorList.get(0).getColor();
    53                 }
     50        // out of range?
     51        if (elevation < colorList.get(0).ele) {
     52            return colorList.get(0).getColor();
     53        }
    5454
    55                 int last = colorList.size() - 1;
    56                 if (elevation > colorList.get(last).ele) {
    57                         return colorList.get(last).getColor();
    58                 }
     55        int last = colorList.size() - 1;
     56        if (elevation > colorList.get(last).ele) {
     57            return colorList.get(last).getColor();
     58        }
    5959
    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);
     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);
    6464
    65                         // elevation within range?
    66                         if (e1.getEle() <= elevation && e2.getEle() >= elevation) {
     65            // elevation within range?
     66            if (e1.getEle() <= elevation && e2.getEle() >= elevation) {
    6767
    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                 }
     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        }
    7373
    74                 // here we should never end!
    75                 throw new RuntimeException("Inconsistent color map - found no entry for elevation " + elevation);
    76         }
     74        // here we should never end!
     75        throw new RuntimeException("Inconsistent color map - found no entry for elevation " + elevation);
     76    }
    7777
    7878
    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         }
     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    }
    9191
    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         }
     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    }
    100100
    101101
    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         }
     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    }
    111111
    112         private static void registerColorMap(ColorMap newMap) {
    113                 CheckParameterUtil.ensureParameterNotNull(newMap);
    114                 colorMaps.put(newMap.getName(), newMap);
    115         }
     112    private static void registerColorMap(ColorMap newMap) {
     113        CheckParameterUtil.ensureParameterNotNull(newMap);
     114        colorMaps.put(newMap.getName(), newMap);
     115    }
    116116
    117         public static void unregisterColorMap(String name) {
    118                 if (colorMaps.containsKey(name)) {
    119                         colorMaps.remove(name);
    120                 }
    121         }
     117    public static void unregisterColorMap(String name) {
     118        if (colorMaps.containsKey(name)) {
     119            colorMaps.remove(name);
     120        }
     121    }
    122122
    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;
     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;
    129129
    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         }
     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    }
    135135
    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);
     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);
    148148
    149                 if (colors.length != ele.length) {
    150                         throw new IllegalArgumentException("Arrays colors and ele must have same length: " + colors.length + " vs " + ele.length);
    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        }
    152152
    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                 }
     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        }
    159159
    160                 // sort by elevation
    161                 Collections.sort(map.colorList);
     160        // sort by elevation
     161        Collections.sort(map.colorList);
    162162
    163                 registerColorMap(map);
    164                 return map;
    165         }
     163        registerColorMap(map);
     164        return map;
     165    }
    166166
    167167
    168         class ColorMapEntry implements Comparable<ColorMapEntry> {
    169                 private final int ele; // limit
    170                 private final Color color;
     168    class ColorMapEntry implements Comparable<ColorMapEntry> {
     169        private final int ele; // limit
     170        private final Color color;
    171171
    172                 public ColorMapEntry(Color color, int ele) {
    173                         super();
    174                         this.color = color;
    175                         this.ele = ele;
    176                 }
     172        public ColorMapEntry(Color color, int ele) {
     173            super();
     174            this.color = color;
     175            this.ele = ele;
     176        }
    177177
    178                 public int getEle() {
    179                         return ele;
    180                 }
     178        public int getEle() {
     179            return ele;
     180        }
    181181
    182                 public Color getColor() {
    183                         return color;
    184                 }
     182        public Color getColor() {
     183            return color;
     184        }
    185185
    186                 @Override
    187                 public int compareTo(ColorMapEntry o) {
    188                         return this.ele - o.ele;
    189                 }
    190         }
     186        @Override
     187        public int compareTo(ColorMapEntry o) {
     188            return this.ele - o.ele;
     189        }
     190    }
    191191}
  • applications/editors/josm/plugins/ElevationProfile/src/org/openstreetmap/josm/plugins/elevation/ElevationHelper.java

    r30737 r32315  
    1919 */
    2020public class ElevationHelper {
    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                 /*
     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        /*
    299299        int lat = (int)Math.round(wpt.getCoor().lat());
    300300        int lon = (int)Math.round(wpt.getCoor().lon());
     
    303303        System.out.println(
    304304                String.format("Geoid(%d, %d) = %d", lat, lon, geoid));
    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         }
     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    }
    368368}
  • applications/editors/josm/plugins/ElevationProfile/src/org/openstreetmap/josm/plugins/elevation/ElevationProfilePlugin.java

    r32313 r32315  
    2323public class ElevationProfilePlugin extends Plugin {
    2424
    25         private static ElevationProfileLayer currentLayer;
     25    private static ElevationProfileLayer currentLayer;
    2626
    27         /**
    28         * Initializes the plugin.
    29         * @param info Context information about the plugin.
    30         */
    31         public ElevationProfilePlugin(PluginInformation info) {
    32                 super(info);
     27    /**
     28    * Initializes the plugin.
     29    * @param info Context information about the plugin.
     30    */
     31    public ElevationProfilePlugin(PluginInformation info) {
     32        super(info);
    3333
    34                 createColorMaps();
     34        createColorMaps();
    3535
    36                 // TODO: Disable this view as long as it is not stable
    37                 MainMenu.add(Main.main.menu.imagerySubMenu, new AddElevationLayerAction(), false, 0);
    38         }
     36        // TODO: Disable this view as long as it is not stable
     37        MainMenu.add(Main.main.menu.imagerySubMenu, new AddElevationLayerAction(), false, 0);
     38    }
    3939
    40         /**
    41         * Called after Main.mapFrame is initialized. (After the first data is loaded).
    42         * You can use this callback to tweak the newFrame to your needs, as example install
    43         * an alternative Painter.
    44         */
    45         @Override
    46         public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame) {
    47                 super.mapFrameInitialized(oldFrame, newFrame);
     40    /**
     41    * Called after Main.mapFrame is initialized. (After the first data is loaded).
     42    * You can use this callback to tweak the newFrame to your needs, as example install
     43    * an alternative Painter.
     44    */
     45    @Override
     46    public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame) {
     47        super.mapFrameInitialized(oldFrame, newFrame);
    4848
    49                 if (newFrame != null) {
    50                         ElevationMapMode eleMode = new ElevationMapMode("Elevation profile", newFrame);
    51                         newFrame.addMapMode(new IconToggleButton(eleMode));
    52                         ElevationProfileDialog eleProfileDlg = new ElevationProfileDialog();
    53                         eleProfileDlg.addModelListener(eleMode);
    54                         eleProfileDlg.setProfileLayer(getCurrentLayer());
    55                         newFrame.addToggleDialog(eleProfileDlg);
    56                 }
    57         }
     49        if (newFrame != null) {
     50            ElevationMapMode eleMode = new ElevationMapMode("Elevation profile", newFrame);
     51            newFrame.addMapMode(new IconToggleButton(eleMode));
     52            ElevationProfileDialog eleProfileDlg = new ElevationProfileDialog();
     53            eleProfileDlg.addModelListener(eleMode);
     54            eleProfileDlg.setProfileLayer(getCurrentLayer());
     55            newFrame.addToggleDialog(eleProfileDlg);
     56        }
     57    }
    5858
    59         /**
    60         * Gets the elevation profile layer which decorates the current layer
    61         * with some markers.
    62         * @return
    63         */
    64         public static ElevationProfileLayer getCurrentLayer(){
    65                 if(currentLayer == null){
    66                         currentLayer = new ElevationProfileLayer(tr("Elevation Profile"));
    67                         Main.main.addLayer(currentLayer);
    68                 }
    69                 return currentLayer;
    70         }
     59    /**
     60    * Gets the elevation profile layer which decorates the current layer
     61    * with some markers.
     62    * @return
     63    */
     64    public static ElevationProfileLayer getCurrentLayer(){
     65        if(currentLayer == null){
     66            currentLayer = new ElevationProfileLayer(tr("Elevation Profile"));
     67            Main.main.addLayer(currentLayer);
     68        }
     69        return currentLayer;
     70    }
    7171
    72         private void createColorMaps() {
    73                 // Data taken from http://proceedings.esri.com/library/userconf/proc98/proceed/to850/pap842/p842.htm
    74                 ColorMap.create("Physical_US",
    75                                 new Color[]{
    76                                                 new Color(18,129,242),
    77                                                 new Color(113,153,89),
    78                                                 new Color(117,170,101),
    79                                                 new Color(149,190,113),
    80                                                 new Color(178,214,117),
    81                                                 new Color(202,226,149),
    82                                                 new Color(222,238,161),
    83                                                 new Color(242,238,161),
    84                                                 new Color(238,222,153),
    85                                                 new Color(242,206,133),
    86                                                 new Color(234,182,129),
    87                                                 new Color(218,157,121),
    88                                                 new Color(194,141,125),
    89                                                 new Color(214,157,145),
    90                                                 new Color(226,174,165),
    91                                                 new Color(222,186,182),
    92                                                 new Color(238,198,210),
    93                                                 new Color(255,206,226),
    94                                                 new Color(250,218,234),
    95                                                 new Color(255,222,230),
    96                                                 new Color(255,230,242),
    97                                                 new Color(255,242,255)
    98                 },
    99                                 // elevation in meters - the page above uses feet, so these values differs slightly
    100                                 new int[]{
    101                                                 -3000,
    102                                                 0,
    103                                                 150,
    104                                                 300,
    105                                                 450,
    106                                                 600,
    107                                                 750,
    108                                                 900,
    109                                                 1050,
    110                                                 1200,
    111                                                 1350,
    112                                                 1500,
    113                                                 1650,
    114                                                 1800,
    115                                                 1950,
    116                                                 2100,
    117                                                 2250,
    118                                                 2400,
    119                                                 2550,
    120                                                 2700,
    121                                                 2750,
    122                                                 3000
    123                 }
    124                                 );
    125         }
     72    private void createColorMaps() {
     73        // Data taken from http://proceedings.esri.com/library/userconf/proc98/proceed/to850/pap842/p842.htm
     74        ColorMap.create("Physical_US",
     75                new Color[]{
     76                        new Color(18,129,242),
     77                        new Color(113,153,89),
     78                        new Color(117,170,101),
     79                        new Color(149,190,113),
     80                        new Color(178,214,117),
     81                        new Color(202,226,149),
     82                        new Color(222,238,161),
     83                        new Color(242,238,161),
     84                        new Color(238,222,153),
     85                        new Color(242,206,133),
     86                        new Color(234,182,129),
     87                        new Color(218,157,121),
     88                        new Color(194,141,125),
     89                        new Color(214,157,145),
     90                        new Color(226,174,165),
     91                        new Color(222,186,182),
     92                        new Color(238,198,210),
     93                        new Color(255,206,226),
     94                        new Color(250,218,234),
     95                        new Color(255,222,230),
     96                        new Color(255,230,242),
     97                        new Color(255,242,255)
     98        },
     99                // elevation in meters - the page above uses feet, so these values differs slightly
     100                new int[]{
     101                        -3000,
     102                        0,
     103                        150,
     104                        300,
     105                        450,
     106                        600,
     107                        750,
     108                        900,
     109                        1050,
     110                        1200,
     111                        1350,
     112                        1500,
     113                        1650,
     114                        1800,
     115                        1950,
     116                        2100,
     117                        2250,
     118                        2400,
     119                        2550,
     120                        2700,
     121                        2750,
     122                        3000
     123        }
     124                );
     125    }
    126126}
  • applications/editors/josm/plugins/ElevationProfile/src/org/openstreetmap/josm/plugins/elevation/HgtReader.java

    r30737 r32315  
    2222 */
    2323public class HgtReader {
    24         private static final int SECONDS_PER_MINUTE = 60;
     24    private static final int SECONDS_PER_MINUTE = 60;
    2525
    26         public static final String HGT_EXT = ".hgt";
     26    public static final String HGT_EXT = ".hgt";
    2727
    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
     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
    3232
    33         private final HashMap<String, ShortBuffer> cache = new HashMap<>();
     33    private final HashMap<String, ShortBuffer> cache = new HashMap<>();
    3434
    35         public double getElevationFromHgt(LatLon coor) {
    36                 try {
    37                         String file = getHgtFileName(coor);
    38                         // given area in cache?
    39                         if (!cache.containsKey(file)) {
     35    public double getElevationFromHgt(LatLon coor) {
     36        try {
     37            String file = getHgtFileName(coor);
     38            // given area in cache?
     39            if (!cache.containsKey(file)) {
    4040
    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                         }
     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            }
    5858
    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         }
     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    }
    7272
    73         @SuppressWarnings("resource")
    74         private ShortBuffer readHgtFile(String file) throws Exception {
    75                 CheckParameterUtil.ensureParameterNotNull(file);
     73    @SuppressWarnings("resource")
     74    private ShortBuffer readHgtFile(String file) throws Exception {
     75        CheckParameterUtil.ensureParameterNotNull(file);
    7676
    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
     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
    8383
    84                         ByteBuffer bb = ByteBuffer.allocateDirect((int) fc.size());
    85                         while (bb.remaining() > 0) fc.read(bb);
     84            ByteBuffer bb = ByteBuffer.allocateDirect((int) fc.size());
     85            while (bb.remaining() > 0) fc.read(bb);
    8686
    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                 }
     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        }
    9393
    94                 return sb;
    95         }
     94        return sb;
     95    }
    9696
    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);
     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);
    106106
    107                 ShortBuffer sb = cache.get(tag);
     107        ShortBuffer sb = cache.get(tag);
    108108
    109                 if (sb == null) {
    110                         return ElevationHelper.NO_ELEVATION;
    111                 }
     109        if (sb == null) {
     110            return ElevationHelper.NO_ELEVATION;
     111        }
    112112
    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;
     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;
    116116
    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);
     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);
    120120
    121                 row = HGT_ROW_LENGTH - row;
    122                 int cell = (HGT_ROW_LENGTH*  (row - 1)) + col;
     121        row = HGT_ROW_LENGTH - row;
     122        int cell = (HGT_ROW_LENGTH*  (row - 1)) + col;
    123123
    124                 //System.out.println("Read SRTM elevation data from row/col/cell " + row + "," + col + ", " + cell + ", " + sb.limit());
     124        //System.out.println("Read SRTM elevation data from row/col/cell " + row + "," + col + ", " + cell + ", " + sb.limit());
    125125
    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         }
     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    }
    140140
    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();
     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();
    152152
    153                 String latPref = "N";
    154                 if (lat < 0) latPref = "S";
     153        String latPref = "N";
     154        if (lat < 0) latPref = "S";
    155155
    156                 String lonPref = "E";
    157                 if (lon < 0) {
    158                         lonPref = "W";
    159                 }
     156        String lonPref = "E";
     157        if (lon < 0) {
     158            lonPref = "W";
     159        }
    160160
    161                 return String.format("%s%02d%s%03d%s", latPref, lat, lonPref, lon, HGT_EXT);
    162         }
     161        return String.format("%s%02d%s%03d%s", latPref, lat, lonPref, lon, HGT_EXT);
     162    }
    163163
    164         public static double frac(double d) {
    165                 long iPart;
    166                 double fPart;
     164    public static double frac(double d) {
     165        long iPart;
     166        double fPart;
    167167
    168                 // Get user input
    169                 iPart = (long) d;
    170                 fPart = d - iPart;
    171                 return fPart;
    172         }
     168        // Get user input
     169        iPart = (long) d;
     170        fPart = d - iPart;
     171        return fPart;
     172    }
    173173}
  • applications/editors/josm/plugins/ElevationProfile/src/org/openstreetmap/josm/plugins/elevation/actions/AddElevationLayerAction.java

    r30344 r32315  
    1313public class AddElevationLayerAction extends JosmAction {
    1414
    15         /**
    16         *
    17         */
    18         private static final long serialVersionUID = -745642875640041385L;
    19         private Layer currentLayer;
     15    /**
     16    *
     17    */
     18    private static final long serialVersionUID = -745642875640041385L;
     19    private Layer currentLayer;
    2020
    21         public AddElevationLayerAction() {
    22                 super(tr("Elevation Grid Layer (experimental!)"), "elevation", tr("Shows elevation grid layer"), null, true);
    23         }
     21    public AddElevationLayerAction() {
     22        super(tr("Elevation Grid Layer (experimental!)"), "elevation", tr("Shows elevation grid layer"), null, true);
     23    }
    2424
    25         @Override
    26         public void actionPerformed(ActionEvent arg0) {
    27                 if (currentLayer == null) {
    28                         currentLayer = new ElevationGridLayer(tr("Elevation Grid")); // TODO: Better name
    29                         Main.main.addLayer(currentLayer);
    30                 }
    31         }
     25    @Override
     26    public void actionPerformed(ActionEvent arg0) {
     27        if (currentLayer == null) {
     28            currentLayer = new ElevationGridLayer(tr("Elevation Grid")); // TODO: Better name
     29            Main.main.addLayer(currentLayer);
     30        }
     31    }
    3232}
  • applications/editors/josm/plugins/ElevationProfile/src/org/openstreetmap/josm/plugins/elevation/gpx/ElevationModel.java

    r30737 r32315  
    2424 */
    2525public class ElevationModel implements IGpxVisitor, IElevationModel {
    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         }
     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    }
    245245}
  • applications/editors/josm/plugins/ElevationProfile/src/org/openstreetmap/josm/plugins/elevation/gpx/ElevationProfile.java

    r30737 r32315  
    3434public class ElevationProfile implements IElevationProfile,
    3535IGpxWaypointVisitor {
    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()
     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()
    433433                + ", minHeight=" + getMinHeight() + ", maxHeight="
    434434                + getMaxHeight() + "]";*/
    435         }
     435    }
    436436}
  • applications/editors/josm/plugins/ElevationProfile/src/org/openstreetmap/josm/plugins/elevation/grid/EleVertex.java

    r30737 r32315  
    1010
    1111public class EleVertex {
    12         private static final int NPOINTS = 3;
    13         private static final double MIN_DIST = 90;
    14 
    15         private double avrgEle = Double.NaN;
    16         private double area = Double.NaN;
    17         private final EleCoordinate[] points = new EleCoordinate[NPOINTS];
    18 
    19         public EleVertex(EleCoordinate p1, EleCoordinate p2, EleCoordinate p3) {
    20                 points[0] = p1;
    21                 points[1] = p2;
    22                 points[2] = p3;
    23 
    24                 // compute elevation
    25                 double z = 0D;
    26                 boolean eleValid = true;
    27                 for (EleCoordinate point : points) {
    28                         if (ElevationHelper.isValidElevation(p1.getEle())) {
    29                                 z += point.getEle();
    30                         } else {
    31                                 eleValid = false;
    32                                 break;
    33                         }
    34                 }
    35 
    36                 if (eleValid) {
    37                         avrgEle = z / NPOINTS;
    38                 } else {
    39                         avrgEle = ElevationHelper.NO_ELEVATION;
    40                 }
    41 
    42                 // compute the (approx.!) area of the vertex using heron's formula
    43                 double a = p1.greatCircleDistance(p2);
    44                 double b = p2.greatCircleDistance(p3);
    45                 double c = p1.greatCircleDistance(p3);
    46 
    47                 double s = (a + b + c) / 2D;
    48                 double sq = s * (s - a) * (s - b) * (s - c);
    49                 area = Math.sqrt(sq);
    50         }
    51 
    52         public List<EleVertex> divide() {
    53                 TriangleEdge[] edges = new TriangleEdge[NPOINTS];
    54 
    55                 int k = 0;
    56                 for (int i = 0; i < points.length; i++) {
    57                         EleCoordinate c1 = points[i];
    58 
    59                         for (int j = i + 1; j < points.length; j++) {
    60                                 EleCoordinate c2 = points[j];
    61 
    62                                 edges[k++] = new TriangleEdge(i, j, c1.greatCircleDistance(c2));
    63                         }
    64                 }
    65 
    66                 /*
     12    private static final int NPOINTS = 3;
     13    private static final double MIN_DIST = 90;
     14
     15    private double avrgEle = Double.NaN;
     16    private double area = Double.NaN;
     17    private final EleCoordinate[] points = new EleCoordinate[NPOINTS];
     18
     19    public EleVertex(EleCoordinate p1, EleCoordinate p2, EleCoordinate p3) {
     20        points[0] = p1;
     21        points[1] = p2;
     22        points[2] = p3;
     23
     24        // compute elevation
     25        double z = 0D;
     26        boolean eleValid = true;
     27        for (EleCoordinate point : points) {
     28            if (ElevationHelper.isValidElevation(p1.getEle())) {
     29                z += point.getEle();
     30            } else {
     31                eleValid = false;
     32                break;
     33            }
     34        }
     35
     36        if (eleValid) {
     37            avrgEle = z / NPOINTS;
     38        } else {
     39            avrgEle = ElevationHelper.NO_ELEVATION;
     40        }
     41
     42        // compute the (approx.!) area of the vertex using heron's formula
     43        double a = p1.greatCircleDistance(p2);
     44        double b = p2.greatCircleDistance(p3);
     45        double c = p1.greatCircleDistance(p3);
     46
     47        double s = (a + b + c) / 2D;
     48        double sq = s * (s - a) * (s - b) * (s - c);
     49        area = Math.sqrt(sq);
     50    }
     51
     52    public List<EleVertex> divide() {
     53        TriangleEdge[] edges = new TriangleEdge[NPOINTS];
     54
     55        int k = 0;
     56        for (int i = 0; i < points.length; i++) {
     57            EleCoordinate c1 = points[i];
     58
     59            for (int j = i + 1; j < points.length; j++) {
     60                EleCoordinate c2 = points[j];
     61
     62                edges[k++] = new TriangleEdge(i, j, c1.greatCircleDistance(c2));
     63            }
     64        }
     65
     66        /*
    6767    for (int i = 0; i < edges.length; i++) {
    6868        TriangleEdge triangleEdge = edges[i];
     
    7070    }*/
    7171
    72                 // sort by distance
    73                 Arrays.sort(edges);
    74                 // pick the longest edge
    75                 TriangleEdge longest = edges[0];
    76 
    77 
    78                 //System.out.println("Longest " + longest);
    79                 EleCoordinate pI = points[longest.getI()];
    80                 EleCoordinate pJ = points[longest.getJ()];
    81                 EleCoordinate pK = points[longest.getK()];
    82                 EleCoordinate newP = getMid(pI, pJ);
    83                 /*
     72        // sort by distance
     73        Arrays.sort(edges);
     74        // pick the longest edge
     75        TriangleEdge longest = edges[0];
     76
     77
     78        //System.out.println("Longest " + longest);
     79        EleCoordinate pI = points[longest.getI()];
     80        EleCoordinate pJ = points[longest.getJ()];
     81        EleCoordinate pK = points[longest.getK()];
     82        EleCoordinate newP = getMid(pI, pJ);
     83        /*
    8484    System.out.println(pI);
    8585    System.out.println(pJ);
    8686    System.out.println(pK);
    8787    System.out.println(newP);
    88                 */
    89                 List<EleVertex> res = new ArrayList<>();
    90                 res.add(new EleVertex(pI, pK, newP));
    91                 res.add(new EleVertex(pJ, pK, newP));
    92 
    93                 return res;
    94         }
    95 
    96         /**
    97         * Checks if vertex requires further processing or is finished. Currently this
    98         * method returns <code>true</code>, if the average deviation is < 5m
    99         *
    100         * @return true, if is finished
    101         */
    102         public boolean isFinished() {
    103                 /*double z = 0D;
     88        */
     89        List<EleVertex> res = new ArrayList<>();
     90        res.add(new EleVertex(pI, pK, newP));
     91        res.add(new EleVertex(pJ, pK, newP));
     92
     93        return res;
     94    }
     95
     96    /**
     97    * Checks if vertex requires further processing or is finished. Currently this
     98    * method returns <code>true</code>, if the average deviation is < 5m
     99    *
     100    * @return true, if is finished
     101    */
     102    public boolean isFinished() {
     103        /*double z = 0D;
    104104        double avrgEle = getEle();
    105105
     
    108108        }*/
    109109
    110                 // TODO: Check for proper limit
    111                 return /*z < 75 || */getArea() < (30 * 30); // = 3 * 25
    112         }
    113 
    114         /**
    115         * Gets the approximate area of this vertex in square meters.
    116         *
    117         * @return the area
    118         */
    119         public double getArea() {
    120                 return area;
    121         }
    122 
    123         /**
    124         * Gets the (linear interpolated) mid point of c1 and c2.
    125         *
    126         * @param c1 the first coordinate
    127         * @param c2 the second coordinate
    128         * @return the mid point
    129         */
    130         public EleCoordinate getMid(EleCoordinate c1, EleCoordinate c2) {
    131                 double x = (c1.getX() + c2.getX()) / 2.0;
    132                 double y = (c1.getY() + c2.getY()) / 2.0;
    133 
    134                 double z = (c1.getEle() + c2.getEle()) / 2.0;
    135                 if (c1.greatCircleDistance(c2) > MIN_DIST) {
    136                         double hgtZ = ElevationHelper.getSrtmElevation(new LatLon(y, x));
    137 
    138                         if (ElevationHelper.isValidElevation(hgtZ)) {
    139                                 z = hgtZ;
    140                         }
    141                 }
    142 
    143                 return new EleCoordinate(y, x, z);
    144         }
    145 
    146         /**
    147         * Gets the coordinate for the given index.
    148         *
    149         * @param index the index between 0 and NPOINTS:
    150         * @return the elevation coordinate instance
    151         * @throws IllegalArgumentException, if index is invalid
    152         */
    153         public EleCoordinate get(int index) {
    154                 if (index < 0 || index >= NPOINTS) throw new IllegalArgumentException("Invalid index: " + index);
    155 
    156                 return points[index];
    157         }
    158 
    159         /**
    160         * Gets the average elevation of this vertex.
    161         *
    162         * @return the ele
    163         */
    164         public double getEle() {
    165 
    166                 return avrgEle;
    167         }
    168 
    169         @Override
    170         public String toString() {
    171                 return "EleVertex [avrgEle=" + avrgEle + ", area=" + area + ", points="
    172                                 + Arrays.toString(points) + "]";
    173         }
    174 
    175 
    176 
    177 
    178         class TriangleEdge implements Comparable<TriangleEdge> {
    179                 private final int i;
    180                 private final int j;
    181                 private final double dist;
    182 
    183                 public TriangleEdge(int i, int j, double dist) {
    184                         super();
    185                         this.i = i;
    186                         this.j = j;
    187                         this.dist = dist;
    188                 }
    189 
    190                 public int getI() {
    191                         return i;
    192                 }
    193 
    194                 public int getJ() {
    195                         return j;
    196                 }
    197 
    198                 public int getK() {
    199                         if (i == 0) {
    200                                 return j == 1 ? 2 : 1;
    201                         } else if (i == 1) {
    202                                 return j == 0 ? 2 : 0;
    203                         } else {
    204                                 return j == 0 ? 1 : 0;
    205                         }
    206                 }
    207 
    208                 public double getDist() {
    209                         return dist;
    210                 }
    211 
    212                 @Override
    213                 public int compareTo(TriangleEdge o) {
    214                         return (int) (o.getDist() - dist);
    215                 }
    216 
    217                 @Override
    218                 public String toString() {
    219                         return "TriangleEdge [i=" + i + ", j=" + j + ", dist=" + dist + "]";
    220                 }
    221         }
     110        // TODO: Check for proper limit
     111        return /*z < 75 || */getArea() < (30 * 30); // = 3 * 25
     112    }
     113
     114    /**
     115    * Gets the approximate area of this vertex in square meters.
     116    *
     117    * @return the area
     118    */
     119    public double getArea() {
     120        return area;
     121    }
     122
     123    /**
     124    * Gets the (linear interpolated) mid point of c1 and c2.
     125    *
     126    * @param c1 the first coordinate
     127    * @param c2 the second coordinate
     128    * @return the mid point
     129    */
     130    public EleCoordinate getMid(EleCoordinate c1, EleCoordinate c2) {
     131        double x = (c1.getX() + c2.getX()) / 2.0;
     132        double y = (c1.getY() + c2.getY()) / 2.0;
     133
     134        double z = (c1.getEle() + c2.getEle()) / 2.0;
     135        if (c1.greatCircleDistance(c2) > MIN_DIST) {
     136            double hgtZ = ElevationHelper.getSrtmElevation(new LatLon(y, x));
     137
     138            if (ElevationHelper.isValidElevation(hgtZ)) {
     139                z = hgtZ;
     140            }
     141        }
     142
     143        return new EleCoordinate(y, x, z);
     144    }
     145
     146    /**
     147    * Gets the coordinate for the given index.
     148    *
     149    * @param index the index between 0 and NPOINTS:
     150    * @return the elevation coordinate instance
     151    * @throws IllegalArgumentException, if index is invalid
     152    */
     153    public EleCoordinate get(int index) {
     154        if (index < 0 || index >= NPOINTS) throw new IllegalArgumentException("Invalid index: " + index);
     155
     156        return points[index];
     157    }
     158
     159    /**
     160    * Gets the average elevation of this vertex.
     161    *
     162    * @return the ele
     163    */
     164    public double getEle() {
     165
     166        return avrgEle;
     167    }
     168
     169    @Override
     170    public String toString() {
     171        return "EleVertex [avrgEle=" + avrgEle + ", area=" + area + ", points="
     172                + Arrays.toString(points) + "]";
     173    }
     174
     175
     176
     177
     178    class TriangleEdge implements Comparable<TriangleEdge> {
     179        private final int i;
     180        private final int j;
     181        private final double dist;
     182
     183        public TriangleEdge(int i, int j, double dist) {
     184            super();
     185            this.i = i;
     186            this.j = j;
     187            this.dist = dist;
     188        }
     189
     190        public int getI() {
     191            return i;
     192        }
     193
     194        public int getJ() {
     195            return j;
     196        }
     197
     198        public int getK() {
     199            if (i == 0) {
     200                return j == 1 ? 2 : 1;
     201            } else if (i == 1) {
     202                return j == 0 ? 2 : 0;
     203            } else {
     204                return j == 0 ? 1 : 0;
     205            }
     206        }
     207
     208        public double getDist() {
     209            return dist;
     210        }
     211
     212        @Override
     213        public int compareTo(TriangleEdge o) {
     214            return (int) (o.getDist() - dist);
     215        }
     216
     217        @Override
     218        public String toString() {
     219            return "TriangleEdge [i=" + i + ", j=" + j + ", dist=" + dist + "]";
     220        }
     221    }
    222222}
  • applications/editors/josm/plugins/ElevationProfile/src/org/openstreetmap/josm/plugins/elevation/grid/ElevationGridLayer.java

    r31574 r32315  
    3232 */
    3333public class ElevationGridLayer extends Layer implements TileLoaderListener {
    34         private static final int ELE_ZOOM_LEVEL = 13;
    35         private final IVertexRenderer vertexRenderer;
    36         private final MemoryTileCache tileCache;
    37         protected TileSource tileSource;
    38         protected ElevationGridTileLoader tileLoader;
    39         protected TileController tileController;
    40 
    41         private Bounds lastBounds;
    42         private TileSet tileSet;
    43 
    44         /**
    45         * @param info
    46         */
    47         public ElevationGridLayer(String name) {
    48                 super(name);
    49 
    50                 setOpacity(0.8);
    51                 setBackgroundLayer(true);
    52                 vertexRenderer = new SimpleVertexRenderer();
    53 
    54                 tileCache = new MemoryTileCache();
    55                 tileCache.setCacheSize(500);
    56                 tileSource = new ElevationGridTileSource(name);
    57                 tileLoader = new ElevationGridTileLoader(this);
    58                 tileController = new ElevationGridTileController(tileSource, tileCache, this, tileLoader);
    59         }
    60 
    61         @Override
    62         public void paint(Graphics2D g, MapView mv, Bounds box) {
    63                 boolean needsNewTileSet = tileSet == null || (lastBounds == null || !lastBounds.equals(box));
    64 
    65                 if (needsNewTileSet) {
    66                         tileSet = new TileSet(box.getMin(), box.getMax(), ELE_ZOOM_LEVEL); // we use a vector format with constant zoom level
    67                         lastBounds = box;
    68                         System.out.println("paint " + tileSet);
    69                 }
    70 
    71                 if (tileSet.insane()) {
    72                         myDrawString(g, tr("zoom in to load any tiles"), 120, 120);
    73                         return;
    74                 } else if (tileSet.tooLarge()) {
    75                         myDrawString(g, tr("zoom in to load more tiles"), 120, 120);
    76                         return;
    77                 } else if (tileSet.tooSmall()) {
    78                         myDrawString(g, tr("increase zoom level to see more detail"), 120, 120);
    79                         return;
    80                 }
    81 
    82                 for(int x = tileSet.x0; x <= tileSet.x1; x++) {
    83                         for(int y = tileSet.y0; y <= tileSet.y1; y++) {
    84                                 Tile t = tileController.getTile(x, y, ELE_ZOOM_LEVEL);
    85 
    86                                 if (t != null && t.isLoaded() && t instanceof ElevationGridTile) {
    87                                         ((ElevationGridTile)t).paintTile(g, mv, vertexRenderer);
    88                                 } else {
    89                                         // give some consolation...
    90                                         Point topLeft = mv.getPoint(new LatLon(tileSource.tileXYToLatLon(x, y, ELE_ZOOM_LEVEL)));
    91                                         t.paint(g, topLeft.x, topLeft.y);
    92                                 }
    93                         }
    94                 }
    95         }
    96 
    97         @Override
    98         public String getToolTipText() {
    99                 // TODO Auto-generated method stub
    100                 return null;
    101         }
    102 
    103         @Override
    104         public void visitBoundingBox(BoundingXYVisitor v) {
    105                 // TODO Auto-generated method stub
    106 
    107         }
    108 
    109         @Override
    110         public Action[] getMenuEntries() {
    111                 // TODO Auto-generated method stub
    112                 return null;
    113         }
    114 
    115         @Override
    116         public void tileLoadingFinished(Tile tile, boolean success) {
    117                 try {
    118                         if (Main.map != null) {
    119                                 Main.map.repaint(100);
    120                         }
    121                 } catch(Exception ex) {
    122                         System.err.println(ex);
    123                         ex.printStackTrace(System.err);
    124                 }
    125         }
    126 
    127         @Override
    128         public Icon getIcon() {
    129                 return ImageProvider.get("layer", "elevation");
    130         }
    131 
    132         @Override
    133         public void mergeFrom(Layer from) {
    134                 // TODO Auto-generated method stub
    135 
    136         }
    137 
    138         @Override
    139         public boolean isMergable(Layer other) {
    140                 // TODO Auto-generated method stub
    141                 return false;
    142         }
    143 
    144         @Override
    145         public Object getInfoComponent() {
    146                 // TODO Auto-generated method stub
    147                 return null;
    148         }
    149 
    150 
    151         // Stolen from TMSLayer...
    152         void myDrawString(Graphics g, String text, int x, int y) {
    153                 Color oldColor = g.getColor();
    154                 g.setColor(Color.black);
    155                 g.drawString(text,x+1,y+1);
    156                 g.setColor(oldColor);
    157                 g.drawString(text,x,y);
    158         }
    159 
    160         private class TileSet {
    161                 int x0, x1, y0, y1;
    162                 int tileMax = -1;
    163 
    164                 /**
    165                 * Create a TileSet by known LatLon bbox without layer shift correction
    166                 */
    167                 public TileSet(LatLon topLeft, LatLon botRight, int zoom) {
    168                         if (zoom == 0)
    169                                 return;
    170 
    171                         TileXY p0 = tileSource.latLonToTileXY(topLeft.lat(), topLeft.lon(), zoom);
    172                         TileXY p1 = tileSource.latLonToTileXY(botRight.lat(), botRight.lon(), zoom);
    173 
    174                         x0 = p0.getXIndex();
    175                         y0 = p0.getYIndex();
    176                         x1 = p1.getXIndex();
    177                         y1 = p1.getYIndex();
    178                         if (x0 > x1) {
    179                                 int tmp = x0;
    180                                 x0 = x1;
    181                                 x1 = tmp;
    182                         }
    183                         if (y0 > y1) {
    184                                 int tmp = y0;
    185                                 y0 = y1;
    186                                 y1 = tmp;
    187                         }
    188                         tileMax = (int)Math.pow(2.0, zoom);
    189                         if (x0 < 0) {
    190                                 x0 = 0;
    191                         }
    192                         if (y0 < 0) {
    193                                 y0 = 0;
    194                         }
    195                         if (x1 > tileMax) {
    196                                 x1 = tileMax;
    197                         }
    198                         if (y1 > tileMax) {
    199                                 y1 = tileMax;
    200                         }
    201                 }
    202 
    203                 int size() {
    204                         int x_span = x1 - x0 + 1;
    205                         int y_span = y1 - y0 + 1;
    206                         return x_span * y_span;
    207                 }
    208 
    209                 @Override
    210                 public String toString() {
    211                         return "TileSet [x0=" + x0 + ", x1=" + x1 + ", y0=" + y0 + ", y1="
    212                                         + y1 + ", size()=" + size() + ", tilesSpanned()="
    213                                         + tilesSpanned() + "]";
    214                 }
    215 
    216                 double tilesSpanned() {
    217                         return Math.sqrt(1.0 * this.size());
    218                 }
    219 
    220                 boolean tooSmall() {
    221                         return this.tilesSpanned() < 1;
    222                 }
    223 
    224                 boolean tooLarge() {
    225                         return this.tilesSpanned() > 50;
    226                 }
    227 
    228                 boolean insane() {
    229                         return this.tilesSpanned() > 200;
    230                 }
    231         }
     34    private static final int ELE_ZOOM_LEVEL = 13;
     35    private final IVertexRenderer vertexRenderer;
     36    private final MemoryTileCache tileCache;
     37    protected TileSource tileSource;
     38    protected ElevationGridTileLoader tileLoader;
     39    protected TileController tileController;
     40
     41    private Bounds lastBounds;
     42    private TileSet tileSet;
     43
     44    /**
     45    * @param info
     46    */
     47    public ElevationGridLayer(String name) {
     48        super(name);
     49
     50        setOpacity(0.8);
     51        setBackgroundLayer(true);
     52        vertexRenderer = new SimpleVertexRenderer();
     53
     54        tileCache = new MemoryTileCache();
     55        tileCache.setCacheSize(500);
     56        tileSource = new ElevationGridTileSource(name);
     57        tileLoader = new ElevationGridTileLoader(this);
     58        tileController = new ElevationGridTileController(tileSource, tileCache, this, tileLoader);
     59    }
     60
     61    @Override
     62    public void paint(Graphics2D g, MapView mv, Bounds box) {
     63        boolean needsNewTileSet = tileSet == null || (lastBounds == null || !lastBounds.equals(box));
     64
     65        if (needsNewTileSet) {
     66            tileSet = new TileSet(box.getMin(), box.getMax(), ELE_ZOOM_LEVEL); // we use a vector format with constant zoom level
     67            lastBounds = box;
     68            System.out.println("paint " + tileSet);
     69        }
     70
     71        if (tileSet.insane()) {
     72            myDrawString(g, tr("zoom in to load any tiles"), 120, 120);
     73            return;
     74        } else if (tileSet.tooLarge()) {
     75            myDrawString(g, tr("zoom in to load more tiles"), 120, 120);
     76            return;
     77        } else if (tileSet.tooSmall()) {
     78            myDrawString(g, tr("increase zoom level to see more detail"), 120, 120);
     79            return;
     80        }
     81
     82        for(int x = tileSet.x0; x <= tileSet.x1; x++) {
     83            for(int y = tileSet.y0; y <= tileSet.y1; y++) {
     84                Tile t = tileController.getTile(x, y, ELE_ZOOM_LEVEL);
     85
     86                if (t != null && t.isLoaded() && t instanceof ElevationGridTile) {
     87                    ((ElevationGridTile)t).paintTile(g, mv, vertexRenderer);
     88                } else {
     89                    // give some consolation...
     90                    Point topLeft = mv.getPoint(new LatLon(tileSource.tileXYToLatLon(x, y, ELE_ZOOM_LEVEL)));
     91                    t.paint(g, topLeft.x, topLeft.y);
     92                }
     93            }
     94        }
     95    }
     96
     97    @Override
     98    public String getToolTipText() {
     99        // TODO Auto-generated method stub
     100        return null;
     101    }
     102
     103    @Override
     104    public void visitBoundingBox(BoundingXYVisitor v) {
     105        // TODO Auto-generated method stub
     106
     107    }
     108
     109    @Override
     110    public Action[] getMenuEntries() {
     111        // TODO Auto-generated method stub
     112        return null;
     113    }
     114
     115    @Override
     116    public void tileLoadingFinished(Tile tile, boolean success) {
     117        try {
     118            if (Main.map != null) {
     119                Main.map.repaint(100);
     120            }
     121        } catch(Exception ex) {
     122            System.err.println(ex);
     123            ex.printStackTrace(System.err);
     124        }
     125    }
     126
     127    @Override
     128    public Icon getIcon() {
     129        return ImageProvider.get("layer", "elevation");
     130    }
     131
     132    @Override
     133    public void mergeFrom(Layer from) {
     134        // TODO Auto-generated method stub
     135
     136    }
     137
     138    @Override
     139    public boolean isMergable(Layer other) {
     140        // TODO Auto-generated method stub
     141        return false;
     142    }
     143
     144    @Override
     145    public Object getInfoComponent() {
     146        // TODO Auto-generated method stub
     147        return null;
     148    }
     149
     150
     151    // Stolen from TMSLayer...
     152    void myDrawString(Graphics g, String text, int x, int y) {
     153        Color oldColor = g.getColor();
     154        g.setColor(Color.black);
     155        g.drawString(text,x+1,y+1);
     156        g.setColor(oldColor);
     157        g.drawString(text,x,y);
     158    }
     159
     160    private class TileSet {
     161        int x0, x1, y0, y1;
     162        int tileMax = -1;
     163
     164        /**
     165        * Create a TileSet by known LatLon bbox without layer shift correction
     166        */
     167        public TileSet(LatLon topLeft, LatLon botRight, int zoom) {
     168            if (zoom == 0)
     169                return;
     170
     171            TileXY p0 = tileSource.latLonToTileXY(topLeft.lat(), topLeft.lon(), zoom);
     172            TileXY p1 = tileSource.latLonToTileXY(botRight.lat(), botRight.lon(), zoom);
     173
     174            x0 = p0.getXIndex();
     175            y0 = p0.getYIndex();
     176            x1 = p1.getXIndex();
     177            y1 = p1.getYIndex();
     178            if (x0 > x1) {
     179                int tmp = x0;
     180                x0 = x1;
     181                x1 = tmp;
     182            }
     183            if (y0 > y1) {
     184                int tmp = y0;
     185                y0 = y1;
     186                y1 = tmp;
     187            }
     188            tileMax = (int)Math.pow(2.0, zoom);
     189            if (x0 < 0) {
     190                x0 = 0;
     191            }
     192            if (y0 < 0) {
     193                y0 = 0;
     194            }
     195            if (x1 > tileMax) {
     196                x1 = tileMax;
     197            }
     198            if (y1 > tileMax) {
     199                y1 = tileMax;
     200            }
     201        }
     202
     203        int size() {
     204            int x_span = x1 - x0 + 1;
     205            int y_span = y1 - y0 + 1;
     206            return x_span * y_span;
     207        }
     208
     209        @Override
     210        public String toString() {
     211            return "TileSet [x0=" + x0 + ", x1=" + x1 + ", y0=" + y0 + ", y1="
     212                    + y1 + ", size()=" + size() + ", tilesSpanned()="
     213                    + tilesSpanned() + "]";
     214        }
     215
     216        double tilesSpanned() {
     217            return Math.sqrt(1.0 * this.size());
     218        }
     219
     220        boolean tooSmall() {
     221            return this.tilesSpanned() < 1;
     222        }
     223
     224        boolean tooLarge() {
     225            return this.tilesSpanned() > 50;
     226        }
     227
     228        boolean insane() {
     229            return this.tilesSpanned() > 200;
     230        }
     231    }
    232232}
  • applications/editors/josm/plugins/ElevationProfile/src/org/openstreetmap/josm/plugins/elevation/grid/ElevationGridTile.java

    r31574 r32315  
    2525
    2626public class ElevationGridTile extends Tile {
    27         private final BlockingDeque<EleVertex> toDo = new LinkedBlockingDeque<>();
    28         private final BlockingDeque<EleVertex> vertices = new LinkedBlockingDeque<>();
     27    private final BlockingDeque<EleVertex> toDo = new LinkedBlockingDeque<>();
     28    private final BlockingDeque<EleVertex> vertices = new LinkedBlockingDeque<>();
    2929
    30         private Bounds box;
     30    private Bounds box;
    3131
    32         public ElevationGridTile(TileSource source, int xtile, int ytile, int zoom) {
    33                 super(source, xtile, ytile, zoom);
     32    public ElevationGridTile(TileSource source, int xtile, int ytile, int zoom) {
     33        super(source, xtile, ytile, zoom);
    3434
    35                 box = tile2Bounds(xtile, ytile, zoom);
    36                 initQueue();
    37         }
     35        box = tile2Bounds(xtile, ytile, zoom);
     36        initQueue();
     37    }
    3838
    39         public ElevationGridTile(TileSource source, int xtile, int ytile, int zoom,
    40                         BufferedImage image) {
    41                 super(source, xtile, ytile, zoom, image);
     39    public ElevationGridTile(TileSource source, int xtile, int ytile, int zoom,
     40            BufferedImage image) {
     41        super(source, xtile, ytile, zoom, image);
    4242
    4343
    44         }
     44    }
    4545
    46         @Override
    47         public void loadPlaceholderFromCache(TileCache cache) {
    48                 // TODO Auto-generated method stub
    49                 super.loadPlaceholderFromCache(cache);
     46    @Override
     47    public void loadPlaceholderFromCache(TileCache cache) {
     48        // TODO Auto-generated method stub
     49        super.loadPlaceholderFromCache(cache);
    5050
    51                 //System.out.println("loadPlaceholderFromCache");
    52         }
     51        //System.out.println("loadPlaceholderFromCache");
     52    }
    5353
    54         @Override
    55         public String getUrl() throws IOException {
    56                 // TODO Auto-generated method stub
    57                 return super.getUrl();
    58         }
     54    @Override
     55    public String getUrl() throws IOException {
     56        // TODO Auto-generated method stub
     57        return super.getUrl();
     58    }
    5959
    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);
     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);
    6666
    67                 //g.drawString(String.format("EGT %d/%d ", getXtile(), getYtile()), x, y);
    68                 g.drawString(getStatus(), x, y);
    69         }
     67        //g.drawString(String.format("EGT %d/%d ", getXtile(), getYtile()), x, y);
     68        g.drawString(getStatus(), x, y);
     69    }
    7070
    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);
     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);
    8585
    86                         // obtain vertex color
    87                         g.setColor(vertexRenderer.getElevationColor(eleVertex));
    88                         // TODO: Move to renderer
    89                         g.fill(shape);
    90                 }
    91         }
     86            // obtain vertex color
     87            g.setColor(vertexRenderer.getElevationColor(eleVertex));
     88            // TODO: Move to renderer
     89            g.fill(shape);
     90        }
     91    }
    9292
    93         @Override
    94         public void loadImage(InputStream input) throws IOException {
    95                 if (isLoaded()) return;
     93    @Override
     94    public void loadImage(InputStream input) throws IOException {
     95        if (isLoaded()) return;
    9696
    97                 // TODO: Save
     97        // TODO: Save
    9898
    99                 // We abuse the loadImage method to render the vertices...
    100                 //
    101                 while (toDo.size() > 0) {
    102                         EleVertex vertex = toDo.poll();
     99        // We abuse the loadImage method to render the vertices...
     100        //
     101        while (toDo.size() > 0) {
     102            EleVertex vertex = toDo.poll();
    103103
    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         }
     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    }
    115115
    116         public BlockingDeque<EleVertex> getVertices() {
    117                 return vertices;
    118         }
     116    public BlockingDeque<EleVertex> getVertices() {
     117        return vertices;
     118    }
    119119
    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                 return new Bounds(
    129                                 new LatLon(source.tileXYToLatLon(x, y, zoom)),
    130                                 new LatLon(source.tileXYToLatLon(x + 1, y + 1, zoom)));
    131         }
     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        return new Bounds(
     129                new LatLon(source.tileXYToLatLon(x, y, zoom)),
     130                new LatLon(source.tileXYToLatLon(x + 1, y + 1, zoom)));
     131    }
    132132
    133         /**
    134         * Inits the 'todo' queue with the initial vertices.
    135         */
    136         private void initQueue() {
    137                 LatLon min = box.getMin();
    138                 LatLon max = box.getMax();
     133    /**
     134    * Inits the 'todo' queue with the initial vertices.
     135    */
     136    private void initQueue() {
     137        LatLon min = box.getMin();
     138        LatLon max = box.getMax();
    139139
    140                 // compute missing coordinates
    141                 LatLon h1 = new LatLon(min.lat(), max.lon());
    142                 LatLon h2 = new LatLon(max.lat(), min.lon());
     140        // compute missing coordinates
     141        LatLon h1 = new LatLon(min.lat(), max.lon());
     142        LatLon h2 = new LatLon(max.lat(), min.lon());
    143143
    144                 double eleMin = ElevationHelper.getSrtmElevation(min);
    145                 double eleMax = ElevationHelper.getSrtmElevation(max);
     144        double eleMin = ElevationHelper.getSrtmElevation(min);
     145        double eleMax = ElevationHelper.getSrtmElevation(max);
    146146
    147                 // SRTM files present?
    148                 if (!ElevationHelper.isValidElevation(eleMax) || !ElevationHelper.isValidElevation(eleMin)) {
    149                         setError(tr("No SRTM data"));
    150                         return;
    151                 }
     147        // SRTM files present?
     148        if (!ElevationHelper.isValidElevation(eleMax) || !ElevationHelper.isValidElevation(eleMin)) {
     149            setError(tr("No SRTM data"));
     150            return;
     151        }
    152152
    153                 // compute elevation coords
    154                 EleCoordinate p0 = new EleCoordinate(min, eleMin);
    155                 EleCoordinate p1 = new EleCoordinate(h1, ElevationHelper.getSrtmElevation(h1));
    156                 EleCoordinate p2 = new EleCoordinate(max, eleMax);
    157                 EleCoordinate p3 = new EleCoordinate(h2, ElevationHelper.getSrtmElevation(h2));
     153        // compute elevation coords
     154        EleCoordinate p0 = new EleCoordinate(min, eleMin);
     155        EleCoordinate p1 = new EleCoordinate(h1, ElevationHelper.getSrtmElevation(h1));
     156        EleCoordinate p2 = new EleCoordinate(max, eleMax);
     157        EleCoordinate p3 = new EleCoordinate(h2, ElevationHelper.getSrtmElevation(h2));
    158158
    159                 // compute initial vertices
    160                 EleVertex v1 = new EleVertex(p0, p1, p2);
    161                 EleVertex v2 = new EleVertex(p2, p3, p0);
    162                 // enqueue vertices
    163                 toDo.add(v1);
    164                 toDo.add(v2);
    165         }
     159        // compute initial vertices
     160        EleVertex v1 = new EleVertex(p0, p1, p2);
     161        EleVertex v2 = new EleVertex(p2, p3, p0);
     162        // enqueue vertices
     163        toDo.add(v1);
     164        toDo.add(v2);
     165    }
    166166
    167         @Override
    168         public String toString() {
    169                 return "ElevationGridTile [box=" + box + ", xtile=" + xtile
    170                                 + ", ytile=" + ytile + "]";
    171         }
     167    @Override
     168    public String toString() {
     169        return "ElevationGridTile [box=" + box + ", xtile=" + xtile
     170                + ", ytile=" + ytile + "]";
     171    }
    172172
    173173
  • applications/editors/josm/plugins/ElevationProfile/src/org/openstreetmap/josm/plugins/elevation/grid/ElevationGridTileController.java

    r31544 r32315  
    1616public class ElevationGridTileController extends TileController {
    1717
    18         /**
    19         * @param source
    20         * @param tileCache
    21         * @param listener
    22         */
    23         public ElevationGridTileController(TileSource source, TileCache tileCache,
    24                         TileLoaderListener listener, TileLoader loader) {
    25                 super(source, tileCache, listener);
     18    /**
     19    * @param source
     20    * @param tileCache
     21    * @param listener
     22    */
     23    public ElevationGridTileController(TileSource source, TileCache tileCache,
     24            TileLoaderListener listener, TileLoader loader) {
     25        super(source, tileCache, listener);
    2626
    27                 tileSource = source; // FIXME: hard-coded in base class (although parameter is given)!!
    28                 tileLoader = loader; // FIXME: hard-coded in base class!
    29         }
     27        tileSource = source; // FIXME: hard-coded in base class (although parameter is given)!!
     28        tileLoader = loader; // FIXME: hard-coded in base class!
     29    }
    3030
    31         @Override
    32         public Tile getTile(int tilex, int tiley, int zoom) {
    33                 int max = (1 << zoom);
    34                 if (tilex < 0 || tilex >= max || tiley < 0 || tiley >= max)
    35                         return null;
    36                 Tile tile = tileCache.getTile(tileSource, tilex, tiley, zoom);
    37                 if (tile == null) {
    38                         // FIXME: Provide/use a factory method here
    39                         tile = new ElevationGridTile(tileSource, tilex, tiley, zoom);
    40                         tileCache.addTile(tile);
    41                         tile.loadPlaceholderFromCache(tileCache);
    42                 }
    43                 if (tile.hasError()) {
    44                         tile.loadPlaceholderFromCache(tileCache);
    45                 }
    46                 if (!tile.isLoaded()) {
    47                         tileLoader.createTileLoaderJob(tile).submit();
    48                 }
    49                 return tile;
    50         }
     31    @Override
     32    public Tile getTile(int tilex, int tiley, int zoom) {
     33        int max = (1 << zoom);
     34        if (tilex < 0 || tilex >= max || tiley < 0 || tiley >= max)
     35            return null;
     36        Tile tile = tileCache.getTile(tileSource, tilex, tiley, zoom);
     37        if (tile == null) {
     38            // FIXME: Provide/use a factory method here
     39            tile = new ElevationGridTile(tileSource, tilex, tiley, zoom);
     40            tileCache.addTile(tile);
     41            tile.loadPlaceholderFromCache(tileCache);
     42        }
     43        if (tile.hasError()) {
     44            tile.loadPlaceholderFromCache(tileCache);
     45        }
     46        if (!tile.isLoaded()) {
     47            tileLoader.createTileLoaderJob(tile).submit();
     48        }
     49        return tile;
     50    }
    5151
    52         /**
    53         *
    54         */
     52    /**
     53    *
     54    */
    5555}
  • applications/editors/josm/plugins/ElevationProfile/src/org/openstreetmap/josm/plugins/elevation/grid/ElevationGridTileLoader.java

    r31544 r32315  
    1313 */
    1414public class ElevationGridTileLoader implements TileLoader {
    15         protected TileLoaderListener listener;
     15    protected TileLoaderListener listener;
    1616
    17         public ElevationGridTileLoader(TileLoaderListener listener) {
    18                 CheckParameterUtil.ensureParameterNotNull(listener);
    19                 this.listener = listener;
    20         }
     17    public ElevationGridTileLoader(TileLoaderListener listener) {
     18        CheckParameterUtil.ensureParameterNotNull(listener);
     19        this.listener = listener;
     20    }
    2121
    22         @Override
    23         public TileJob createTileLoaderJob(final Tile tile) {
    24                 CheckParameterUtil.ensureParameterNotNull(tile);
     22    @Override
     23    public TileJob createTileLoaderJob(final Tile tile) {
     24        CheckParameterUtil.ensureParameterNotNull(tile);
    2525
    26                 return new TileJob() {
     26        return new TileJob() {
    2727
    28                         @Override
    29                         public void run() {
    30                                 synchronized (tile) {
    31                                         if ((tile.isLoaded() && !tile.hasError()) || tile.isLoading())
    32                                                 return;
    33                                         tile.initLoading();
    34                                 }
    35                                 try {
    36                                         tile.loadImage(null);
    37                                         tile.setLoaded(true);
    38                                         listener.tileLoadingFinished(tile, true);
    39                                 } catch (Exception e) {
    40                                         tile.setError(e.getMessage());
    41                                         listener.tileLoadingFinished(tile, false);
    42                                 } finally {
    43                                         tile.finishLoading();
    44                                 }
    45                         }
     28            @Override
     29            public void run() {
     30                synchronized (tile) {
     31                    if ((tile.isLoaded() && !tile.hasError()) || tile.isLoading())
     32                        return;
     33                    tile.initLoading();
     34                }
     35                try {
     36                    tile.loadImage(null);
     37                    tile.setLoaded(true);
     38                    listener.tileLoadingFinished(tile, true);
     39                } catch (Exception e) {
     40                    tile.setError(e.getMessage());
     41                    listener.tileLoadingFinished(tile, false);
     42                } finally {
     43                    tile.finishLoading();
     44                }
     45            }
    4646
    47                         @Override
    48                         public Tile getTile() {
    49                                 return tile;
    50                         }
     47            @Override
     48            public Tile getTile() {
     49                return tile;
     50            }
    5151
    52                         @Override
    53                         public void submit() {
    54                                 run();
    55                         }
     52            @Override
     53            public void submit() {
     54                run();
     55            }
    5656
    57                         @Override
    58                         public void submit(boolean force) {
    59                                 submit();
     57            @Override
     58            public void submit(boolean force) {
     59                submit();
    6060
    61                         }
    62                 };
    63         }
     61            }
     62        };
     63    }
    6464
    65         @Override
    66         public void cancelOutstandingTasks() {
    67                 // intentionally left blank
    68         }
     65    @Override
     66    public void cancelOutstandingTasks() {
     67        // intentionally left blank
     68    }
    6969
    7070}
  • applications/editors/josm/plugins/ElevationProfile/src/org/openstreetmap/josm/plugins/elevation/gui/DefaultElevationProfileRenderer.java

    r30737 r32315  
    3333IElevationProfileRenderer {
    3434
    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         }
     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    }
    541541
    542542
  • applications/editors/josm/plugins/ElevationProfile/src/org/openstreetmap/josm/plugins/elevation/gui/ElevationProfileDialog.java

    r31648 r32315  
    4747public class ElevationProfileDialog extends ToggleDialog implements LayerChangeListener, ComponentListener {
    4848
    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(SystemOfMeasurement.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         }
     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(SystemOfMeasurement.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    }
    461461}
  • applications/editors/josm/plugins/ElevationProfile/src/org/openstreetmap/josm/plugins/elevation/gui/ElevationProfilePanel.java

    r30737 r32315  
    3636 */
    3737public class ElevationProfilePanel extends JPanel implements ComponentListener, MouseMotionListener {
    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.
     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.
    254254
    255255    private void drawHCenteredString(String s, int x, int y, Graphics g) {
     
    257257    }*/
    258258
    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         }
     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    }
    518518}
Note: See TracChangeset for help on using the changeset viewer.