X-Git-Url: https://gitweb.fperrin.net/?a=blobdiff_plain;f=tim%2Fprune%2Fgui%2Fmap%2FMapCanvas.java;h=82cf434f7387adcfe56b5ff5c9d48710844d4d66;hb=649c5da6ee1bbc590699e11a92316ece2ea8512d;hp=d2f36ce165b45cfd5c315da68c6a70b5726a1f51;hpb=1ee49ae3c8ef3aa2e63eadd458531e5f8bd4f92c;p=GpsPrune.git diff --git a/tim/prune/gui/map/MapCanvas.java b/tim/prune/gui/map/MapCanvas.java index d2f36ce..82cf434 100644 --- a/tim/prune/gui/map/MapCanvas.java +++ b/tim/prune/gui/map/MapCanvas.java @@ -1,13 +1,14 @@ package tim.prune.gui.map; +import java.awt.BasicStroke; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.FontMetrics; import java.awt.Graphics; +import java.awt.Graphics2D; import java.awt.Image; -import java.awt.RenderingHints; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; @@ -20,7 +21,6 @@ import java.awt.event.MouseMotionListener; import java.awt.event.MouseWheelEvent; import java.awt.event.MouseWheelListener; import java.awt.image.BufferedImage; -import java.awt.image.RescaleOp; import javax.swing.BorderFactory; import javax.swing.BoxLayout; @@ -40,6 +40,7 @@ import tim.prune.I18nManager; import tim.prune.UpdateMessageBroker; import tim.prune.config.ColourScheme; import tim.prune.config.Config; +import tim.prune.data.Checker; import tim.prune.data.Coordinate; import tim.prune.data.DataPoint; import tim.prune.data.DoubleRange; @@ -66,8 +67,8 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe private Selection _selection = null; /** Previously selected point */ private int _prevSelectedPoint = -1; - /** Tile cacher */ - private MapTileCacher _tileCacher = new MapTileCacher(this); + /** Tile manager */ + private MapTileManager _tileManager = new MapTileManager(this); /** Image to display */ private BufferedImage _mapImage = null; /** Slider for transparency */ @@ -100,8 +101,6 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe private int _dragFromX = -1; /** y coordinate of drag from point */ private int _dragFromY = -1; - /** Flag set to true for right-click dragging */ - private boolean _zoomDragging = false; /** x coordinate of drag to point */ private int _dragToX = -1; /** y coordinate of drag to point */ @@ -112,6 +111,8 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe private int _popupMenuY = -1; /** Flag to prevent showing too often the error message about loading maps */ private boolean _shownOsmErrorAlready = false; + /** Current drawing mode */ + private int _drawMode = MODE_DEFAULT; /** Constant for click sensitivity when selecting nearest point */ private static final int CLICK_SENSITIVITY = 10; @@ -123,6 +124,11 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe // Colours private static final Color COLOR_MESSAGES = Color.GRAY; + // Drawing modes + private static final int MODE_DEFAULT = 0; + private static final int MODE_ZOOM_RECT = 1; + private static final int MODE_DRAW_POINTS_START = 2; + private static final int MODE_DRAW_POINTS_CONT = 3; /** * Constructor @@ -153,7 +159,7 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe ItemListener mapCheckListener = new ItemListener() { public void itemStateChanged(ItemEvent e) { - _tileCacher.clearAll(); + _tileManager.clearMemoryCaches(); _recalculate = true; Config.setConfigBoolean(Config.KEY_SHOW_MAP, e.getStateChange() == ItemEvent.SELECTED); UpdateMessageBroker.informSubscribers(); // to let menu know @@ -163,16 +169,22 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe _topPanel.setLayout(new FlowLayout()); _topPanel.setOpaque(false); // Make slider for transparency - _transparencySlider = new JSlider(0, 5, 0); + _transparencySlider = new JSlider(-6, 6, 0); _transparencySlider.setPreferredSize(new Dimension(100, 20)); _transparencySlider.setMajorTickSpacing(1); _transparencySlider.setSnapToTicks(true); _transparencySlider.setOpaque(false); + _transparencySlider.setValue(0); _transparencySlider.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { - _recalculate = true; - repaint(); + int val = _transparencySlider.getValue(); + if (val == 1 || val == -1) + _transparencySlider.setValue(0); + else { + _recalculate = true; + repaint(); + } } }); _transparencySlider.setFocusable(false); // stop slider from stealing keyboard focus @@ -268,17 +280,17 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe zoomInItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { + panMap((_popupMenuX - getWidth()/2)/2, (_popupMenuY - getHeight()/2)/2); zoomIn(); }}); - zoomInItem.setEnabled(true); _popup.add(zoomInItem); JMenuItem zoomOutItem = new JMenuItem(I18nManager.getText("menu.map.zoomout")); zoomOutItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { + panMap(-(_popupMenuX - getWidth()/2), -(_popupMenuY - getHeight()/2)); zoomOut(); }}); - zoomOutItem.setEnabled(true); _popup.add(zoomOutItem); JMenuItem zoomFullItem = new JMenuItem(I18nManager.getText("menu.map.zoomfull")); zoomFullItem.addActionListener(new ActionListener() { @@ -288,7 +300,6 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe _recalculate = true; repaint(); }}); - zoomFullItem.setEnabled(true); _popup.add(zoomFullItem); _popup.addSeparator(); // Set background @@ -305,13 +316,18 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe newPointItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { - double lat = MapUtils.getLatitudeFromY(_mapPosition.getYFromPixels(_popupMenuY, getHeight())); - double lon = MapUtils.getLongitudeFromX(_mapPosition.getXFromPixels(_popupMenuX, getWidth())); - _app.createPoint(new DataPoint(new Latitude(lat, Coordinate.FORMAT_NONE), - new Longitude(lon, Coordinate.FORMAT_NONE), null)); + _app.createPoint(createPointFromClick(_popupMenuX, _popupMenuY)); }}); - newPointItem.setEnabled(true); _popup.add(newPointItem); + // draw point series + JMenuItem drawPointsItem = new JMenuItem(I18nManager.getText("menu.map.drawpoints")); + drawPointsItem.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) + { + _drawMode = MODE_DRAW_POINTS_START; + } + }); + _popup.add(drawPointsItem); } @@ -327,7 +343,7 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe _yRange = new DoubleRange(MapUtils.getYFromLatitude(_latRange.getMinimum()), MapUtils.getYFromLatitude(_latRange.getMaximum())); _mapPosition.zoomToXY(_xRange.getMinimum(), _xRange.getMaximum(), _yRange.getMinimum(), _yRange.getMaximum(), - getWidth(), getHeight()); + getWidth(), getHeight()); } @@ -372,18 +388,18 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe _prevSelectedPoint = selectedPoint; } - // Draw the mapImage if necessary + // Draw the map contents if necessary if ((_mapImage == null || _recalculate)) { - getMapTiles(); - _scaleBar.updateScale(_mapPosition.getZoom(), _mapPosition.getCentreTileY()); + paintMapContents(); + _scaleBar.updateScale(_mapPosition.getZoom(), _mapPosition.getYFromPixels(0, 0)); } // Draw the prepared image onto the panel if (_mapImage != null) { inG.drawImage(_mapImage, 0, 0, getWidth(), getHeight(), null); } // Draw the zoom rectangle if necessary - if (_zoomDragging) + if (_drawMode == MODE_ZOOM_RECT) { inG.setColor(Color.RED); inG.drawLine(_dragFromX, _dragFromY, _dragFromX, _dragToY); @@ -391,6 +407,15 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe inG.drawLine(_dragToX, _dragFromY, _dragToX, _dragToY); inG.drawLine(_dragFromX, _dragToY, _dragToX, _dragToY); } + else if (_drawMode == MODE_DRAW_POINTS_CONT) + { + // draw line to mouse position to show drawing mode + inG.setColor(Config.getColourScheme().getColour(ColourScheme.IDX_POINT)); + int prevIndex = _track.getNumPoints()-1; + int px = getWidth() / 2 + _mapPosition.getXFromCentre(_track.getX(prevIndex)); + int py = getHeight() / 2 + _mapPosition.getYFromCentre(_track.getY(prevIndex)); + inG.drawLine(px, py, _dragToX, _dragToY); + } } else { @@ -406,9 +431,9 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe /** - * Get the map tiles for the current zoom level and given tile parameters + * Paint the map tiles and the points on to the _mapImage */ - private void getMapTiles() + private void paintMapContents() { if (_mapImage == null || _mapImage.getWidth() != getWidth() || _mapImage.getHeight() != getHeight()) { @@ -427,16 +452,17 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe // reset error message if (!showMap) {_shownOsmErrorAlready = false;} + _recalculate = false; // Only get map tiles if selected if (showMap) { // init tile cacher - _tileCacher.centreMap(_mapPosition.getZoom(), _mapPosition.getCentreTileX(), _mapPosition.getCentreTileY()); + _tileManager.centreMap(_mapPosition.getZoom(), _mapPosition.getCentreTileX(), _mapPosition.getCentreTileY()); boolean loadingFailed = false; if (_mapImage == null) return; - if (_tileCacher.isOverzoomed()) + if (_tileManager.isOverzoomed()) { // display overzoom message g.setColor(COLOR_MESSAGES); @@ -444,6 +470,7 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe } else { + int numLayers = _tileManager.getNumLayers(); // Loop over tiles drawing each one int[] tileIndices = _mapPosition.getTileIndices(getWidth(), getHeight()); int[] pixelOffsets = _mapPosition.getDisplayOffsets(getWidth(), getHeight()); @@ -453,22 +480,26 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe for (int tileY = tileIndices[2]; tileY <= tileIndices[3]; tileY++) { int y = (tileY - tileIndices[2]) * 256 - pixelOffsets[1]; - Image image = _tileCacher.getTile(tileX, tileY); - if (image != null) { - g.drawImage(image, x, y, 256, 256, null); + // Loop over layers + for (int l=0; l 1.0f) + // Make maps brighter / fainter according to slider + final int brightnessIndex = Math.max(1, _transparencySlider.getValue()) - 1; + if (brightnessIndex > 0) { - RenderingHints hints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); - hints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); - RescaleOp op = new RescaleOp(scaleFactor, 0, hints); - op.filter(_mapImage, _mapImage); + final int[] alphas = {0, 40, 80, 120, 160, 210}; + Color bgColor = Config.getColourScheme().getColour(ColourScheme.IDX_BACKGROUND); + bgColor = new Color(bgColor.getRed(), bgColor.getGreen(), bgColor.getBlue(), alphas[brightnessIndex]); + g.setColor(bgColor); + g.fillRect(0, 0, getWidth(), getHeight()); } } } @@ -485,7 +516,6 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe // free g g.dispose(); - _recalculate = false; // Zoom to fit if no points found if (pointsPainted <= 0 && _checkBounds) { zoomToFit(); @@ -506,12 +536,24 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe private int paintPoints(Graphics inG) { // Set up colours - final Color pointColour = Config.getColourScheme().getColour(ColourScheme.IDX_POINT); - final Color rangeColour = Config.getColourScheme().getColour(ColourScheme.IDX_SELECTION); - final Color currentColour = Config.getColourScheme().getColour(ColourScheme.IDX_PRIMARY); - final Color secondColour = Config.getColourScheme().getColour(ColourScheme.IDX_SECONDARY); - final Color textColour = Config.getColourScheme().getColour(ColourScheme.IDX_TEXT); + final ColourScheme cs = Config.getColourScheme(); + final int[] opacities = {255, 190, 130, 80, 40, 0}; + int opacity = 255; + if (_transparencySlider.getValue() < 0) + opacity = opacities[-1 - _transparencySlider.getValue()]; + final Color pointColour = makeTransparentColour(cs.getColour(ColourScheme.IDX_POINT), opacity); + final Color rangeColour = makeTransparentColour(cs.getColour(ColourScheme.IDX_SELECTION), opacity); + final Color currentColour = makeTransparentColour(cs.getColour(ColourScheme.IDX_PRIMARY), opacity); + final Color secondColour = makeTransparentColour(cs.getColour(ColourScheme.IDX_SECONDARY), opacity); + final Color textColour = makeTransparentColour(cs.getColour(ColourScheme.IDX_TEXT), opacity); + // try to set line width for painting + if (inG instanceof Graphics2D) + { + int lineWidth = Config.getConfigInt(Config.KEY_LINE_WIDTH); + if (lineWidth < 1 || lineWidth > 4) {lineWidth = 2;} + ((Graphics2D) inG).setStroke(new BasicStroke(lineWidth)); + } int pointsPainted = 0; // draw track points inG.setColor(pointColour); @@ -615,11 +657,11 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe } } } - // Loop over points, drawing blobs for photo points + // Loop over points, drawing blobs for photo / audio points inG.setColor(secondColour); for (int i=0; i<_track.getNumPoints(); i++) { - if (_track.getPoint(i).getPhoto() != null) + if (_track.getPoint(i).hasMedia()) { int px = getWidth() / 2 + _mapPosition.getXFromCentre(_track.getX(i)); int py = getHeight() / 2 + _mapPosition.getYFromCentre(_track.getY(i)); @@ -707,6 +749,17 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe return false; } + /** + * Make a semi-transparent colour for drawing with + * @param inColour base colour (fully opaque) + * @param inOpacity opacity where 0=invisible and 255=full + * @return new colour object + */ + private static Color makeTransparentColour(Color inColour, int inOpacity) + { + if (inOpacity > 240) return inColour; + return new Color(inColour.getRed(), inColour.getGreen(), inColour.getBlue(), inOpacity); + } /** * Inform that tiles have been updated and the map can be repainted @@ -762,6 +815,20 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe repaint(); } + /** + * Create a DataPoint object from the given click coordinates + * @param inX x coordinate of click + * @param inY y coordinate of click + * @return DataPoint with given coordinates and no altitude + */ + private DataPoint createPointFromClick(int inX, int inY) + { + double lat = MapUtils.getLatitudeFromY(_mapPosition.getYFromPixels(inY, getHeight())); + double lon = MapUtils.getLongitudeFromX(_mapPosition.getXFromPixels(inX, getWidth())); + return new DataPoint(new Latitude(lat, Coordinate.FORMAT_NONE), + new Longitude(lon, Coordinate.FORMAT_NONE), null); + } + /** * @see javax.swing.JComponent#getMinimumSize() */ @@ -791,16 +858,47 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe // select point if it's a left-click if (!inE.isMetaDown()) { - int pointIndex = _track.getNearestPointIndex( - _mapPosition.getXFromPixels(inE.getX(), getWidth()), - _mapPosition.getYFromPixels(inE.getY(), getHeight()), - _mapPosition.getBoundsFromPixels(CLICK_SENSITIVITY), false); - // Extend selection for shift-click - if (inE.isShiftDown()) { - _trackInfo.extendSelection(pointIndex); + if (inE.getClickCount() == 1) + { + // single click + if (_drawMode == MODE_DEFAULT) + { + int pointIndex = _track.getNearestPointIndex( + _mapPosition.getXFromPixels(inE.getX(), getWidth()), + _mapPosition.getYFromPixels(inE.getY(), getHeight()), + _mapPosition.getBoundsFromPixels(CLICK_SENSITIVITY), false); + // Extend selection for shift-click + if (inE.isShiftDown()) { + _trackInfo.extendSelection(pointIndex); + } + else { + _trackInfo.selectPoint(pointIndex); + } + } + else if (_drawMode == MODE_DRAW_POINTS_START) + { + _app.createPoint(createPointFromClick(inE.getX(), inE.getY())); + _dragToX = inE.getX(); + _dragToY = inE.getY(); + _drawMode = MODE_DRAW_POINTS_CONT; + } + else if (_drawMode == MODE_DRAW_POINTS_CONT) + { + DataPoint point = createPointFromClick(inE.getX(), inE.getY()); + _app.createPoint(point); + point.setSegmentStart(false); + } } - else { - _trackInfo.selectPoint(pointIndex); + else if (inE.getClickCount() == 2) + { + // double click + if (_drawMode == MODE_DEFAULT) { + panMap(inE.getX() - getWidth()/2, inE.getY() - getHeight()/2); + zoomIn(); + } + else if (_drawMode == MODE_DRAW_POINTS_START || _drawMode == MODE_DRAW_POINTS_CONT) { + _drawMode = MODE_DEFAULT; + } } } else @@ -847,13 +945,15 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe public void mouseReleased(MouseEvent inE) { _recalculate = true; - if (_zoomDragging && Math.abs(_dragToX - _dragFromX) > 20 && Math.abs(_dragToY - _dragFromY) > 20) + if (_drawMode == MODE_ZOOM_RECT && Math.abs(_dragToX - _dragFromX) > 20 + && Math.abs(_dragToY - _dragFromY) > 20) { - //System.out.println("Finished zoom: " + _dragFromX + ", " + _dragFromY + " to " + _dragToX + ", " + _dragToY); _mapPosition.zoomToPixels(_dragFromX, _dragToX, _dragFromY, _dragToY, getWidth(), getHeight()); } + if (_drawMode == MODE_ZOOM_RECT) { + _drawMode = MODE_DEFAULT; + } _dragFromX = _dragFromY = -1; - _zoomDragging = false; repaint(); } @@ -866,20 +966,19 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe if (!inE.isMetaDown()) { // Left mouse drag - pan map by appropriate amount - _zoomDragging = false; if (_dragFromX != -1) { panMap(_dragFromX - inE.getX(), _dragFromY - inE.getY()); _recalculate = true; repaint(); } - _dragFromX = inE.getX(); - _dragFromY = inE.getY(); + _dragFromX = _dragToX = inE.getX(); + _dragFromY = _dragToY = inE.getY(); } else { // Right-click and drag - draw rectangle and control zoom - _zoomDragging = true; + _drawMode = MODE_ZOOM_RECT; if (_dragFromX == -1) { _dragFromX = inE.getX(); _dragFromY = inE.getY(); @@ -896,7 +995,13 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe */ public void mouseMoved(MouseEvent inEvent) { - // ignore + // Ignore unless we're drawing points + if (_drawMode == MODE_DRAW_POINTS_CONT) + { + _dragToX = inEvent.getX(); + _dragToY = inEvent.getY(); + repaint(); + } } /** @@ -919,7 +1024,7 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe _checkBounds = true; } if ((inUpdateType & DataSubscriber.MAPSERVER_CHANGED) > 0) { - _tileCacher.setTileConfig(new MapTileConfig()); + _tileManager.resetConfig(); } repaint(); // enable or disable components @@ -938,9 +1043,11 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe { int code = inE.getKeyCode(); int currPointIndex = _selection.getCurrentPointIndex(); - // Check for meta key - if (inE.isControlDown()) + // Check for Ctrl key (for Linux/Win) or meta key (Clover key for Mac) + if (inE.isControlDown() || inE.isMetaDown()) { + // Shift as well makes things faster + final int pointIncrement = inE.isShiftDown()?3:1; // Check for arrow keys to zoom in and out if (code == KeyEvent.VK_UP) zoomIn(); @@ -948,9 +1055,20 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe zoomOut(); // Key nav for next/prev point else if (code == KeyEvent.VK_LEFT && currPointIndex > 0) - _trackInfo.selectPoint(currPointIndex-1); + _trackInfo.incrementPointIndex(-pointIncrement); else if (code == KeyEvent.VK_RIGHT) - _trackInfo.selectPoint(currPointIndex+1); + _trackInfo.incrementPointIndex(pointIncrement); + else if (code == KeyEvent.VK_PAGE_UP) + _trackInfo.selectPoint(Checker.getPreviousSegmentStart( + _trackInfo.getTrack(), _trackInfo.getSelection().getCurrentPointIndex())); + else if (code == KeyEvent.VK_PAGE_DOWN) + _trackInfo.selectPoint(Checker.getNextSegmentStart( + _trackInfo.getTrack(), _trackInfo.getSelection().getCurrentPointIndex())); + // Check for home and end + else if (code == KeyEvent.VK_HOME) + _trackInfo.selectPoint(0); + else if (code == KeyEvent.VK_END) + _trackInfo.selectPoint(_trackInfo.getTrack().getNumPoints()-1); } else { @@ -966,9 +1084,11 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe else if (code == KeyEvent.VK_LEFT) rightwardsPan = -PAN_DISTANCE; panMap(rightwardsPan, upwardsPan); - // Check for delete key to delete current point - if (code == KeyEvent.VK_DELETE && currPointIndex >= 0) - { + // Check for escape + if (code == KeyEvent.VK_ESCAPE) + _drawMode = MODE_DEFAULT; + // Check for backspace key to delete current point (delete key already handled by menu) + else if (code == KeyEvent.VK_BACK_SPACE && currPointIndex >= 0) { _app.deleteCurrentPoint(); } } @@ -996,10 +1116,14 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe public void mouseWheelMoved(MouseWheelEvent inE) { int clicks = inE.getWheelRotation(); - if (clicks < 0) + if (clicks < 0) { + panMap((inE.getX() - getWidth()/2)/2, (inE.getY() - getHeight()/2)/2); zoomIn(); - else if (clicks > 0) + } + else if (clicks > 0) { + panMap(-(inE.getX() - getWidth()/2), -(inE.getY() - getHeight()/2)); zoomOut(); + } } /**