package tim.prune.gui.map; /** * Class to hold the current position of the map */ public class MapPosition { /** Width and height of each tile of map */ private static final int MAP_TILE_SIZE = 256; /** x position (scale depends on zoom) */ private int _xPosition = 0; /** y position (scale depends on zoom) */ private int _yPosition = 0; /** Zoom level, from 2 to max */ private int _zoom = 12; /** Factor to zoom by, 2 to the power of zoom */ private int _zoomFactor = 1 << _zoom; /** Maximum zoom level */ private static final int MAX_ZOOM = 21; /** * Zoom and pan to show the selected area * @param inMinX minimum transformed X * @param inMaxX maximum transformed X * @param inMinY minimum transformed Y * @param inMaxY maximum transformed Y * @param inWidth width of display * @param inHeight height of display */ public void zoomToXY(double inMinX, double inMaxX, double inMinY, double inMaxY, int inWidth, int inHeight) { // System.out.println("Zooming to " + inMinX + ", " + inMaxX + ", " + inMinY + ", " + inMaxY + "; width=" + inWidth + ", height=" + inHeight); double diffX = Math.abs(inMaxX - inMinX); double diffY = Math.abs(inMaxY - inMinY); // Find out what zoom level to go to int requiredZoom = -1; for (int currZoom = MAX_ZOOM; currZoom >= 2; currZoom--) { if (transformToPixels(diffX, currZoom) < inWidth && transformToPixels(diffY, currZoom) < inHeight) { requiredZoom = currZoom; break; } } if (requiredZoom < 2) requiredZoom = 2; // Set position setZoom(requiredZoom); _xPosition = transformToPixels((inMinX + inMaxX) / 2.0); _yPosition = transformToPixels((inMinY + inMaxY) / 2.0); } /** * Ensure that zoom and zoomFactor remain in sync * @param inZoom zoom level to set */ private void setZoom(int inZoom) { _zoom = inZoom; _zoomFactor = 1 << _zoom; } /** * Zoom and pan to show the selected area * @param inMinX minimum pixels X * @param inMaxX maximum pixels X * @param inMinY minimum pixels Y * @param inMaxY maximum pixels Y * @param inWidth width of display * @param inHeight height of display */ public void zoomToPixels(int inMinX, int inMaxX, int inMinY, int inMaxY, int inWidth, int inHeight) { // System.out.println("Current position is " + _xPosition + ", " + _yPosition); int diffX = Math.abs(inMaxX - inMinX); int diffY = Math.abs(inMaxY - inMinY); // Find out what zoom level to go to int requiredZoom = -1; int multFactor = 0; for (int currZoom = MAX_ZOOM; currZoom >= _zoom; currZoom--) { multFactor = 1 << (currZoom - _zoom); if ((diffX * multFactor) < inWidth && (diffY * multFactor) < inHeight) { requiredZoom = currZoom; break; } } setZoom(requiredZoom); // Set position _xPosition = (_xPosition - inWidth/2 + (inMinX + inMaxX) / 2) * multFactor; _yPosition = (_yPosition - inHeight/2 + (inMinY + inMaxY) / 2) * multFactor; } /** * Transform a given coordinate into pixels using the current zoom value * @param inValue value to transform * @return pixels */ private int transformToPixels(double inValue) { return transformToPixels(inValue, _zoom); } /** * Transform a given coordinate into pixels using the specified zoom value * @param inValue value to transform * @param inZoom zoom value to use * @return pixels */ private static int transformToPixels(double inValue, int inZoom) { return (int) (inValue * MAP_TILE_SIZE * (1 << inZoom)); } /** * Convert pixels back into x coordinates * @param inPixelX x coordinate on screen * @param inWidth current width of window * @return x coordinate */ public double getXFromPixels(int inPixelX, int inWidth) { return ((inPixelX - inWidth/2) + _xPosition) * 1.0 / MAP_TILE_SIZE / _zoomFactor; } /** * Convert pixels back into y coordinates * @param inPixelY y coordinate on screen * @param inHeight current height of window * @return y coordinate */ public double getYFromPixels(int inPixelY, int inHeight) { return ((inPixelY - inHeight/2) + _yPosition) * 1.0 / MAP_TILE_SIZE / _zoomFactor; } /** * Get the horizontal offset from the centre * @param inValue value to transform * @return number of pixels right (+ve) or left (-ve) from the centre */ public int getXFromCentre(double inValue) { return transformToPixels(inValue) - _xPosition; } /** * Get the vertical offset from the centre * @param inValue value to transform * @return number of pixels up (+ve) or down (-ve) from the centre */ public int getYFromCentre(double inValue) { return transformToPixels(inValue) - _yPosition; } /** * Convert a pixel value into a bounds value for sensitivity * @param inPixels number of pixels * @return bounds value to use for x,y checking */ public double getBoundsFromPixels(int inPixels) { return (inPixels * 1.0 / MAP_TILE_SIZE / _zoomFactor); } /** * Get the leftmost, rightmost, upper and lower index boundaries for the tiles to display * @param inWidth width of window * @param inHeight height of window * @return tile indices as array left, right, up, down */ public int[] getTileIndices(int inWidth, int inHeight) { int[] result = new int[4]; result[0] = getTileIndex(_xPosition - inWidth/2); result[1] = getTileIndex(_xPosition + inWidth/2); result[2] = getTileIndex(_yPosition - inHeight/2); result[3] = getTileIndex(_yPosition + inHeight/2); return result; } /** * Get the pixel offsets for the display * @param inWidth width of window * @param inHeight height of window * @return offsets as x, y */ public int[] getDisplayOffsets(int inWidth, int inHeight) { int[] result = new int[2]; result[0] = getDisplayOffset(_xPosition - inWidth/2); result[1] = getDisplayOffset(_yPosition - inHeight/2); return result; } /** * @return x index of the centre tile */ public int getCentreTileX() { return getTileIndex(_xPosition); } /** * @return y index of the centre tile */ public int getCentreTileY() { return getTileIndex(_yPosition); } /** * @param inPosition position of point * @return tile index for that point */ private int getTileIndex(int inPosition) { return inPosition / MAP_TILE_SIZE; } /** * @param inPosition position of point * @return pixel offset for that point */ private int getDisplayOffset(int inPosition) { return inPosition % MAP_TILE_SIZE; // I thought that &255 would be slightly faster, but it gives the wrong result } /** * Zoom in one level */ public void zoomIn() { if (_zoom < MAX_ZOOM) { setZoom(_zoom + 1); _xPosition *= 2; _yPosition *= 2; } } /** * Zoom out one level */ public void zoomOut() { if (_zoom >= 3) { setZoom(_zoom - 1); _xPosition /= 2; _yPosition /= 2; } } /** * @return current zoom level */ public int getZoom() { return _zoom; } /** * Pan map by the specified amount * @param inDeltaX amount to pan right * @param inDeltaY amount to pan down */ public void pan(int inDeltaX, int inDeltaY) { // TODO: Check bounds? _xPosition += inDeltaX; _yPosition += inDeltaY; } }