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;
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;
import tim.prune.DataSubscriber;
import tim.prune.FunctionLibrary;
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;
+import tim.prune.data.Latitude;
+import tim.prune.data.Longitude;
import tim.prune.data.Selection;
import tim.prune.data.Track;
import tim.prune.data.TrackInfo;
private App _app = null;
/** Track object */
private Track _track = null;
+ /** TrackInfo object */
+ private TrackInfo _trackInfo = null;
/** Selection object */
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 */
private JSlider _transparencySlider = null;
+ /** Checkbox for scale bar */
+ private JCheckBox _scaleCheckBox = null;
/** Checkbox for maps */
private JCheckBox _mapCheckBox = null;
/** Checkbox for autopan */
private JPanel _topPanel = null;
/** Side component panel */
private JPanel _sidePanel = null;
+ /** Scale bar */
+ private ScaleBar _scaleBar = null;
/* Data */
private DoubleRange _latRange = null, _lonRange = null;
private DoubleRange _xRange = null, _yRange = null;
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 */
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;
private static final int AUTOPAN_DISTANCE = 75;
// Colours
- private static final Color COLOR_BG = Color.WHITE;
private static final Color COLOR_MESSAGES = Color.GRAY;
- private static final Color COLOR_POINT = Color.BLUE;
- private static final Color COLOR_POINT_DELETED = Color.RED;
- private static final Color COLOR_CURR_RANGE = Color.GREEN;
- private static final Color COLOR_CROSSHAIRS = Color.RED;
- private static final Color COLOR_WAYPT_NAME = Color.BLACK;
- private static final Color COLOR_PHOTO_PT = Color.ORANGE;
+ // 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
public MapCanvas(App inApp, TrackInfo inTrackInfo)
{
_app = inApp;
+ _trackInfo = inTrackInfo;
_track = inTrackInfo.getTrack();
_selection = inTrackInfo.getSelection();
_mapPosition = new MapPosition();
ItemListener mapCheckListener = new ItemListener() {
public void itemStateChanged(ItemEvent e)
{
- _tileCacher.clearAll();
+ _tileManager.clearMemoryCaches();
_recalculate = true;
- repaint();
+ Config.setConfigBoolean(Config.KEY_SHOW_MAP, e.getStateChange() == ItemEvent.SELECTED);
+ UpdateMessageBroker.informSubscribers(); // to let menu know
}
};
_topPanel = new JPanel();
_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
_topPanel.add(_transparencySlider);
+ // Add checkbox button for enabling scale bar
+ _scaleCheckBox = new JCheckBox(IconManager.getImageIcon(IconManager.SCALEBAR_BUTTON), true);
+ _scaleCheckBox.setSelectedIcon(IconManager.getImageIcon(IconManager.SCALEBAR_BUTTON_ON));
+ _scaleCheckBox.setOpaque(false);
+ _scaleCheckBox.setToolTipText(I18nManager.getText("menu.map.showscalebar"));
+ _scaleCheckBox.addItemListener(new ItemListener() {
+ public void itemStateChanged(ItemEvent e) {
+ _scaleBar.setVisible(_scaleCheckBox.isSelected());
+ }
+ });
+ _scaleCheckBox.setFocusable(false); // stop button from stealing keyboard focus
+ _topPanel.add(_scaleCheckBox);
// Add checkbox button for enabling maps or not
_mapCheckBox = new JCheckBox(IconManager.getImageIcon(IconManager.MAP_BUTTON), false);
_mapCheckBox.setSelectedIcon(IconManager.getImageIcon(IconManager.MAP_BUTTON_ON));
zoomOutButton.setFocusable(false); // stop button from stealing keyboard focus
_sidePanel.add(zoomOutButton);
+ // Bottom panel for scale bar
+ _scaleBar = new ScaleBar();
+
// add control panels to this one
setLayout(new BorderLayout());
_topPanel.setVisible(false);
_sidePanel.setVisible(false);
add(_topPanel, BorderLayout.NORTH);
add(_sidePanel, BorderLayout.WEST);
+ add(_scaleBar, BorderLayout.SOUTH);
// Make popup menu
makePopup();
}
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() {
_recalculate = true;
repaint();
}});
- zoomFullItem.setEnabled(true);
_popup.add(zoomFullItem);
_popup.addSeparator();
// Set background
newPointItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
- _app.createPoint(MapUtils.getLatitudeFromY(_mapPosition.getYFromPixels(_popupMenuY, getHeight())),
- MapUtils.getLongitudeFromX(_mapPosition.getXFromPixels(_popupMenuX, getWidth())));
+ _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);
}
_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());
}
_prevSelectedPoint = selectedPoint;
}
- // Draw the mapImage if necessary
- if ((_mapImage == null || _recalculate)) {
- getMapTiles();
+ // Draw the map contents if necessary
+ if ((_mapImage == null || _recalculate))
+ {
+ 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);
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
{
- inG.setColor(COLOR_BG);
+ inG.setColor(Config.getColourScheme().getColour(ColourScheme.IDX_BACKGROUND));
inG.fillRect(0, 0, getWidth(), getHeight());
inG.setColor(COLOR_MESSAGES);
inG.drawString(I18nManager.getText("display.nodata"), 50, getHeight()/2);
+ _scaleBar.updateScale(-1, 0);
}
// Draw slider etc on top
paintChildren(inG);
/**
- * 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())
{
// Clear map
Graphics g = _mapImage.getGraphics();
- // Clear to white
- g.setColor(COLOR_BG);
+ // Clear to background
+ g.setColor(Config.getColourScheme().getColour(ColourScheme.IDX_BACKGROUND));
g.fillRect(0, 0, getWidth(), getHeight());
+ // Check whether maps are on or not
+ boolean showMap = Config.getConfigBoolean(Config.KEY_SHOW_MAP);
+ _mapCheckBox.setSelected(showMap);
+
// reset error message
- if (!_mapCheckBox.isSelected()) {_shownOsmErrorAlready = false;}
+ if (!showMap) {_shownOsmErrorAlready = false;}
+ _recalculate = false;
// Only get map tiles if selected
- if (_mapCheckBox.isSelected())
+ 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);
}
else
{
+ int numLayers = _tileManager.getNumLayers();
// Loop over tiles drawing each one
int[] tileIndices = _mapPosition.getTileIndices(getWidth(), getHeight());
int[] pixelOffsets = _mapPosition.getDisplayOffsets(getWidth(), getHeight());
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<numLayers; l++)
+ {
+ Image image = _tileManager.getTile(l, tileX, tileY);
+ if (image != null) {
+ g.drawImage(image, x, y, 256, 256, null);
+ }
}
}
}
- // Make maps brighter / fainter
- float[] scaleFactors = {1.0f, 1.05f, 1.1f, 1.2f, 1.6f, 2.0f};
- float scaleFactor = scaleFactors[_transparencySlider.getValue()];
- if (scaleFactor > 1.0f) {
- 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);
+ // Make maps brighter / fainter according to slider
+ final int brightnessIndex = Math.max(1, _transparencySlider.getValue()) - 1;
+ if (brightnessIndex > 0)
+ {
+ 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());
}
}
}
// free g
g.dispose();
- _recalculate = false;
// Zoom to fit if no points found
if (pointsPainted <= 0 && _checkBounds) {
zoomToFit();
}
_checkBounds = false;
// enable / disable transparency slider
- _transparencySlider.setEnabled(_mapCheckBox.isSelected());
+ _transparencySlider.setEnabled(showMap);
}
*/
private int paintPoints(Graphics inG)
{
+ // Set up colours
+ 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(COLOR_POINT);
+ inG.setColor(pointColour);
int prevX = -1, prevY = -1;
boolean connectPoints = _connectCheckBox.isSelected();
boolean prevPointVisible = false, currPointVisible = false;
+ boolean anyWaypoints = false;
+ boolean isWaypoint = false;
for (int i=0; i<_track.getNumPoints(); i++)
{
int px = getWidth() / 2 + _mapPosition.getXFromCentre(_track.getX(i));
int py = getHeight() / 2 + _mapPosition.getYFromCentre(_track.getY(i));
currPointVisible = px >= 0 && px < getWidth() && py >= 0 && py < getHeight();
+ isWaypoint = _track.getPoint(i).isWaypoint();
+ anyWaypoints = anyWaypoints || isWaypoint;
if (currPointVisible)
{
- if (!_track.getPoint(i).isWaypoint())
+ if (!isWaypoint)
{
// Draw rectangle for track point
if (_track.getPoint(i).getDeleteFlag()) {
- inG.setColor(COLOR_POINT_DELETED);
+ inG.setColor(currentColour);
}
else {
- inG.setColor(COLOR_POINT);
+ inG.setColor(pointColour);
}
inG.drawRect(px-2, py-2, 3, 3);
pointsPainted++;
}
}
- if (!_track.getPoint(i).isWaypoint())
+ if (!isWaypoint)
{
// Connect track points if either of them are visible
if (connectPoints && (currPointVisible || prevPointVisible)
}
// Loop over points, just drawing blobs for waypoints
- inG.setColor(COLOR_WAYPT_NAME);
+ inG.setColor(textColour);
FontMetrics fm = inG.getFontMetrics();
int nameHeight = fm.getHeight();
int width = getWidth();
int height = getHeight();
- for (int i=0; i<_track.getNumPoints(); i++)
- {
- if (_track.getPoint(i).isWaypoint())
+ if (anyWaypoints) {
+ for (int i=0; i<_track.getNumPoints(); i++)
{
- int px = getWidth() / 2 + _mapPosition.getXFromCentre(_track.getX(i));
- int py = getHeight() / 2 + _mapPosition.getYFromCentre(_track.getY(i));
- if (px >= 0 && px < getWidth() && py >= 0 && py < getHeight())
+ if (_track.getPoint(i).isWaypoint())
{
- inG.fillRect(px-3, py-3, 6, 6);
- pointsPainted++;
+ int px = getWidth() / 2 + _mapPosition.getXFromCentre(_track.getX(i));
+ int py = getHeight() / 2 + _mapPosition.getYFromCentre(_track.getY(i));
+ if (px >= 0 && px < getWidth() && py >= 0 && py < getHeight())
+ {
+ inG.fillRect(px-3, py-3, 6, 6);
+ pointsPainted++;
+ }
}
}
- }
- // Loop over points again, now draw names for waypoints
- for (int i=0; i<_track.getNumPoints(); i++)
- {
- if (_track.getPoint(i).isWaypoint())
+ // Loop over points again, now draw names for waypoints
+ for (int i=0; i<_track.getNumPoints(); i++)
{
- int px = getWidth() / 2 + _mapPosition.getXFromCentre(_track.getX(i));
- int py = getHeight() / 2 + _mapPosition.getYFromCentre(_track.getY(i));
- if (px >= 0 && px < getWidth() && py >= 0 && py < getHeight())
+ if (_track.getPoint(i).isWaypoint())
{
- // Figure out where to draw waypoint name so it doesn't obscure track
- String waypointName = _track.getPoint(i).getWaypointName();
- int nameWidth = fm.stringWidth(waypointName);
- boolean drawnName = false;
- // Make arrays for coordinates right left up down
- int[] nameXs = {px + 2, px - nameWidth - 2, px - nameWidth/2, px - nameWidth/2};
- int[] nameYs = {py + (nameHeight/2), py + (nameHeight/2), py - 2, py + nameHeight + 2};
- for (int extraSpace = 4; extraSpace < 13 && !drawnName; extraSpace+=2)
+ int px = getWidth() / 2 + _mapPosition.getXFromCentre(_track.getX(i));
+ int py = getHeight() / 2 + _mapPosition.getYFromCentre(_track.getY(i));
+ if (px >= 0 && px < getWidth() && py >= 0 && py < getHeight())
{
- // Shift arrays for coordinates right left up down
- nameXs[0] += 2; nameXs[1] -= 2;
- nameYs[2] -= 2; nameYs[3] += 2;
- // Check each direction in turn right left up down
- for (int a=0; a<4; a++)
+ // Figure out where to draw waypoint name so it doesn't obscure track
+ String waypointName = _track.getPoint(i).getWaypointName();
+ int nameWidth = fm.stringWidth(waypointName);
+ boolean drawnName = false;
+ // Make arrays for coordinates right left up down
+ int[] nameXs = {px + 2, px - nameWidth - 2, px - nameWidth/2, px - nameWidth/2};
+ int[] nameYs = {py + (nameHeight/2), py + (nameHeight/2), py - 2, py + nameHeight + 2};
+ for (int extraSpace = 4; extraSpace < 13 && !drawnName; extraSpace+=2)
{
- if (nameXs[a] > 0 && (nameXs[a] + nameWidth) < width
- && nameYs[a] < height && (nameYs[a] - nameHeight) > 0
- && !overlapsPoints(nameXs[a], nameYs[a], nameWidth, nameHeight))
+ // Shift arrays for coordinates right left up down
+ nameXs[0] += 2; nameXs[1] -= 2;
+ nameYs[2] -= 2; nameYs[3] += 2;
+ // Check each direction in turn right left up down
+ for (int a=0; a<4; a++)
{
- // Found a rectangle to fit - draw name here and quit
- inG.drawString(waypointName, nameXs[a], nameYs[a]);
- drawnName = true;
- break;
+ if (nameXs[a] > 0 && (nameXs[a] + nameWidth) < width
+ && nameYs[a] < height && (nameYs[a] - nameHeight) > 0
+ && !overlapsPoints(nameXs[a], nameYs[a], nameWidth, nameHeight, textColour))
+ {
+ // Found a rectangle to fit - draw name here and quit
+ inG.drawString(waypointName, nameXs[a], nameYs[a]);
+ drawnName = true;
+ break;
+ }
}
}
}
}
}
}
- // Loop over points, drawing blobs for photo points
- inG.setColor(COLOR_PHOTO_PT);
+ // 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));
// Draw selected range
if (_selection.hasRangeSelected())
{
- inG.setColor(COLOR_CURR_RANGE);
+ inG.setColor(rangeColour);
for (int i=_selection.getStart(); i<=_selection.getEnd(); i++)
{
int px = getWidth() / 2 + _mapPosition.getXFromCentre(_track.getX(i));
{
int px = getWidth() / 2 + _mapPosition.getXFromCentre(_track.getX(selectedPoint));
int py = getHeight() / 2 + _mapPosition.getYFromCentre(_track.getY(selectedPoint));
- inG.setColor(COLOR_CROSSHAIRS);
+ inG.setColor(currentColour);
// crosshairs
inG.drawLine(px, 0, px, getHeight());
inG.drawLine(0, py, getWidth(), py);
* @param inY bottom Y coordinate
* @param inWidth width of rectangle
* @param inHeight height of rectangle
- * @return true if there's at least one data point in the rectangle
+ * @param inTextColour colour of text
+ * @return true if the rectangle overlaps stuff too close to the given colour
*/
- private boolean overlapsPoints(int inX, int inY, int inWidth, int inHeight)
+ private boolean overlapsPoints(int inX, int inY, int inWidth, int inHeight, Color inTextColour)
{
- // each of the colour channels must be brighter than this to count as empty
- final int BRIGHTNESS_LIMIT = 210;
+ // each of the colour channels must be further away than this to count as empty
+ final int BRIGHTNESS_LIMIT = 80;
+ final int textRGB = inTextColour.getRGB();
+ final int textLow = textRGB & 255;
+ final int textMid = (textRGB >> 8) & 255;
+ final int textHigh = (textRGB >> 16) & 255;
try
{
// loop over x coordinate of rectangle
{
int pixelColor = _mapImage.getRGB(inX + x, inY - y);
// split into four components rgba
- int lowestBit = pixelColor & 255;
- int secondBit = (pixelColor >> 8) & 255;
- int thirdBit = (pixelColor >> 16) & 255;
+ int pixLow = pixelColor & 255;
+ int pixMid = (pixelColor >> 8) & 255;
+ int pixHigh = (pixelColor >> 16) & 255;
//int fourthBit = (pixelColor >> 24) & 255; // alpha ignored
- if (lowestBit < BRIGHTNESS_LIMIT || secondBit < BRIGHTNESS_LIMIT || thirdBit < BRIGHTNESS_LIMIT) return true;
+ // If colours are too close in any channel then it's an overlap
+ if (Math.abs(pixLow-textLow) < BRIGHTNESS_LIMIT ||
+ Math.abs(pixMid-textMid) < BRIGHTNESS_LIMIT ||
+ Math.abs(pixHigh-textHigh) < BRIGHTNESS_LIMIT) {return true;}
}
}
}
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
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()
*/
// 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);
- _selection.selectPoint(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 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
{
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();
}
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();
*/
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();
+ }
}
/**
_checkBounds = true;
}
if ((inUpdateType & DataSubscriber.MAPSERVER_CHANGED) > 0) {
- _tileCacher.setTileConfig(new MapTileConfig());
+ _tileManager.resetConfig();
}
repaint();
// enable or disable components
public void keyPressed(KeyEvent inE)
{
int code = inE.getKeyCode();
- // Check for meta key
- if (inE.isControlDown())
+ int currPointIndex = _selection.getCurrentPointIndex();
+ // 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();
else if (code == KeyEvent.VK_DOWN)
zoomOut();
// Key nav for next/prev point
- else if (code == KeyEvent.VK_LEFT)
- _selection.selectPreviousPoint();
+ else if (code == KeyEvent.VK_LEFT && currPointIndex > 0)
+ _trackInfo.incrementPointIndex(-pointIncrement);
else if (code == KeyEvent.VK_RIGHT)
- _selection.selectNextPoint();
+ _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
{
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 && _selection.getCurrentPointIndex() >= 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();
}
}
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();
+ }
+ }
+
+ /**
+ * @return current map position
+ */
+ public MapPosition getMapPosition()
+ {
+ return _mapPosition;
}
}