package tim.prune.gui.map;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JPanel;
import tim.prune.I18nManager;
import tim.prune.config.ColourScheme;
import tim.prune.config.Config;
import tim.prune.data.Unit;
/**
* Class to show a scale bar on the main map of GpsPrune
*/
public class ScaleBar extends JPanel
{
/** zoom level */
private int _zoomLevel = -1;
/** y position */
private double _yPos = 0.0;
// Dimensions
/** Offset from left side in pixels */
private static final int LEFT_OFFSET = 20;
/** Offset from top in pixels */
private static final int Y_OFFSET = 10;
/** Tick height in pixels */
private static final int TICK_HEIGHT = 5;
/** Margin between bar and end text in pixels */
private static final int MARGIN_WIDTH = 8;
/** scales for each zoom level */
private static final int[] _scales = {10000, 5000, 2000, 2000, 1000, 500, 200, 100,
50, 20, 10, 5, 2, 2, 1,
-2, -5, -10, -20, -50, -100, -200};
/** pixel counts for each zoom level (metric) */
private static final double[] _metricPixels = {64, 64, 51, 102, 102, 102, 81, 81,
81, 65, 65, 65, 52, 105, 105,
105, 83, 83, 83, 67, 67, 67};
/**
* Constructor
*/
public ScaleBar()
{
super();
setOpaque(false);
setPreferredSize(new Dimension(100, 20));
}
/**
* Paint method to override display
* @param inG graphics object
*/
public void paint(Graphics inG)
{
super.paint(inG);
if (_zoomLevel > -1)
{
try {
final double distScaleFactor = Config.getUnitSet().getDistanceUnit().getMultFactorFromStd();
double drightSide = LEFT_OFFSET + _metricPixels[_zoomLevel] / 1000.0 / distScaleFactor;
int scale = _scales[_zoomLevel];
// work out cos(latitude) from y position, and apply to scale
final double angle = Math.PI * (1 - 2*_yPos);
final double lat = Math.atan(Math.sinh(angle));
final double cosLat = Math.cos(lat);
int rightSide = (int) (drightSide / cosLat);
// Adjust if scale is too large
while (rightSide > 300)
{
rightSide /= 2;
scale /= 2;
// Abort if scale is now less than 1 unit (shouldn't ever be)
if (scale < 1) {return;}
}
// Abort if scale is negative (around poles)
if (rightSide < 1) {return;}
// Determine colours to use
Color barColour = Config.getColourScheme().getColour(ColourScheme.IDX_TEXT);
Color blankColour = new Color(255-barColour.getRed(), 255-barColour.getGreen(), 255-barColour.getBlue());
// Should this blank colour be set to saturation zero?
// Draw blank bars behind
inG.setColor(blankColour);
inG.drawLine(LEFT_OFFSET, Y_OFFSET-1, rightSide+2, Y_OFFSET-1);
inG.drawLine(LEFT_OFFSET, Y_OFFSET+2, rightSide+2, Y_OFFSET+2);
inG.drawLine(LEFT_OFFSET-1, Y_OFFSET+2, LEFT_OFFSET-1, Y_OFFSET-TICK_HEIGHT);
inG.drawLine(LEFT_OFFSET+2, Y_OFFSET+2, LEFT_OFFSET+2, Y_OFFSET-TICK_HEIGHT);
inG.drawLine(rightSide-1, Y_OFFSET+2, rightSide-1, Y_OFFSET-TICK_HEIGHT);
inG.drawLine(rightSide+2, Y_OFFSET+2, rightSide+2, Y_OFFSET-TICK_HEIGHT);
// horizontal
inG.setColor(barColour);
inG.drawLine(LEFT_OFFSET, Y_OFFSET, rightSide, Y_OFFSET);
inG.drawLine(LEFT_OFFSET, Y_OFFSET+1, rightSide, Y_OFFSET+1);
// 0 tick
inG.drawLine(LEFT_OFFSET, Y_OFFSET, LEFT_OFFSET, Y_OFFSET-TICK_HEIGHT);
inG.drawLine(LEFT_OFFSET+1, Y_OFFSET, LEFT_OFFSET+1, Y_OFFSET-TICK_HEIGHT);
// end tick
inG.drawLine(rightSide, Y_OFFSET+1, rightSide, Y_OFFSET-TICK_HEIGHT);
inG.drawLine(rightSide+1, Y_OFFSET+1, rightSide+1, Y_OFFSET-TICK_HEIGHT);
// text
String text = getScaleText(scale, Config.getUnitSet().getDistanceUnit());
inG.setColor(blankColour);
inG.drawString(text, rightSide+MARGIN_WIDTH-1, Y_OFFSET);
inG.drawString(text, rightSide+MARGIN_WIDTH+1, Y_OFFSET);
inG.drawString(text, rightSide+MARGIN_WIDTH, Y_OFFSET-1);
inG.drawString(text, rightSide+MARGIN_WIDTH, Y_OFFSET+1);
inG.setColor(barColour);
inG.drawString(text, rightSide+MARGIN_WIDTH, Y_OFFSET);
}
catch (ArrayIndexOutOfBoundsException ai) {}
}
}
/**
* Get the scale text for the given scale
* @param inScale scale number
* @param inDistUnit distance unit
* @return scale text as string
*/
private static String getScaleText(int inScale, Unit inDistUnit)
{
if (inScale > 0) {
// Positive scale means km or miles
return "" + inScale + " " +
I18nManager.getText(inDistUnit.getShortnameKey());
}
// negative scale means a fraction
return "" + (-1.0 / inScale) + " " + I18nManager.getText(inDistUnit.getShortnameKey());
// might be nice to say 100m instead of 0.1km, 275ft instead of 0.2miles, etc - need to be done by Unit itself?
}
/**
* Update the scale level
* @param inZoom new zoom level
* @param inYPos y position, where 0 is north pole, 1 is south pole
*/
public void updateScale(int inZoom, double inYPos)
{
_zoomLevel = inZoom;
_yPos = inYPos;
}
}