Ignore:
Timestamp:
2014-10-18T23:07:52+02:00 (10 years ago)
Author:
donvip
Message:

[josm_plugins] fix Java 7 / unused code warnings

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

Legend:

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

    r30344 r30737  
    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<String, ColorMap>();
    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<ColorMap.ColorMapEntry>();
    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

    r30344 r30737  
    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<WayPoint>(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/HgtReader.java

    r30344 r30737  
    1717/**
    1818 *  Class HgtReader reads data from SRTM HGT files. Currently this class is restricted to a resolution of 3 arc seconds.
    19  * 
     19 *
    2020 *  SRTM data files are available at the <a href="http://dds.cr.usgs.gov/srtm/version2_1/SRTM3">NASA SRTM site</a>
    2121 *  @author Oliver Wieland <oliver.wieland@online.de>
    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<String, ShortBuffer>();
     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/gpx/ElevationModel.java

    r30344 r30737  
    1919 * Represents the top-level part of the elevation model. The elevation model
    2020 * breaks done into the tracks/routes of a GPX file.
    21  * 
     21 *
    2222 * @see IElevationModelTrackListener
    2323 * @author Oliver Wieland <oliver.wieland@online.de>
    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<IElevationModelListener>();
    32     private final List<WayPoint> buffer = new ArrayList<WayPoint>();
    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

    r30344 r30737  
    1717 * full way point set and then reduces the number of way points to a given
    1818 * amount, if necessary.
    19  * 
     19 *
    2020 * The computation is done via implementing {@link IGpxWaypointVisitor},
    2121 * subclasses may override the {@link ElevationProfile#visitWayPoint(WayPoint)}
    2222 * method to compute own values or run specific actions. The computation is
    2323 * triggered by calling {@link ElevationProfile#updateValues()}.
    24  * 
     24 *
    2525 * Elevation profiles can break down into further child profiles. This is
    2626 * intended to show different levels of details, if the number of way points
    2727 * exceed the display space (which is usually the case).
    28  * 
     28 *
    2929 * {@link IElevationProfile} {@link IGpxWaypointVisitor} {@link GpxIterator}
    30  * 
     30 *
    3131 * @author Oliver Wieland <oliver.wieland@online.de>
    32  * 
     32 *
    3333 */
    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<WayPoint>(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

    r30701 r30737  
    8787    System.out.println(newP);
    8888                 */
    89                 List<EleVertex> res = new ArrayList<EleVertex>();
     89                List<EleVertex> res = new ArrayList<>();
    9090                res.add(new EleVertex(pI, pK, newP));
    9191                res.add(new EleVertex(pJ, pK, newP));
  • applications/editors/josm/plugins/ElevationProfile/src/org/openstreetmap/josm/plugins/elevation/grid/ElevationGridTile.java

    r30344 r30737  
    2525
    2626public class ElevationGridTile extends Tile {
    27     private final BlockingDeque<EleVertex> toDo = new LinkedBlockingDeque<EleVertex>();
    28     private final BlockingDeque<EleVertex> vertices = new LinkedBlockingDeque<EleVertex>();
     27        private final BlockingDeque<EleVertex> toDo = new LinkedBlockingDeque<>();
     28        private final BlockingDeque<EleVertex> vertices = new LinkedBlockingDeque<>();
    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         Bounds bb = new Bounds(
    129                 new LatLon(source.tileYToLat(y, zoom), source.tileXToLon(x, zoom)),
    130                 new LatLon(source.tileYToLat(y + 1, zoom), source.tileXToLon(x + 1, zoom)));
     120        /**
     121        * See also <a href="http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Tile_bounding_box">OSM Wiki</a>
     122        * @param x the x
     123        * @param y the y
     124        * @param zoom the zoom
     125        * @return the bounds
     126        */
     127        private Bounds tile2Bounds(final int x, final int y, final int zoom) {
     128                Bounds bb = new Bounds(
     129                                new LatLon(source.tileYToLat(y, zoom), source.tileXToLon(x, zoom)),
     130                                new LatLon(source.tileYToLat(y + 1, zoom), source.tileXToLon(x + 1, zoom)));
    131131
    132         return bb;
    133     }
     132                return bb;
     133        }
    134134
    135     /**
    136     * Inits the 'todo' queue with the initial vertices.
    137     */
    138     private void initQueue() {
    139         LatLon min = box.getMin();
    140         LatLon max = box.getMax();
     135        /**
     136        * Inits the 'todo' queue with the initial vertices.
     137        */
     138        private void initQueue() {
     139                LatLon min = box.getMin();
     140                LatLon max = box.getMax();
    141141
    142         // compute missing coordinates
    143         LatLon h1 = new LatLon(min.lat(), max.lon());
    144         LatLon h2 = new LatLon(max.lat(), min.lon());
     142                // compute missing coordinates
     143                LatLon h1 = new LatLon(min.lat(), max.lon());
     144                LatLon h2 = new LatLon(max.lat(), min.lon());
    145145
    146         double eleMin = ElevationHelper.getSrtmElevation(min);
    147         double eleMax = ElevationHelper.getSrtmElevation(max);
     146                double eleMin = ElevationHelper.getSrtmElevation(min);
     147                double eleMax = ElevationHelper.getSrtmElevation(max);
    148148
    149         // SRTM files present?
    150         if (!ElevationHelper.isValidElevation(eleMax) || !ElevationHelper.isValidElevation(eleMin)) {
    151             setError(tr("No SRTM data"));
    152             return;
    153         }
     149                // SRTM files present?
     150                if (!ElevationHelper.isValidElevation(eleMax) || !ElevationHelper.isValidElevation(eleMin)) {
     151                        setError(tr("No SRTM data"));
     152                        return;
     153                }
    154154
    155         // compute elevation coords
    156         EleCoordinate p0 = new EleCoordinate(min, eleMin);
    157         EleCoordinate p1 = new EleCoordinate(h1, ElevationHelper.getSrtmElevation(h1));
    158         EleCoordinate p2 = new EleCoordinate(max, eleMax);
    159         EleCoordinate p3 = new EleCoordinate(h2, ElevationHelper.getSrtmElevation(h2));
     155                // compute elevation coords
     156                EleCoordinate p0 = new EleCoordinate(min, eleMin);
     157                EleCoordinate p1 = new EleCoordinate(h1, ElevationHelper.getSrtmElevation(h1));
     158                EleCoordinate p2 = new EleCoordinate(max, eleMax);
     159                EleCoordinate p3 = new EleCoordinate(h2, ElevationHelper.getSrtmElevation(h2));
    160160
    161         // compute initial vertices
    162         EleVertex v1 = new EleVertex(p0, p1, p2);
    163         EleVertex v2 = new EleVertex(p2, p3, p0);
    164         // enqueue vertices
    165         toDo.add(v1);
    166         toDo.add(v2);
    167     }
     161                // compute initial vertices
     162                EleVertex v1 = new EleVertex(p0, p1, p2);
     163                EleVertex v2 = new EleVertex(p2, p3, p0);
     164                // enqueue vertices
     165                toDo.add(v1);
     166                toDo.add(v2);
     167        }
    168168
    169     @Override
    170     public String toString() {
    171         return "ElevationGridTile [box=" + box + ", xtile=" + xtile
    172                 + ", ytile=" + ytile + "]";
    173     }
     169        @Override
     170        public String toString() {
     171                return "ElevationGridTile [box=" + box + ", xtile=" + xtile
     172                                + ", ytile=" + ytile + "]";
     173        }
    174174
    175175
  • applications/editors/josm/plugins/ElevationProfile/src/org/openstreetmap/josm/plugins/elevation/gui/DefaultElevationProfileRenderer.java

    r30344 r30737  
    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<Rectangle>();
    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

    r30532 r30737  
    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<GpxLayer, ElevationModel>();
    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<IElevationModelListener>();
    70 
    71     /**
    72     * Corresponding layer instance within map view.
    73     */
    74     private ElevationProfileLayer profileLayer;
    75 
    76     /**
    77     * Default constructor
    78     */
    79     public ElevationProfileDialog() {
    80         this(tr("Elevation Profile"), "elevation",
    81                 tr("Open the elevation profile window."), null, 200, true);
    82     }
    83 
    84     /**
    85     * Constructor (see below)
    86     */
    87     public ElevationProfileDialog(String name, String iconName, String tooltip,
    88             Shortcut shortcut, int preferredHeight) {
    89         this(name, iconName, tooltip, shortcut, preferredHeight, false);
    90     }
    91 
    92     /**
    93     * Constructor
    94      *
    95     * @param name
    96     *            the name of the dialog
    97     * @param iconName
    98     *            the name of the icon to be displayed
    99     * @param tooltip
    100     *            the tool tip
    101     * @param shortcut
    102     *            the shortcut
    103     * @param preferredHeight
    104     *            the preferred height for the dialog
    105     * @param defShow
    106     *            if the dialog should be shown by default, if there is no
    107     *            preference
    108     */
    109     public ElevationProfileDialog(String name, String iconName, String tooltip,
    110             Shortcut shortcut, int preferredHeight, boolean defShow) {
    111         super(name, iconName, tooltip, shortcut, preferredHeight, defShow);
    112 
    113         // create model
    114         model = new ElevationModel();
    115 
    116         // top panel
    117         JPanel rootPanel = new JPanel();
    118         GridLayout gridLayout1 = new GridLayout(2, 1);
    119         rootPanel.setLayout(gridLayout1);
    120 
    121         // statistics panel
    122         JPanel statPanel = new JPanel();
    123         GridLayout gridLayoutStat = new GridLayout(2, 6);
    124         statPanel.setLayout(gridLayoutStat);
    125 
    126         // first row: Headlines with bold font
    127         String[] labels = new String[]{tr("Min"), tr("Avrg"), tr("Max"), tr("Dist"), tr("Gain"), tr("Time")};
    128         for (int i = 0; i < labels.length; i++) {
    129             JLabel lbl = new JLabel(labels[i]);
    130             lbl.setFont(getFont().deriveFont(Font.BOLD));
    131             statPanel.add(lbl);
    132         }
    133 
    134         // second row
    135         minHeightLabel = new JLabel("0 m");
    136         statPanel.add(minHeightLabel);
    137         avrgHeightLabel = new JLabel("0 m");
    138         statPanel.add(avrgHeightLabel);
    139         maxHeightLabel = new JLabel("0 m");
    140         statPanel.add(maxHeightLabel);
    141         distLabel = new JLabel("0 km");
    142         statPanel.add(distLabel);
    143         elevationGainLabel = new JLabel("0 m");
    144         statPanel.add(elevationGainLabel);
    145         totalTimeLabel = new JLabel("0");
    146         statPanel.add(totalTimeLabel);
    147 
    148         // track selection panel
    149         JPanel trackPanel = new JPanel();
    150         FlowLayout fl = new FlowLayout(FlowLayout.LEFT);
    151         trackPanel.setLayout(fl);
    152 
    153         JLabel lbTrack = new JLabel(tr("Tracks"));
    154         lbTrack.setFont(getFont().deriveFont(Font.BOLD));
    155         trackPanel.add(lbTrack);
    156 
    157         zoomButton = new JButton(tr("Zoom"));
    158         zoomButton.addActionListener(new ActionListener() {
    159             @Override
    160             public void actionPerformed(ActionEvent arg0) {
    161                 if (model != null) {
    162                     IElevationProfile profile = model.getCurrentProfile();
    163                     if (profile != null) {
    164                         Main.map.mapView.zoomTo(profile.getBounds());
    165                     }
    166                 }
    167 
    168             }
    169         });
    170         zoomButton.setEnabled(false);
    171 
    172         trackCombo = new JComboBox<>(new TrackModel());
    173         trackCombo.setPreferredSize(new Dimension(200, 24)); // HACK!
    174         trackCombo.setEnabled(false); // we have no model on startup
    175 
    176         trackPanel.add(trackCombo);
    177         trackPanel.add(zoomButton);
    178 
    179         // assemble root panel
    180         rootPanel.add(statPanel);
    181         rootPanel.add(trackPanel);
    182 
    183         JPanel mainPanel = new JPanel(new BorderLayout());
    184         mainPanel.add(rootPanel, BorderLayout.PAGE_END);
    185 
    186         // add chart component
    187         profPanel = new ElevationProfilePanel(null);
    188         mainPanel.add(profPanel, BorderLayout.CENTER);
    189         profPanel.addComponentListener(this);
    190 
    191         createLayout(mainPanel, true, null);
    192     }
    193 
    194     @Override
    195     public void showNotify() {
    196         MapView.addLayerChangeListener(this);
    197         if (Main.isDisplayingMapView()) {
    198             Layer layer = Main.map.mapView.getActiveLayer();
    199             if (layer instanceof GpxLayer) {
    200                 setActiveLayer((GpxLayer) layer);
    201             }
    202         }
    203     }
    204 
    205     @Override
    206     public void hideNotify() {
    207         MapView.removeLayerChangeListener(this);
    208     }
    209 
    210     /**
    211     * Gets the elevation model instance.
    212     * @return
    213     */
    214     public IElevationModel getModel() {
    215         return model;
    216     }
    217 
    218     /**
    219     * Sets the elevation model instance.
    220     * @param model The new model.
    221     */
    222     public void setModel(IElevationModel model) {
    223         if (this.model != model) {
    224             this.model = model;
    225             profPanel.setElevationModel(model);
    226             updateView();
    227         }
    228     }
    229 
    230     /**
    231     * Gets the associated layer instance of the elevation profile.
    232     * @return
    233     */
    234     public ElevationProfileLayer getProfileLayer() {
    235         return profileLayer;
    236     }
    237 
    238     /**
    239     * Sets the associated layer instance of the elevation profile.
    240     * @param profileLayer The elevation profile layer.
    241     */
    242     public void setProfileLayer(ElevationProfileLayer profileLayer) {
    243         if (this.profileLayer != profileLayer) {
    244             if (this.profileLayer != null) {
    245                 profPanel.removeSelectionListener(this.profileLayer);
    246             }
    247             this.profileLayer = profileLayer;
    248             profPanel.addSelectionListener(this.profileLayer);
    249         }
    250     }
    251 
    252     /**
    253     * Refreshes the dialog when model data have changed and notifies clients
    254     * that the model has changed.
    255     */
    256     private void updateView() {
    257         if (model == null) {
    258             disableView();
    259             return;
    260         }
    261 
    262         IElevationProfile profile = model.getCurrentProfile();
    263         if (profile != null) {
    264             // Show name of profile in title
    265             setTitle(String.format("%s: %s", tr("Elevation Profile"), profile.getName()));
    266 
    267             if (profile.hasElevationData()) {
    268                 // Show elevation data
    269                 minHeightLabel.setText(
    270                         ElevationHelper.getElevationText(profile.getMinHeight()));
    271                 maxHeightLabel.setText(
    272                         ElevationHelper.getElevationText(profile.getMaxHeight()));
    273                 avrgHeightLabel.setText(
    274                         ElevationHelper.getElevationText(profile.getAverageHeight()));
    275                 elevationGainLabel.setText(
    276                         ElevationHelper.getElevationText(profile.getGain()));
    277             }
    278 
    279             // compute values for time and distance
    280             long diff = profile.getTimeDifference();
    281             long minutes = diff / (1000 * 60);
    282             long hours = minutes / 60;
    283             minutes = minutes % 60;
    284 
    285             double dist = profile.getDistance();
    286 
    287             totalTimeLabel.setText(String.format("%d:%02d h", hours, minutes));
    288             distLabel.setText(NavigatableComponent.getSystemOfMeasurement().getDistText(dist));
    289             trackCombo.setEnabled(model.profileCount() > 1);
    290             trackCombo.setModel(new TrackModel());
    291             zoomButton.setEnabled(true);
    292         } else { // no elevation data, -> switch back to empty view
    293             disableView();
    294         }
    295 
    296         fireModelChanged();
    297         repaint();
    298     }
    299 
    300     private void disableView() {
    301         setTitle(String.format("%s: (No data)", tr("Elevation Profile")));
    302 
    303         minHeightLabel.setText(EMPTY_DATA_STRING);
    304         maxHeightLabel.setText(EMPTY_DATA_STRING);
    305         avrgHeightLabel.setText(EMPTY_DATA_STRING);
    306         elevationGainLabel.setText(EMPTY_DATA_STRING);
    307         totalTimeLabel.setText(EMPTY_DATA_STRING);
    308         distLabel.setText(EMPTY_DATA_STRING);
    309         trackCombo.setEnabled(false);
    310         zoomButton.setEnabled(false);
    311     }
    312 
    313     /**
    314     * Fires the 'model changed' event to all listeners.
    315     */
    316     protected void fireModelChanged() {
    317         for (IElevationModelListener listener : listeners) {
    318             listener.elevationProfileChanged(getModel().getCurrentProfile());
    319         }
    320     }
    321 
    322     /**
    323     * Adds a model listener to this instance.
    324      *
    325     * @param listener
    326     *            The listener to add.
    327     */
    328     public void addModelListener(IElevationModelListener listener) {
    329         this.listeners.add(listener);
    330     }
    331 
    332     /**
    333     * Removes a model listener from this instance.
    334      *
    335     * @param listener
    336     *            The listener to remove.
    337     */
    338     public void removeModelListener(IElevationModelListener listener) {
    339         this.listeners.remove(listener);
    340     }
    341 
    342     /**
    343     * Removes all listeners from this instance.
    344     */
    345     public void removeAllListeners() {
    346         this.listeners.clear();
    347     }
    348 
    349     @Override
    350     public void activeLayerChange(Layer oldLayer, Layer newLayer) {
    351         if (newLayer instanceof GpxLayer) {
    352             setActiveLayer((GpxLayer) newLayer);
    353         }
    354     }
    355 
    356     private void setActiveLayer(GpxLayer newLayer) {
    357         if (activeLayer != newLayer) {
    358             activeLayer = newLayer;
    359 
    360             // layer does not exist -> create
    361             if (!layerMap.containsKey(newLayer)) {
    362                 GpxData gpxData = newLayer.data;
    363                 ElevationModel newEM = new ElevationModel(newLayer.getName(),
    364                         gpxData);
    365                 layerMap.put(newLayer, newEM);
    366             }
    367 
    368             ElevationModel em = layerMap.get(newLayer);
    369             setModel(em);
    370         }
    371     }
    372 
    373     @Override
    374     public void layerAdded(Layer newLayer) {
    375         if (newLayer instanceof GpxLayer) {
    376             GpxLayer gpxLayer = (GpxLayer) newLayer;
    377             setActiveLayer(gpxLayer);
    378         }
    379     }
    380 
    381     @Override
    382     public void layerRemoved(Layer oldLayer) {
    383         if (layerMap.containsKey(oldLayer)) {
    384             layerMap.remove(oldLayer);
    385         }
    386 
    387         if (layerMap.size() == 0) {
    388             setModel(null);
    389             if (profileLayer != null) {
    390                 profileLayer.setProfile(null);
    391             }
    392         }
    393     }
    394 
    395     @Override
    396     public void componentHidden(ComponentEvent e) {
    397     }
    398 
    399     @Override
    400     public void componentMoved(ComponentEvent e) {
    401     }
    402 
    403     @Override
    404     public void componentResized(ComponentEvent e) {
    405     }
    406 
    407     @Override
    408     public void componentShown(ComponentEvent e) {
    409     }
    410 
    411     class TrackModel implements ComboBoxModel<IElevationProfile> {
    412         private Collection<ListDataListener> listeners;
    413 
    414         @Override
    415         public void addListDataListener(ListDataListener arg0) {
    416             if (listeners == null) {
    417                 listeners = new ArrayList<ListDataListener>();
    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(NavigatableComponent.getSystemOfMeasurement().getDistText(dist));
     289                        trackCombo.setEnabled(model.profileCount() > 1);
     290                        trackCombo.setModel(new TrackModel());
     291                        zoomButton.setEnabled(true);
     292                } else { // no elevation data, -> switch back to empty view
     293                        disableView();
     294                }
     295
     296                fireModelChanged();
     297                repaint();
     298        }
     299
     300        private void disableView() {
     301                setTitle(String.format("%s: (No data)", tr("Elevation Profile")));
     302
     303                minHeightLabel.setText(EMPTY_DATA_STRING);
     304                maxHeightLabel.setText(EMPTY_DATA_STRING);
     305                avrgHeightLabel.setText(EMPTY_DATA_STRING);
     306                elevationGainLabel.setText(EMPTY_DATA_STRING);
     307                totalTimeLabel.setText(EMPTY_DATA_STRING);
     308                distLabel.setText(EMPTY_DATA_STRING);
     309                trackCombo.setEnabled(false);
     310                zoomButton.setEnabled(false);
     311        }
     312
     313        /**
     314        * Fires the 'model changed' event to all listeners.
     315        */
     316        protected void fireModelChanged() {
     317                for (IElevationModelListener listener : listeners) {
     318                        listener.elevationProfileChanged(getModel().getCurrentProfile());
     319                }
     320        }
     321
     322        /**
     323        * Adds a model listener to this instance.
     324         *
     325        * @param listener
     326        *            The listener to add.
     327        */
     328        public void addModelListener(IElevationModelListener listener) {
     329                this.listeners.add(listener);
     330        }
     331
     332        /**
     333        * Removes a model listener from this instance.
     334         *
     335        * @param listener
     336        *            The listener to remove.
     337        */
     338        public void removeModelListener(IElevationModelListener listener) {
     339                this.listeners.remove(listener);
     340        }
     341
     342        /**
     343        * Removes all listeners from this instance.
     344        */
     345        public void removeAllListeners() {
     346                this.listeners.clear();
     347        }
     348
     349        @Override
     350        public void activeLayerChange(Layer oldLayer, Layer newLayer) {
     351                if (newLayer instanceof GpxLayer) {
     352                        setActiveLayer((GpxLayer) newLayer);
     353                }
     354        }
     355
     356        private void setActiveLayer(GpxLayer newLayer) {
     357                if (activeLayer != newLayer) {
     358                        activeLayer = newLayer;
     359
     360                        // layer does not exist -> create
     361                        if (!layerMap.containsKey(newLayer)) {
     362                                GpxData gpxData = newLayer.data;
     363                                ElevationModel newEM = new ElevationModel(newLayer.getName(),
     364                                                gpxData);
     365                                layerMap.put(newLayer, newEM);
     366                        }
     367
     368                        ElevationModel em = layerMap.get(newLayer);
     369                        setModel(em);
     370                }
     371        }
     372
     373        @Override
     374        public void layerAdded(Layer newLayer) {
     375                if (newLayer instanceof GpxLayer) {
     376                        GpxLayer gpxLayer = (GpxLayer) newLayer;
     377                        setActiveLayer(gpxLayer);
     378                }
     379        }
     380
     381        @Override
     382        public void layerRemoved(Layer oldLayer) {
     383                if (layerMap.containsKey(oldLayer)) {
     384                        layerMap.remove(oldLayer);
     385                }
     386
     387                if (layerMap.size() == 0) {
     388                        setModel(null);
     389                        if (profileLayer != null) {
     390                                profileLayer.setProfile(null);
     391                        }
     392                }
     393        }
     394
     395        @Override
     396        public void componentHidden(ComponentEvent e) {
     397        }
     398
     399        @Override
     400        public void componentMoved(ComponentEvent e) {
     401        }
     402
     403        @Override
     404        public void componentResized(ComponentEvent e) {
     405        }
     406
     407        @Override
     408        public void componentShown(ComponentEvent e) {
     409        }
     410
     411        class TrackModel implements ComboBoxModel<IElevationProfile> {
     412                private Collection<ListDataListener> listeners;
     413
     414                @Override
     415                public void addListDataListener(ListDataListener arg0) {
     416                        if (listeners == null) {
     417                                listeners = new ArrayList<>();
     418                        }
     419                        listeners.add(arg0);
     420                }
     421
     422                @Override
     423                public IElevationProfile getElementAt(int index) {
     424                        if (model == null) return null;
     425
     426                        IElevationProfile ep = model.getProfiles().get(index);
     427                        return ep;
     428                }
     429
     430                @Override
     431                public int getSize() {
     432                        if (model == null) return 0;
     433
     434                        return model.profileCount();
     435                }
     436
     437                @Override
     438                public void removeListDataListener(ListDataListener listener) {
     439                        if (listeners == null) return;
     440
     441                        listeners.remove(listener);
     442                }
     443
     444                @Override
     445                public IElevationProfile getSelectedItem() {
     446                        if (model == null) return null;
     447
     448                        return model.getCurrentProfile();
     449                }
     450
     451                @Override
     452                public void setSelectedItem(Object selectedObject) {
     453                        if (model != null && selectedObject instanceof IElevationProfile) {
     454                                model.setCurrentProfile((IElevationProfile) selectedObject);
     455                                profileLayer.setProfile(model.getCurrentProfile());
     456
     457                                repaint();
     458                        }
     459                }
     460        }
    461461}
  • applications/editors/josm/plugins/ElevationProfile/src/org/openstreetmap/josm/plugins/elevation/gui/ElevationProfilePanel.java

    r30344 r30737  
    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<IElevationProfileSelectionListener>();
    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.