]> gitweb.fperrin.net Git - GpsPrune.git/blob - src/tim/prune/gui/map/MapPosition.java
Version 20.1, December 2020
[GpsPrune.git] / src / tim / prune / gui / map / MapPosition.java
1 package tim.prune.gui.map;
2
3 /**
4  * Class to hold the current position of the map
5  */
6 public class MapPosition
7 {
8         /** Width and height of each tile of map */
9         private static final int MAP_TILE_SIZE = 256;
10
11         /** x position (scale depends on zoom) */
12         private int _xPosition = 0;
13         /** y position (scale depends on zoom) */
14         private int _yPosition = 0;
15
16         /** Zoom level, from 2 to max */
17         private int _zoom = 12;
18         /** Factor to zoom by, 2 to the power of zoom */
19         private int _zoomFactor = 1 << _zoom;
20         /** Flag to mark if this position has ever been set */
21         private boolean _empty = true;
22         /** Maximum zoom level */
23         private static final int MAX_ZOOM = 21;
24
25
26         /**
27          * Zoom and pan to show the selected area
28          * @param inMinX minimum transformed X
29          * @param inMaxX maximum transformed X
30          * @param inMinY minimum transformed Y
31          * @param inMaxY maximum transformed Y
32          * @param inWidth width of display
33          * @param inHeight height of display
34          */
35         public void zoomToXY(double inMinX, double inMaxX, double inMinY, double inMaxY, int inWidth, int inHeight)
36         {
37                 // System.out.println("Zooming to " + inMinX + ", " + inMaxX + ", " + inMinY + ", " + inMaxY + "; width=" + inWidth + ", height=" + inHeight);
38                 double diffX = Math.abs(inMaxX - inMinX);
39                 double diffY = Math.abs(inMaxY - inMinY);
40                 // Find out what zoom level to go to
41                 int requiredZoom = -1;
42                 for (int currZoom = MAX_ZOOM; currZoom >= 2; currZoom--)
43                 {
44                         if (transformToPixels(diffX, currZoom) < inWidth
45                                 && transformToPixels(diffY, currZoom) < inHeight)
46                         {
47                                 requiredZoom = currZoom;
48                                 break;
49                         }
50                 }
51                 if (requiredZoom < 2) requiredZoom = 2;
52                 else if (requiredZoom > 18) requiredZoom = 18;
53                 // Set position
54                 setZoom(requiredZoom);
55                 _xPosition = transformToPixels((inMinX + inMaxX) / 2.0);
56                 _yPosition = transformToPixels((inMinY + inMaxY) / 2.0);
57                 _empty = false;
58         }
59
60         /**
61          * Ensure that zoom and zoomFactor remain in sync
62          * @param inZoom zoom level to set
63          */
64         private void setZoom(int inZoom)
65         {
66                 _zoom = inZoom;
67                 _zoomFactor = 1 << _zoom;
68                 _empty = false;
69         }
70
71         /**
72          * Zoom and pan to show the selected area
73          * @param inMinX minimum pixels X
74          * @param inMaxX maximum pixels X
75          * @param inMinY minimum pixels Y
76          * @param inMaxY maximum pixels Y
77          * @param inWidth width of display
78          * @param inHeight height of display
79          */
80         public void zoomToPixels(int inMinX, int inMaxX, int inMinY, int inMaxY, int inWidth, int inHeight)
81         {
82                 // System.out.println("Current position is " + _xPosition + ", " + _yPosition);
83                 int diffX = Math.abs(inMaxX - inMinX);
84                 int diffY = Math.abs(inMaxY - inMinY);
85                 // Find out what zoom level to go to
86                 int requiredZoom = -1;
87                 int multFactor = 0;
88                 for (int currZoom = MAX_ZOOM; currZoom >= _zoom; currZoom--)
89                 {
90                         multFactor = 1 << (currZoom - _zoom);
91                         if ((diffX * multFactor) < inWidth && (diffY * multFactor) < inHeight)
92                         {
93                                 requiredZoom = currZoom;
94                                 break;
95                         }
96                 }
97                 setZoom(requiredZoom);
98                 // Set position
99                 _xPosition = (_xPosition - inWidth/2 + (inMinX + inMaxX) / 2) * multFactor;
100                 _yPosition = (_yPosition - inHeight/2 + (inMinY + inMaxY) / 2) * multFactor;
101                 _empty = false;
102         }
103
104         /**
105          * Transform a given coordinate into pixels using the current zoom value
106          * @param inValue value to transform
107          * @return pixels
108          */
109         private int transformToPixels(double inValue)
110         {
111                 return transformToPixels(inValue, _zoom);
112         }
113
114         /**
115          * Transform a given coordinate into pixels using the specified zoom value
116          * @param inValue value to transform
117          * @param inZoom zoom value to use
118          * @return pixels
119          */
120         private static int transformToPixels(double inValue, int inZoom)
121         {
122                 return (int) (inValue * MAP_TILE_SIZE * (1 << inZoom));
123         }
124
125         /**
126          * Convert pixels back into x coordinates
127          * @param inPixelX x coordinate on screen
128          * @param inWidth current width of window
129          * @return x coordinate
130          */
131         public double getXFromPixels(int inPixelX, int inWidth)
132         {
133                 return ((inPixelX - inWidth/2) + _xPosition) * 1.0 / MAP_TILE_SIZE / _zoomFactor;
134         }
135
136         /**
137          * Convert pixels back into y coordinates
138          * @param inPixelY y coordinate on screen
139          * @param inHeight current height of window
140          * @return y coordinate
141          */
142         public double getYFromPixels(int inPixelY, int inHeight)
143         {
144                 return ((inPixelY - inHeight/2) + _yPosition) * 1.0 / MAP_TILE_SIZE / _zoomFactor;
145         }
146
147         /**
148          * Get the horizontal offset from the centre
149          * @param inValue value to transform
150          * @return number of pixels right (+ve) or left (-ve) from the centre
151          */
152         public int getXFromCentre(double inValue)
153         {
154                 return transformToPixels(inValue) - _xPosition;
155         }
156
157         /**
158          * Get the vertical offset from the centre
159          * @param inValue value to transform
160          * @return number of pixels up (+ve) or down (-ve) from the centre
161          */
162         public int getYFromCentre(double inValue)
163         {
164                 return transformToPixels(inValue) - _yPosition;
165         }
166
167         /**
168          * Convert a pixel value into a bounds value for sensitivity
169          * @param inPixels number of pixels
170          * @return bounds value to use for x,y checking
171          */
172         public double getBoundsFromPixels(int inPixels) {
173                 return (inPixels * 1.0 / MAP_TILE_SIZE / _zoomFactor);
174         }
175
176         /**
177          * Get the leftmost, rightmost, upper and lower index boundaries for the tiles to display
178          * @param inWidth width of window
179          * @param inHeight height of window
180          * @return tile indices as array left, right, up, down
181          */
182         public int[] getTileIndices(int inWidth, int inHeight)
183         {
184                 int[] result = new int[4];
185                 result[0] = getTileIndex(_xPosition - inWidth/2);
186                 result[1] = getTileIndex(_xPosition + inWidth/2);
187                 result[2] = getTileIndex(_yPosition - inHeight/2);
188                 result[3] = getTileIndex(_yPosition + inHeight/2);
189                 return result;
190         }
191
192         /**
193          * Get the pixel offsets for the display
194          * @param inWidth width of window
195          * @param inHeight height of window
196          * @return offsets as x, y
197          */
198         public int[] getDisplayOffsets(int inWidth, int inHeight)
199         {
200                 int[] result = new int[2];
201                 result[0] = getDisplayOffset(_xPosition - inWidth/2);
202                 result[1] = getDisplayOffset(_yPosition - inHeight/2);
203                 return result;
204         }
205
206         /**
207          * @return x index of the centre tile
208          */
209         public int getCentreTileX()
210         {
211                 return getTileIndex(_xPosition);
212         }
213
214         /**
215          * @return y index of the centre tile
216          */
217         public int getCentreTileY()
218         {
219                 return getTileIndex(_yPosition);
220         }
221
222         /**
223          * @param inPosition position of point
224          * @return tile index for that point
225          */
226         private int getTileIndex(int inPosition)
227         {
228                 return inPosition / MAP_TILE_SIZE;
229         }
230
231         /**
232          * @param inPosition position of point
233          * @return pixel offset for that point
234          */
235         private int getDisplayOffset(int inPosition)
236         {
237                 return inPosition % MAP_TILE_SIZE;
238                 // I thought that &255 would be slightly faster, but it gives the wrong result
239         }
240
241         /**
242          * Zoom in one level
243          */
244         public void zoomIn()
245         {
246                 if (_zoom < MAX_ZOOM)
247                 {
248                         setZoom(_zoom + 1);
249                         _xPosition *= 2;
250                         _yPosition *= 2;
251                 }
252         }
253
254         /**
255          * Zoom out one level
256          */
257         public void zoomOut()
258         {
259                 if (_zoom >= 3)
260                 {
261                         setZoom(_zoom - 1);
262                         _xPosition /= 2;
263                         _yPosition /= 2;
264                 }
265         }
266
267         /**
268          * @return current zoom level
269          */
270         public int getZoom()
271         {
272                 return _zoom;
273         }
274
275         /**
276          * Pan map by the specified amount
277          * @param inDeltaX amount to pan right
278          * @param inDeltaY amount to pan down
279          */
280         public void pan(int inDeltaX, int inDeltaY)
281         {
282                 // TODO: Check bounds?
283                 _xPosition += inDeltaX;
284                 _yPosition += inDeltaY;
285         }
286
287         /**
288          * @return true if this position has never been set
289          */
290         public boolean isEmpty()
291         {
292                 return _empty;
293         }
294 }