// License: GPL. For details, see LICENSE file. package org.openstreetmap.josm.plugins.elevation.gui; import static org.openstreetmap.josm.tools.I18n.tr; import java.awt.Font; import java.awt.Graphics2D; import javax.swing.Action; import javax.swing.Icon; import org.openstreetmap.josm.Main; import org.openstreetmap.josm.data.Bounds; import org.openstreetmap.josm.data.gpx.WayPoint; import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor; import org.openstreetmap.josm.gui.MapView; import org.openstreetmap.josm.gui.dialogs.LayerListPopup; import org.openstreetmap.josm.gui.layer.Layer; import org.openstreetmap.josm.plugins.elevation.ElevationHelper; import org.openstreetmap.josm.plugins.elevation.IElevationProfile; import org.openstreetmap.josm.plugins.elevation.gpx.ElevationWayPointKind; import org.openstreetmap.josm.tools.ImageProvider; /** * Layer class to show additional information on the elevation map, e. g. show * min/max elevation markers. * * @author Oliver Wieland <oliver.wieland@online.de> * */ public class ElevationProfileLayer extends Layer implements IElevationProfileSelectionListener { private static final double Level_Factor = 100.0; private IElevationProfile profile; private final IElevationProfileRenderer renderer = new DefaultElevationProfileRenderer(); private WayPoint selWayPoint = null; /** * Creates a new elevation profile layer * * @param name * The name of the layer. */ public ElevationProfileLayer(String name) { super(name); } /** * Gets the current elevation profile shown in this layer. */ public IElevationProfile getProfile() { return profile; } /** * Sets the current elevation profile shown in this layer. * * @param profile * The profile to show in the layer */ public void setProfile(IElevationProfile profile) { if (this.profile != profile) { this.profile = profile; Main.map.repaint(); } } @Override public Icon getIcon() { return ImageProvider.get("layer", "elevation"); } @Override public Object getInfoComponent() { return getToolTipText(); } @Override public Action[] getMenuEntries() { // TODO: More entries??? return new Action[] {new LayerListPopup.InfoAction(this)}; } @Override public String getToolTipText() { if (profile != null) { return tr("Elevation profile for track ''{0}''.", profile.getName()); } else { return tr("Elevation profile"); } } @Override public boolean isMergable(Layer other) { return false; } @Override public void mergeFrom(Layer from) { // nothing to do } @Override public void paint(Graphics2D g, MapView mv, Bounds box) { WayPoint lastWpt = null; renderer.beginRendering(); if (profile != null) { // choose smaller font Font oldFont = g.getFont(); Font lFont = g.getFont().deriveFont(9.0f); g.setFont(lFont); try { // paint way points one by one for (WayPoint wpt : profile.getWayPoints()) { if (lastWpt != null) { // determine way point ElevationWayPointKind kind = classifyWayPoint(lastWpt, wpt); // render way point as line renderer.renderLine(g, profile, mv, lastWpt, wpt, kind); // render single way point renderer.renderWayPoint(g, profile, mv, wpt, kind); } // else first way point -> is paint later // remember last wpt for next iteration lastWpt = wpt; } // now we paint special way points in emphasized style // paint start/end renderer.renderWayPoint(g, profile, mv, profile.getStartWayPoint(), ElevationWayPointKind.StartPoint); renderer.renderWayPoint(g, profile, mv, profile.getEndWayPoint(), ElevationWayPointKind.EndPoint); // paint min/max renderer.renderWayPoint(g, profile, mv, profile.getMaxWayPoint(), ElevationWayPointKind.MaxElevation); renderer.renderWayPoint(g, profile, mv, profile.getMinWayPoint(), ElevationWayPointKind.MinElevation); // paint selected way point, if available if (selWayPoint != null) { renderer.renderWayPoint(g, profile, mv, selWayPoint, ElevationWayPointKind.Highlighted); } } finally { g.setFont(oldFont); } } renderer.finishRendering(); } /** * Checks if the given way point requires special decoration (e. g. elevation gain/loss or level crossing). * * Parameters <tt>ele1</tt> and <tt>ele2</tt> point are used for detecting "level crossings", * e. g. 1 to 2 indicate that we crossed the 200m elevation in upward direction * * @param lastWpt the last way point * @param actWpt the actual way point * @return the elevation way point kind */ private ElevationWayPointKind classifyWayPoint(WayPoint lastWpt, WayPoint actWpt) { // get elevation values int actEle = (int) ElevationHelper.getElevation(actWpt); int lastEle = (int) ElevationHelper.getElevation(lastWpt); // normalize elevation to levels int actLevel = (int) (actEle / Level_Factor); int lastLevel = (int) (lastEle / Level_Factor); double slope = Math.abs(ElevationHelper.computeSlope(lastWpt.getCoor(), actWpt.getCoor())); // plain way point by default ElevationWayPointKind kind = ElevationWayPointKind.Plain; // check, if we passed an elevation level // We assume, that we cannot pass more than one levels between two way points ;-) if (actLevel != lastLevel && Math.abs(actLevel - lastLevel) == 1) { if (actLevel > lastLevel) { // we went down? kind = ElevationWayPointKind.ElevationLevelGain; } else { kind = ElevationWayPointKind.ElevationLevelLoss; } } else { // check for elevation gain or loss if (actEle > lastEle) { // we went uphill? // TODO: Provide parameters for high/low thresholds if (slope > 2) kind = ElevationWayPointKind.ElevationGainLow; if (slope > 15) kind = ElevationWayPointKind.ElevationGainHigh; } else { if (slope > 2) kind = ElevationWayPointKind.ElevationLossLow; if (slope > 15) kind = ElevationWayPointKind.ElevationLossHigh; } } return kind; } @Override public void visitBoundingBox(BoundingXYVisitor v) { // What to do here? } @Override public void selectedWayPointChanged(WayPoint newWayPoint) { if (selWayPoint != newWayPoint) { selWayPoint = newWayPoint; Main.map.repaint(); } } }