Java tutorial
/******************************************************************************* * Copyright (C) 2005, 2010 Wolfgang Schramm and Contributors * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation version 2 of the License. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA *******************************************************************************/ package net.tourbook.mapping; import java.awt.Point; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Set; import net.tourbook.application.TourbookPlugin; import net.tourbook.data.TourData; import net.tourbook.data.TourMarker; import net.tourbook.data.TourWayPoint; import net.tourbook.preferences.ITourbookPreferences; import net.tourbook.preferences.PrefPageAppearanceMap; import net.tourbook.ui.ColorCacheInt; import net.tourbook.ui.UI; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Device; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.ImageData; import org.eclipse.swt.graphics.PaletteData; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Display; import de.byteholder.geoclipse.map.Map; import de.byteholder.geoclipse.map.MapPainter; import de.byteholder.geoclipse.map.Tile; import de.byteholder.geoclipse.mapprovider.MP; import de.byteholder.gpx.GeoPosition; /** * Paints a tour into the map */ public class TourPainter extends MapPainter { private static final int MARKER_MARGIN = 2; private static final int MARKER_POLE = 16; private final static IPreferenceStore _prefStore = TourbookPlugin.getDefault().getPreferenceStore(); private static IPropertyChangeListener _prefChangeListener; private int[] _dataSerie; private ILegendProvider _legendProvider; // painting parameter private int _lineWidth; private int _lineWidth2; private static boolean _prefIsDrawLine; private static boolean _prefIsDrawSquare; private static int _prefLineWidth; private static boolean _prefWithBorder; private static int _prefBorderWidth; private static boolean _isImageAvailable = false; /** * Tour start/end image */ private static Image _tourStartMarker; private static Image _tourEndMarker; /** * Tour Way Point image */ private static Image _twpImage; private static Rectangle _twpImageBounds; private final static ColorCacheInt _colorCache = new ColorCacheInt(); static { /** * this code optimizes the performance by reading from the pref store which is not very * efficient */ getTourPainterSettings(); // create pref listener _prefChangeListener = new IPropertyChangeListener() { @Override public void propertyChange(final PropertyChangeEvent event) { final String property = event.getProperty(); // test if the color or statistic data have changed if (property.equals(ITourbookPreferences.GRAPH_COLORS_HAS_CHANGED)) { getTourPainterSettings(); } } }; // add pref listener _prefStore.addPropertyChangeListener(_prefChangeListener); // remove pre listener /* * this listener is never removed */ // container.addDisposeListener(new DisposeListener() { // public void widgetDisposed(final DisposeEvent e) { // TourbookPlugin.getDefault().getPluginPreferences().removePropertyChangeListener(_prefChangeListener); // } // }); } public TourPainter() { super(); /* * I've not yet understood to manage this problem because TourPainter() is created from an * extension point but setting the instance in the constructor is not valid according to * FindBugs */ } /** * Draw legend colors into the legend bounds * * @param gc * @param legendBounds * @param isVertical * when <code>true</code> the legend is drawn vertical, when false the legend is * drawn horizontal * @param colorId * @return */ public static void drawLegendColors(final GC gc, final Rectangle legendBounds, final ILegendProvider legendProvider, final boolean isVertical) { if (legendProvider == null) { return; } final Device display = gc.getDevice(); final LegendConfig config = legendProvider.getLegendConfig(); // ensure units are available if (config.units == null /* || config.unitLabels == null */) { return; } // get configuration for the legend final ArrayList<Integer> legendUnits = new ArrayList<Integer>(config.units); final Integer unitFactor = config.unitFactor; final int legendMaxValue = config.legendMaxValue; final int legendMinValue = config.legendMinValue; final int legendDiffValue = legendMaxValue - legendMinValue; final String unitText = config.unitText; final List<String> unitLabels = config.unitLabels; Rectangle legendBorder; int legendPositionX; int legendPositionY; int legendWidth; int legendHeight; int availableLegendPixels; if (isVertical) { // vertical legend legendPositionX = legendBounds.x + 1; legendPositionY = legendBounds.y + TourMapView.LEGEND_MARGIN_TOP_BOTTOM; legendWidth = 20; legendHeight = legendBounds.height - 2 * TourMapView.LEGEND_MARGIN_TOP_BOTTOM; availableLegendPixels = legendHeight - 1; legendBorder = new Rectangle(legendPositionX - 1, // legendPositionY - 1, legendWidth + 1, legendHeight + 1); } else { // horizontal legend legendPositionX = legendBounds.x + 1; legendPositionY = legendBounds.y + 1; legendWidth = legendBounds.width - 1; legendHeight = legendBounds.height; availableLegendPixels = legendWidth - 1; legendBorder = legendBounds; } // pixelValue contains the value for ONE pixel final float pixelValue = (float) legendDiffValue / availableLegendPixels; // draw border around the colors final Color borderColor = display.getSystemColor(SWT.COLOR_GRAY); gc.setForeground(borderColor); gc.drawRectangle(legendBorder); final Color legendTextColor = display.getSystemColor(SWT.COLOR_BLACK); final Color legendTextBackgroundColor = display.getSystemColor(SWT.COLOR_WHITE); int legendValue = 0; int unitLabelIndex = 0; final Color textBorderColor = new Color(display, 0xF1, 0xEE, 0xE8); for (int pixelIndex = 0; pixelIndex <= availableLegendPixels; pixelIndex++) { legendValue = (int) (legendMinValue + pixelValue * pixelIndex); int valuePosition; if (isVertical) { valuePosition = legendPositionY + availableLegendPixels - pixelIndex; } else { valuePosition = legendPositionX + availableLegendPixels - pixelIndex; } /* * draw legend unit */ if (isVertical) { // find a unit which corresponds to the current legend value for (final Integer unitValue : legendUnits) { if (legendValue >= unitValue) { /* * get unit label */ String valueText; if (unitLabels == null) { // set default unit label final int unit = unitValue / unitFactor; valueText = Integer.toString(unit) + UI.SPACE + unitText; } else { // when unitLabels are available, they will overwrite the default labeling valueText = unitLabels.get(unitLabelIndex++); } final org.eclipse.swt.graphics.Point valueTextExtent = gc.textExtent(valueText); gc.setForeground(legendTextColor); gc.setBackground(legendTextBackgroundColor); gc.drawLine(legendWidth, // valuePosition, // legendWidth + 5, valuePosition); // draw unit value and text // if (unitLabels == null) { // gc.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_CYAN)); // gc.fillRectangle(legendWidth + 5, // valuePosition - valueTextExtent.y / 2, // valueTextExtent.x, // valueTextExtent.y); // } final int devXText = legendWidth + 5; final int devYText = valuePosition - valueTextExtent.y / 2; gc.setForeground(textBorderColor); // gc.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_RED)); gc.drawText(valueText, devXText - 1, devYText, true); gc.drawText(valueText, devXText + 1, devYText, true); gc.drawText(valueText, devXText, devYText - 1, true); gc.drawText(valueText, devXText, devYText + 1, true); gc.setForeground(legendTextColor); gc.drawText(valueText, devXText, devYText, true); // prevent to draw this unit again legendUnits.remove(unitValue); break; } } } /* * draw legend color line */ final int lineColorValue = legendProvider.getColorValue(legendValue); final Color lineColor = _colorCache.get(lineColorValue); gc.setForeground(lineColor); if (isVertical) { // vertial legend gc.drawLine(legendPositionX, valuePosition, legendWidth, valuePosition); } else { // horizontal legend gc.drawLine(valuePosition, legendPositionY, valuePosition, legendHeight); } } _colorCache.dispose(); textBorderColor.dispose(); } /** * @param legendConfig * @param legendColor * @param legendValue * @param device * @return Returns a {@link Color} which corresponst to the legend value */ static int getLegendColor(final LegendConfig legendConfig, final LegendColor legendColor, final int legendValue) { int red = 0; int green = 0; int blue = 0; final ValueColor[] valueColors = legendColor.valueColors; final float minBrightnessFactor = legendColor.minBrightnessFactor / (float) 100; final float maxBrightnessFactor = legendColor.maxBrightnessFactor / (float) 100; /* * find the valueColor for the current value */ ValueColor valueColor; ValueColor minValueColor = null; ValueColor maxValueColor = null; for (final ValueColor valueColor2 : valueColors) { valueColor = valueColor2; if (legendValue > valueColor.value) { minValueColor = valueColor; } if (legendValue <= valueColor.value) { maxValueColor = valueColor; } if (minValueColor != null && maxValueColor != null) { break; } } if (minValueColor == null) { // legend value is smaller than minimum value valueColor = valueColors[0]; red = valueColor.red; green = valueColor.green; blue = valueColor.blue; final int minValue = valueColor.value; final int minDiff = legendConfig.legendMinValue - minValue; final float ratio = minDiff == 0 ? 1 : (legendValue - minValue) / (float) minDiff; final float dimmRatio = minBrightnessFactor * ratio; if (legendColor.minBrightness == LegendColor.BRIGHTNESS_DIMMING) { red = red - (int) (dimmRatio * red); green = green - (int) (dimmRatio * green); blue = blue - (int) (dimmRatio * blue); } else if (legendColor.minBrightness == LegendColor.BRIGHTNESS_LIGHTNING) { red = red + (int) (dimmRatio * (255 - red)); green = green + (int) (dimmRatio * (255 - green)); blue = blue + (int) (dimmRatio * (255 - blue)); } } else if (maxValueColor == null) { // legend value is larger than maximum value valueColor = valueColors[valueColors.length - 1]; red = valueColor.red; green = valueColor.green; blue = valueColor.blue; final int maxValue = valueColor.value; final int maxDiff = legendConfig.legendMaxValue - maxValue; final float ratio = maxDiff == 0 ? 1 : (legendValue - maxValue) / (float) maxDiff; final float dimmRatio = maxBrightnessFactor * ratio; if (legendColor.maxBrightness == LegendColor.BRIGHTNESS_DIMMING) { red = red - (int) (dimmRatio * red); green = green - (int) (dimmRatio * green); blue = blue - (int) (dimmRatio * blue); } else if (legendColor.maxBrightness == LegendColor.BRIGHTNESS_LIGHTNING) { red = red + (int) (dimmRatio * (255 - red)); green = green + (int) (dimmRatio * (255 - green)); blue = blue + (int) (dimmRatio * (255 - blue)); } } else { // legend value is in the min/max range final int maxValue = maxValueColor.value; final int minValue = minValueColor.value; final int minRed = minValueColor.red; final int minGreen = minValueColor.green; final int minBlue = minValueColor.blue; final int redDiff = maxValueColor.red - minRed; final int greenDiff = maxValueColor.green - minGreen; final int blueDiff = maxValueColor.blue - minBlue; final int ratioDiff = maxValue - minValue; final float ratio = ratioDiff == 0 ? 1 : (legendValue - minValue) / (float) (ratioDiff); red = (int) (minRed + redDiff * ratio); green = (int) (minGreen + greenDiff * ratio); blue = (int) (minBlue + blueDiff * ratio); } // adjust color values to 0...255, this is optimized final int maxRed = (0 >= red) ? 0 : red; final int maxGreen = (0 >= green) ? 0 : green; final int maxBlue = (0 >= blue) ? 0 : blue; red = (255 <= maxRed) ? 255 : maxRed; green = (255 <= maxGreen) ? 255 : maxGreen; blue = (255 <= maxBlue) ? 255 : maxBlue; final int colorValue = ((red & 0xFF) << 0) | ((green & 0xFF) << 8) | ((blue & 0xFF) << 16); return colorValue; } private static void getTourPainterSettings() { final IPreferenceStore prefStore = TourbookPlugin.getDefault().getPreferenceStore(); final String drawSymbol = prefStore.getString(ITourbookPreferences.MAP_LAYOUT_SYMBOL); _prefIsDrawLine = drawSymbol.equals(PrefPageAppearanceMap.MAP_TOUR_SYMBOL_LINE); _prefIsDrawSquare = drawSymbol.equals(PrefPageAppearanceMap.MAP_TOUR_SYMBOL_SQUARE); _prefLineWidth = prefStore.getInt(ITourbookPreferences.MAP_LAYOUT_SYMBOL_WIDTH); _prefWithBorder = prefStore.getBoolean(ITourbookPreferences.MAP_LAYOUT_PAINT_WITH_BORDER); _prefBorderWidth = prefStore.getInt(ITourbookPreferences.MAP_LAYOUT_BORDER_WIDTH); } private void createImages() { _tourStartMarker = TourbookPlugin.getImageDescriptor(Messages.Image_Map_TourStartMarker).createImage(); _tourEndMarker = TourbookPlugin.getImageDescriptor(Messages.Image_Map_TourEndMarker).createImage(); _twpImage = TourbookPlugin.getImageDescriptor(Messages.Image_Map_WayPoint).createImage(); _twpImageBounds = _twpImage.getBounds(); _isImageAvailable = true; } @Override protected void dispose() { disposeImage(_tourStartMarker); disposeImage(_tourEndMarker); _isImageAvailable = false; } private void disposeImage(final Image image) { if (image != null && !image.isDisposed()) { image.dispose(); } } @Override protected void disposeTempResources() { // _colorCache.dispose(); } @Override protected boolean doPaint(final GC gc, final Map map, final Tile tile, final int parts) { final PaintManager paintManager = PaintManager.getInstance(); final ArrayList<TourData> tourDataList = paintManager.getTourData(); if (tourDataList == null) { return false; } boolean isTourInTile = false; if (_isImageAvailable == false) { createImages(); } // draw tour first, then the marker for (final TourData tourData : tourDataList) { if (tourData == null) { continue; } // check if position is available final double[] latitudeSerie = tourData.latitudeSerie; final double[] longitudeSerie = tourData.longitudeSerie; if (latitudeSerie == null || longitudeSerie == null) { continue; } setDataSerie(tourData); final boolean isDrawTourInTile = drawTour10InTile(gc, map, tile, tourData, parts); isTourInTile = isTourInTile || isDrawTourInTile; // /** // * DEBUG Start // */ // gc.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE)); // gc.fillRectangle(0, 0, 2, 50); // gc.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_RED)); // gc.fillRectangle(2, 0, 2, 50); // gc.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_GREEN)); // gc.fillRectangle(4, 0, 2, 50); // gc.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_BLUE)); // gc.fillRectangle(6, 0, 2, 50); // isTourInTile = true; // /** // * DEBUG End // */ // status if a marker is drawn boolean isMarkerInTile = false; // draw start/end marker if (paintManager.isShowStartEndInMap()) { // draw end marker first isMarkerInTile = drawStaticMarker(gc, map, tile, latitudeSerie[latitudeSerie.length - 1], longitudeSerie[longitudeSerie.length - 1], _tourEndMarker, parts); isTourInTile = isTourInTile || isMarkerInTile; // draw start marker above the end marker isMarkerInTile = drawStaticMarker(// gc, map, tile, latitudeSerie[0], longitudeSerie[0], _tourStartMarker, parts); } isTourInTile = isTourInTile || isMarkerInTile; } if (paintManager._isShowTourMarker || paintManager._isShowWayPoints) { // draw marker above the tour for (final TourData tourData : tourDataList) { if (tourData == null) { continue; } // check if geo position is available final double[] latitudeSerie = tourData.latitudeSerie; final double[] longitudeSerie = tourData.longitudeSerie; if (latitudeSerie == null || longitudeSerie == null) { continue; } setDataSerie(tourData); if (paintManager._isShowTourMarker) { // ckeck if markers are available final ArrayList<TourMarker> sortedMarkers = tourData.getTourMarkersSorted(); if (sortedMarkers.size() > 0) { // draw tour marker boolean isTourMarkerInTile = false; boolean isTourMarkerInTile2 = false; for (final TourMarker tourMarker : sortedMarkers) { if (tourMarker.getLabel().length() == 0) { // skip empty marker continue; } final int serieIndex = tourMarker.getSerieIndex(); // draw tour marker isTourMarkerInTile2 = drawTourMarker(gc, map, tile, latitudeSerie[serieIndex], longitudeSerie[serieIndex], tourMarker, parts); isTourMarkerInTile = isTourMarkerInTile || isTourMarkerInTile2; } isTourInTile = isTourInTile || isTourMarkerInTile; } } if (paintManager._isShowWayPoints) { // ckeck if way points are available final Set<TourWayPoint> wayPoints = tourData.getTourWayPoints(); if (wayPoints.size() > 0) { /* * world positions are cached to optimize performance */ final MP mp = map.getMapProvider(); final String projectionId = mp.getProjection().getId(); final int mapZoomLevel = map.getZoom(); HashMap<Integer, Point> allWayPointWorldPixel = tourData .getWorldPositionForWayPoints(projectionId, mapZoomLevel); if ((allWayPointWorldPixel == null)) { allWayPointWorldPixel = initWorldPixelWayPoint(tourData, wayPoints, mp, projectionId, mapZoomLevel); } // draw tour way points boolean isTourWayPointInTile = false; boolean isTourWayPointInTile2 = false; for (final TourWayPoint tourWayPoint : wayPoints) { final Point twpWorldPixel = allWayPointWorldPixel.get(tourWayPoint.hashCode()); isTourWayPointInTile2 = drawTourWayPoint(gc, map, tile, tourWayPoint, twpWorldPixel, parts); isTourWayPointInTile = isTourWayPointInTile || isTourWayPointInTile2; } isTourInTile = isTourInTile || isTourWayPointInTile; } } } } return isTourInTile; } private boolean drawStaticMarker(final GC gc, final Map map, final Tile tile, final double latitude, final double longitude, final Image markerImage, final int parts) { if (markerImage == null) { return false; } final MP mp = map.getMapProvider(); final int zoomLevel = map.getZoom(); final int tileSize = mp.getTileSize(); final int devPartOffset = ((parts - 1) / 2) * tileSize; // get world viewport for the current tile final int worldPixelTileX = tile.getX() * tileSize; final int worldPixelTileY = tile.getY() * tileSize; // convert lat/long into world pixels final Point worldPixelMarker = mp.geoToPixel(new GeoPosition(latitude, longitude), zoomLevel); // convert world position into device position final int devMarkerPosX = worldPixelMarker.x - worldPixelTileX; final int devMarkerPosY = worldPixelMarker.y - worldPixelTileY; final boolean isMarkerInTile = isBoundsInTile(markerImage.getBounds(), devMarkerPosX, devMarkerPosY, tileSize); if (isMarkerInTile) { // get marker size final Rectangle bounds = markerImage.getBounds(); final int markerWidth = bounds.width; final int markerWidth2 = markerWidth / 2; final int markerHeight = bounds.height; gc.drawImage(markerImage, // devMarkerPosX - markerWidth2 + devPartOffset, devMarkerPosY - markerHeight + devPartOffset); } return isMarkerInTile; } private boolean drawTour10InTile(final GC gc, final Map map, final Tile tile, final TourData tourData, final int parts) { boolean isTourInTile = false; final MP mp = map.getMapProvider(); final int mapZoomLevel = map.getZoom(); final int tileSize = mp.getTileSize(); final int devPartOffset = ((parts - 1) / 2) * tileSize; // get viewport for the current tile final int tileWorldPixelX = tile.getX() * tileSize; final int tileWorldPixelY = tile.getY() * tileSize; final int tileWidth = tileSize; final int tileHeight = tileSize; int devFromWithOffsetX = 0; int devFromWithOffsetY = 0; final double[] latitudeSerie = tourData.latitudeSerie; final double[] longitudeSerie = tourData.longitudeSerie; /* * world positions are cached to optimize performance when multiple tours are selected */ final String projectionId = mp.getProjection().getId(); Point tourWorldPixelPosAll[] = tourData.getWorldPositionForTour(projectionId, mapZoomLevel); if ((tourWorldPixelPosAll == null)) { tourWorldPixelPosAll = initWorldPixelTour(tourData, mp, mapZoomLevel, latitudeSerie, longitudeSerie, projectionId); } final Color systemColorBlue = gc.getDevice().getSystemColor(SWT.COLOR_BLUE); gc.setForeground(systemColorBlue); gc.setBackground(systemColorBlue); int lastInsideIndex = -99; boolean isBorder; // index == 0: paint border // index == 1: paint tour symbol for (int lineIndex = 0; lineIndex < 2; lineIndex++) { if (lineIndex == 0) { if (_prefWithBorder == false) { // skip border continue; } isBorder = true; // draw line border _lineWidth = _prefLineWidth + (_prefBorderWidth * 2); } else if (lineIndex == 1) { isBorder = false; // draw within the border _lineWidth = _prefLineWidth; } else { break; } _lineWidth2 = _lineWidth / 2; gc.setLineWidth(_lineWidth); for (int serieIndex = 0; serieIndex < longitudeSerie.length; serieIndex++) { final Point tourWorldPixel = tourWorldPixelPosAll[serieIndex]; final int tourWorldPixelX = tourWorldPixel.x; final int tourWorldPixelY = tourWorldPixel.y; int devX = tourWorldPixelX - tileWorldPixelX; int devY = tourWorldPixelY - tileWorldPixelY; if (_prefIsDrawLine) { // check if position is in the viewport // get positions with the part offset final int devToWithOffsetX = devX + devPartOffset; final int devToWithOffsetY = devY + devPartOffset; if (serieIndex == 0) { // keep position devFromWithOffsetX = devToWithOffsetX; devFromWithOffsetY = devToWithOffsetY; continue; } Color color = null; // this condition is an inline for: // tileViewport.contains(tileWorldPos.x, tileWorldPos.y) if ((tourWorldPixelX >= tileWorldPixelX) && (tourWorldPixelY >= tileWorldPixelY) && tourWorldPixelX < (tileWorldPixelX + tileWidth) && tourWorldPixelY < (tileWorldPixelY + tileHeight)) { // current position is inside the tile // check if position has changed if (devToWithOffsetX != devFromWithOffsetX || devToWithOffsetY != devFromWithOffsetY) { isTourInTile = true; color = getTourColor(isBorder, serieIndex); drawTour20Line(gc, // devFromWithOffsetX, devFromWithOffsetY, devToWithOffsetX, devToWithOffsetY, color); } lastInsideIndex = serieIndex; } // current position is outside the tile if (serieIndex == lastInsideIndex + 1) { /* * this position is the first which is outside of the tile, draw a line from * the last inside to the first outside position */ drawTour20Line(gc, // devFromWithOffsetX, devFromWithOffsetY, devToWithOffsetX, devToWithOffsetY, color); } // keep position devFromWithOffsetX = devToWithOffsetX; devFromWithOffsetY = devToWithOffsetY; } else { // draw tour with dots/squares // this is an inline for: tileViewport.contains(tileWorldPos.x, tileWorldPos.y) // check if position is in the viewport if ((tourWorldPixelX >= tileWorldPixelX) && (tourWorldPixelY >= tileWorldPixelY) && tourWorldPixelX < (tileWorldPixelX + tileWidth) && tourWorldPixelY < (tileWorldPixelY + tileHeight)) { // current position is inside the tile // optimize drawing: check if position has changed if (devX != devFromWithOffsetX && devY != devFromWithOffsetY) { isTourInTile = true; // adjust positions with the part offset devX += devPartOffset; devY += devPartOffset; final Color color = getTourColor(isBorder, serieIndex); if (_prefIsDrawSquare) { drawTour30Square(gc, devX, devY, color); } else { drawTour40Dot(gc, devX, devY, color); } // set previous pixel devFromWithOffsetX = devX; devFromWithOffsetY = devY; } } } } } _colorCache.dispose(); return isTourInTile; } private void drawTour20Line(final GC gc, final int devXFrom, final int devYFrom, final int devXTo, final int devYTo, final Color color) { if (color != null) { gc.setForeground(color); } drawTour40Dot(gc, devXTo, devYTo, color); // draw line with the color from the legend provider gc.drawLine(devXFrom, devYFrom, devXTo, devYTo); } private void drawTour30Square(final GC gc, final int devX, final int devY, final Color color) { if (color != null) { gc.setBackground(color); } gc.fillRectangle(devX - _lineWidth2, devY - _lineWidth2, _lineWidth, _lineWidth); } private void drawTour40Dot(final GC gc, final int devX, final int devY, final Color color) { if (color != null) { gc.setBackground(color); } if (_lineWidth == 2) { // oval is not filled by a width of 2 gc.fillRectangle(devX, devY, _lineWidth, _lineWidth); } else { gc.fillOval(devX - _lineWidth2, devY - _lineWidth2, _lineWidth, _lineWidth); } } private boolean drawTourMarker(final GC gc, final Map map, final Tile tile, final double latitude, final double longitude, final TourMarker tourMarker, final int parts) { final MP mp = map.getMapProvider(); final int zoomLevel = map.getZoom(); final int tileSize = mp.getTileSize(); final int devPartOffset = ((parts - 1) / 2) * tileSize; // get world viewport for the current tile final int worldTileX = tile.getX() * tileSize; final int worldTileY = tile.getY() * tileSize; // convert lat/long into world pixels final Point worldMarkerPos = mp.geoToPixel(new GeoPosition(latitude, longitude), zoomLevel); // convert world position into device position final int devMarkerPosX = worldMarkerPos.x - worldTileX; final int devMarkerPosY = worldMarkerPos.y - worldTileY; Rectangle markerBounds = tourMarker.getMarkerBounds(); if (markerBounds == null) { /* * create and cache marker bounds */ final org.eclipse.swt.graphics.Point labelExtent = gc.textExtent(tourMarker.getLabel()); final int bannerWidth = labelExtent.x + 2 * MARKER_MARGIN + 1; final int bannerHeight = labelExtent.y + 2 * MARKER_MARGIN; final int markerImageWidth = bannerWidth; final int markerImageHeight = bannerHeight + MARKER_POLE; markerBounds = new Rectangle(bannerWidth, bannerHeight, markerImageWidth, markerImageHeight); tourMarker.setMarkerBounds(markerBounds); } final boolean isMarkerInTile = isBoundsInTile(markerBounds, devMarkerPosX, devMarkerPosY, tileSize); if (isMarkerInTile) { int devX; int devY; final Image tourMarkerImage = drawTourMarkerImage(gc.getDevice(), tourMarker.getLabel(), markerBounds); { devX = devMarkerPosX - markerBounds.width / 2; devY = devMarkerPosY - markerBounds.height; devX += devPartOffset; devY += devPartOffset; gc.drawImage(tourMarkerImage, devX, devY); } tourMarkerImage.dispose(); tile.addMarkerBounds(devX, devY, markerBounds.x, markerBounds.y, zoomLevel, parts); } return isMarkerInTile; } /** * create an image for the tour marker * * @param device * @param markerBounds * @param tourMarker * @return */ private Image drawTourMarkerImage(final Device device, final String markerLabel, final Rectangle markerBounds) { final int bannerWidth = markerBounds.x; final int bannerHeight = markerBounds.y; final int bannerWidth2 = bannerWidth / 2; final int markerImageWidth = markerBounds.width; final int markerImageHeight = markerBounds.height; final int arcSize = 5; final RGB rgbTransparent = Map.getTransparentRGB(); final ImageData markerImageData = new ImageData(// markerImageWidth, markerImageHeight, 24, new PaletteData(0xff, 0xff00, 0xff0000)); markerImageData.transparentPixel = markerImageData.palette.getPixel(rgbTransparent); final Image markerImage = new Image(device, markerImageData); final Rectangle markerImageBounds = markerImage.getBounds(); final Color transparentColor = new Color(device, rgbTransparent); final Color bannerColor = new Color(device, 0x65, 0xF9, 0x1F); final Color bannerBorderColor = new Color(device, 0x69, 0xAF, 0x3D); final GC gc = new GC(markerImage); { // fill transparent color gc.setBackground(transparentColor); gc.fillRectangle(markerImageBounds); gc.setBackground(bannerColor); gc.fillRoundRectangle(0, 0, bannerWidth, bannerHeight, arcSize, arcSize); // draw banner border gc.setForeground(bannerBorderColor); gc.drawRoundRectangle(0, 0, bannerWidth - 1, bannerHeight - 1, arcSize, arcSize); // draw text gc.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_BLACK)); gc.drawText(markerLabel, // MARKER_MARGIN + 1, MARKER_MARGIN, true); // draw pole gc.setForeground(bannerBorderColor); gc.drawLine(bannerWidth2 - 1, bannerHeight, bannerWidth2 - 1, bannerHeight + MARKER_POLE); gc.drawLine(bannerWidth2 + 1, bannerHeight, bannerWidth2 + 1, bannerHeight + MARKER_POLE); gc.setForeground(bannerColor); gc.drawLine(bannerWidth2 - 0, bannerHeight, bannerWidth2 - 0, bannerHeight + MARKER_POLE); // draw image debug border // gc.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_BLUE)); // gc.drawRectangle(0, 0, markerImageBounds.width - 1, markerImageBounds.height - 1); } gc.dispose(); bannerColor.dispose(); bannerBorderColor.dispose(); transparentColor.dispose(); return markerImage; } private boolean drawTourWayPoint(final GC gc, final Map map, final Tile tile, final TourWayPoint twp, final Point twpWorldPixel, final int parts) { final MP mp = map.getMapProvider(); final int zoomLevel = map.getZoom(); final int tileSize = mp.getTileSize(); final int devPartOffset = ((parts - 1) / 2) * tileSize; // get world viewport for the current tile final int tileWorldPixelX = tile.getX() * tileSize; final int tilwWorldPixelY = tile.getY() * tileSize; // convert world position into device position final int devWayPointX = twpWorldPixel.x - tileWorldPixelX; final int devWayPointY = twpWorldPixel.y - tilwWorldPixelY; final boolean isBoundsInTile = isBoundsInTile(_twpImageBounds, devWayPointX, devWayPointY, tileSize); if (isBoundsInTile) { int devX = devWayPointX - _twpImageBounds.width / 2; int devY = devWayPointY - _twpImageBounds.height; devX += devPartOffset; devY += devPartOffset; gc.drawImage(_twpImage, devX, devY); // gc.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_BLUE)); // gc.setLineWidth(1); // gc.drawRectangle(devX, devY, _twpImageBounds.width, _twpImageBounds.height); // tile.addTourWayPointBounds(// twp, new Rectangle(devX - devPartOffset, devY - devPartOffset, _twpImageBounds.width, _twpImageBounds.height), zoomLevel, parts); /* * check if the way point paints into a neighbour tile */ if (parts > 1) { } } return isBoundsInTile; } /** * @param legendBounds * @param valueIndex * @return Returns the position for the value according to the value index in the legend, * {@link Integer#MIN_VALUE} when data are not initialized */ public int getLegendValuePosition(final Rectangle legendBounds, final int valueIndex) { if (_dataSerie == null || valueIndex >= _dataSerie.length) { return Integer.MIN_VALUE; } /* * ONLY VERTICAL LEGENDS ARE SUPPORTED */ final int dataValue = _dataSerie[valueIndex]; int valuePosition = 0; final LegendConfig config = _legendProvider.getLegendConfig(); // final Integer unitFactor = config.unitFactor; // dataValue /= unitFactor; final int legendMaxValue = config.legendMaxValue; final int legendMinValue = config.legendMinValue; final int legendDiffValue = legendMaxValue - legendMinValue; if (dataValue >= legendMaxValue) { // value >= max } else if (dataValue <= legendMinValue) { // value <= min } else { // min < value < max // int legendPositionX = legendBounds.x + 1; final int legendPositionY = legendBounds.y + TourMapView.LEGEND_MARGIN_TOP_BOTTOM; // int legendWidth = 20; final int legendHeight = legendBounds.height - 2 * TourMapView.LEGEND_MARGIN_TOP_BOTTOM; final int pixelDiff = legendHeight - 1; // pixelValue contains the value for ONE pixel // final float pixelValue = (float) legendDiffValue / availableLegendPixels; // valuePosition = legendPositionY + availableLegendPixels - pixelIndex; final int dataValue0 = dataValue - legendMinValue; final float ratio = pixelDiff / (float) legendDiffValue; valuePosition = legendPositionY + (int) (dataValue0 * ratio); } return valuePosition; } private Color getTourColor(final boolean isBorder, final int serieIndex) { if (_dataSerie == null) { return null; } int colorValue = _legendProvider.getColorValue(_dataSerie[serieIndex]); if (isBorder) { // paint the border in a darker color final float brightness = 0.8f; final int red = (int) (((colorValue & 0xFF) >>> 0) * brightness); final int green = (int) (((colorValue & 0xFF00) >>> 8) * brightness); final int blue = (int) (((colorValue & 0xFF0000) >>> 16) * brightness); colorValue = ((red & 0xFF) << 0) | ((green & 0xFF) << 8) | ((blue & 0xFF) << 16); } return _colorCache.get(colorValue); } /** * world pixels are not yet cached, create them now * * @param tourData * @param mp * @param mapZoomLevel * @param latitudeSerie * @param longitudeSerie * @param projectionId * @return */ private Point[] initWorldPixelTour(final TourData tourData, final MP mp, final int mapZoomLevel, final double[] latitudeSerie, final double[] longitudeSerie, final String projectionId) { final Point[] tourWorldPixelPosAll = new Point[latitudeSerie.length]; for (int serieIndex = 0; serieIndex < longitudeSerie.length; serieIndex++) { // convert lat/long into world pixels which depends on the map projection tourWorldPixelPosAll[serieIndex] = mp.geoToPixel(// new GeoPosition(latitudeSerie[serieIndex], longitudeSerie[serieIndex]), mapZoomLevel); } tourData.setWorldPixelForTour(tourWorldPixelPosAll, mapZoomLevel, projectionId); return tourWorldPixelPosAll; } private HashMap<Integer, Point> initWorldPixelWayPoint(final TourData tourData, final Set<TourWayPoint> wayPoints, final MP mp, final String projectionId, final int mapZoomLevel) { // world pixels are not yet cached, create them now final HashMap<Integer, Point> allWayPointWorldPixel = new HashMap<Integer, Point>(); for (final TourWayPoint twp : wayPoints) { // convert lat/long into world pixels which depends on the map projection final GeoPosition geoPosition = new GeoPosition(twp.getLatitude(), twp.getLongitude()); allWayPointWorldPixel.put(twp.hashCode(), mp.geoToPixel(geoPosition, mapZoomLevel)); } tourData.setWorldPixelForWayPoints(allWayPointWorldPixel, mapZoomLevel, projectionId); return allWayPointWorldPixel; } /** * Checks if an image bounds is within the tile. The image is above the image position and one * half to the left and right side * * @param imageBounds * bounds of the image * @param devImagePosX * x position for the image * @param devImagePosY * y position for the image * @param tileSize * width and height of the tile * @return Returns <code>true</code> when the image is visible in the tile */ private boolean isBoundsInTile(final Rectangle imageBounds, final int devImagePosX, final int devImagePosY, final int tileSize) { // get image size final int imageWidth = imageBounds.width; final int imageWidth2 = imageWidth / 2; final int imageHeight = imageBounds.height; final int devImagePosLeft = devImagePosX - imageWidth2; final int devImagePosRight = devImagePosX + imageWidth2; // image position top is in the opposite direction final int devImagePosTop = devImagePosY - imageHeight; if (((devImagePosLeft >= 0 && devImagePosLeft <= tileSize) || (devImagePosRight >= 0 && devImagePosRight <= tileSize)) && (devImagePosY >= 0 && devImagePosY <= tileSize || devImagePosTop >= 0 && devImagePosTop <= tileSize)) { return true; } return false; } @Override protected boolean isPaintingNeeded(final Map map, final Tile tile) { final PaintManager paintManager = PaintManager.getInstance(); final ArrayList<TourData> tourDataList = paintManager.getTourData(); if (tourDataList == null) { return false; } if (_isImageAvailable == false) { createImages(); } final MP mp = map.getMapProvider(); final int mapZoomLevel = map.getZoom(); final int tileSize = mp.getTileSize(); final String projectionId = mp.getProjection().getId(); // get viewport for the current tile final int tileWorldPixelLeft = tile.getX() * tileSize; final int tileWorldPixelRight = tileWorldPixelLeft + tileSize; final int tileWorldPixelTop = tile.getY() * tileSize; final int tileWorldPixelBottom = tileWorldPixelTop + tileSize; /* * check tours */ for (final TourData tourData : tourDataList) { // check tour data if (tourData == null) { continue; } // check if position is available final double[] latitudeSerie = tourData.latitudeSerie; final double[] longitudeSerie = tourData.longitudeSerie; if (latitudeSerie != null && longitudeSerie != null) { /* * world positions are cached to optimize performance when multiple tours are * selected */ Point tourWorldPixelPosAll[] = tourData.getWorldPositionForTour(projectionId, mapZoomLevel); if ((tourWorldPixelPosAll == null)) { // world pixels are not yet cached, create them now tourWorldPixelPosAll = initWorldPixelTour(tourData, mp, mapZoomLevel, latitudeSerie, longitudeSerie, projectionId); } for (int serieIndex = 0; serieIndex < longitudeSerie.length; serieIndex++) { final Point tourWorldPixel = tourWorldPixelPosAll[serieIndex]; // this is an inline for: tileViewport.contains(tileWorldPos.x, tileWorldPos.y) final int tourWorldPixelX = tourWorldPixel.x; final int tourWorldPixelY = tourWorldPixel.y; // check if position is within the tile viewport if ((tourWorldPixelX >= tileWorldPixelLeft) && (tourWorldPixelY >= tileWorldPixelTop) && tourWorldPixelX < tileWorldPixelRight && tourWorldPixelY < tileWorldPixelBottom) { // current position is inside the tile return true; } } } /* * check way points */ final Set<TourWayPoint> wayPoints = tourData.getTourWayPoints(); if (wayPoints.size() > 0) { HashMap<Integer, Point> allWayPointWorldPixel = tourData.getWorldPositionForWayPoints(projectionId, mapZoomLevel); if ((allWayPointWorldPixel == null)) { allWayPointWorldPixel = initWorldPixelWayPoint(tourData, wayPoints, mp, projectionId, mapZoomLevel); } // get image size final int imageWidth = _twpImageBounds.width; final int imageWidth2 = imageWidth / 2; final int imageHeight = _twpImageBounds.height; for (final TourWayPoint twp : wayPoints) { final Point twpWorldPixel = allWayPointWorldPixel.get(twp.hashCode()); if (twpWorldPixel == null) { // this happened but should not continue; } // this is an inline for: tileViewport.contains(tileWorldPos.x, tileWorldPos.y) final int twpWorldPixelX = twpWorldPixel.x; final int twpWorldPixelY = twpWorldPixel.y; final int twpImageWorldPixelX = twpWorldPixelX - imageWidth2; // check if twp image is within the tile viewport if (twpImageWorldPixelX + imageWidth >= tileWorldPixelLeft && twpWorldPixelX < tileWorldPixelRight && twpWorldPixelY >= tileWorldPixelTop && twpWorldPixelY < tileWorldPixelBottom + imageHeight) { // current position is inside the tile return true; } } } } return false; } /** * Sets data serie which is painted * * @param tourData */ private void setDataSerie(final TourData tourData) { final ILegendProvider legendProvider = PaintManager.getInstance().getLegendProvider(); if (legendProvider == null) { _dataSerie = null; return; } _legendProvider = legendProvider; switch (_legendProvider.getTourColorId()) { case TourMapView.TOUR_COLOR_ALTITUDE: final int[] altitudeSerie = tourData.getAltitudeSerie(); if (altitudeSerie == null) { _dataSerie = null; } else { _dataSerie = altitudeSerie; } break; case TourMapView.TOUR_COLOR_GRADIENT: final int[] gradientSerie = tourData.getGradientSerie(); if (gradientSerie == null) { _dataSerie = null; } else { _dataSerie = gradientSerie; } break; case TourMapView.TOUR_COLOR_PULSE: final int[] pulseSerie = tourData.pulseSerie; if (pulseSerie == null) { _dataSerie = null; } else { _dataSerie = pulseSerie; } break; case TourMapView.TOUR_COLOR_SPEED: final int[] speedSerie = tourData.getSpeedSerie(); if (speedSerie == null) { _dataSerie = null; } else { _dataSerie = speedSerie; } break; case TourMapView.TOUR_COLOR_PACE: final int[] paceSerie = tourData.getPaceSerie(); if (paceSerie == null) { _dataSerie = null; } else { _dataSerie = paceSerie; } break; default: break; } } }