├── LICENSE.md
├── UpgradeLog.htm
├── README.md
├── .gitignore
├── src
└── AwesomeTiles
│ ├── AwesomeTiles.csproj
│ ├── Properties
│ └── AssemblyInfo.cs
│ ├── Tile.cs
│ └── TileRange.cs
└── AwesomeTiles.sln
/LICENSE.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/UpgradeLog.htm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OsmSharp/tiles/HEAD/UpgradeLog.htm
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Awesome Tiles
2 |
3 | Awesome tiles has two classes:
4 |
5 | - Tile: Represents one tile with a zoom level and x-y coordinate.
6 | - TileRange: Represents a 'rectangular' collection of tiles.
7 |
8 | #### Usage:
9 |
10 | Just include the classes in your code if you need them.
11 |
12 | #### Example:
13 |
14 | ```csharp
15 | // create a tile at a given location.
16 | var tile = Tile.CreateAroundLocation(lat, lon, zoom);
17 |
18 | // get info about tile.
19 | var tileTop = tile.Top;
20 |
21 | // and many more awesome stuff, that's why it's called awesome tiles and not just tiles!
22 |
23 | ```
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | [Oo]bj
2 | [Bb]in
3 | *.user
4 | *.suo
5 | *.[Cc]ache
6 | *.bak
7 | *.ncb
8 | *.log
9 | *.DS_Store
10 | [Tt]humbs.db
11 | _ReSharper.*
12 | *.resharper
13 | Ankh.NoLoad
14 | Core/Output*/
15 | UI/Output*/
16 | UI/WinForms/Output*/
17 | Data/Oracle/Output*/
18 | Data/PostgreSQL/Output*/
19 | Data/Redis/Output*/
20 | Data/SQLServer/Output*/
21 | Data/SQLite/Output*/
22 | Core/OsmSharp.UnitTests/test-results*/
23 | *.userprefs
24 | Output*/
25 | OutputAndroid*/
26 | OutputWindowsPhone*/
27 | *.psess
28 | test-results*/
29 | *.vsp
30 | .DotSettings
31 | *.vspx
32 | *.patch
33 | TestResults*/
34 |
35 | packages/*
36 | !packages/repositories.config
37 | .vs/*
38 |
39 | *.routerdb
40 | *.osm.pbf
41 |
--------------------------------------------------------------------------------
/src/AwesomeTiles/AwesomeTiles.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard1.6
5 | AwesomeTiles
6 | AwesomeTiles
7 | 1.6.0
8 | $(PackageTargetFallback);dnxcore50
9 | false
10 | false
11 | false
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/AwesomeTiles/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyConfiguration("")]
9 | [assembly: AssemblyCompany("")]
10 | [assembly: AssemblyProduct("AwesomeTiles")]
11 | [assembly: AssemblyTrademark("")]
12 |
13 | // Setting ComVisible to false makes the types in this assembly not visible
14 | // to COM components. If you need to access a type in this assembly from
15 | // COM, set the ComVisible attribute to true on that type.
16 | [assembly: ComVisible(false)]
17 |
18 | // The following GUID is for the ID of the typelib if this project is exposed to COM
19 | [assembly: Guid("fe7497c7-df68-46f4-a7de-e4ffb9e9731c")]
20 |
--------------------------------------------------------------------------------
/AwesomeTiles.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.26730.15
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{4373FD06-D391-4A50-A34B-C044270F2A05}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{3FABD819-BCB6-41D5-A4C9-DA7E8C2A38DF}"
9 | EndProject
10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AwesomeTiles", "src\AwesomeTiles\AwesomeTiles.csproj", "{FE7497C7-DF68-46F4-A7DE-E4FFB9E9731C}"
11 | EndProject
12 | Global
13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
14 | Debug|Any CPU = Debug|Any CPU
15 | Release|Any CPU = Release|Any CPU
16 | EndGlobalSection
17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
18 | {FE7497C7-DF68-46F4-A7DE-E4FFB9E9731C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
19 | {FE7497C7-DF68-46F4-A7DE-E4FFB9E9731C}.Debug|Any CPU.Build.0 = Debug|Any CPU
20 | {FE7497C7-DF68-46F4-A7DE-E4FFB9E9731C}.Release|Any CPU.ActiveCfg = Release|Any CPU
21 | {FE7497C7-DF68-46F4-A7DE-E4FFB9E9731C}.Release|Any CPU.Build.0 = Release|Any CPU
22 | EndGlobalSection
23 | GlobalSection(SolutionProperties) = preSolution
24 | HideSolutionNode = FALSE
25 | EndGlobalSection
26 | GlobalSection(NestedProjects) = preSolution
27 | {FE7497C7-DF68-46F4-A7DE-E4FFB9E9731C} = {4373FD06-D391-4A50-A34B-C044270F2A05}
28 | EndGlobalSection
29 | GlobalSection(ExtensibilityGlobals) = postSolution
30 | SolutionGuid = {6BA36BCC-F8B3-4E0C-925C-BCB1FCD069C9}
31 | EndGlobalSection
32 | EndGlobal
33 |
--------------------------------------------------------------------------------
/src/AwesomeTiles/Tile.cs:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT)
2 |
3 | // Copyright (c) 2016 Ben Abelshausen
4 |
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 |
12 | // The above copyright notice and this permission notice shall be included in
13 | // all copies or substantial portions of the Software.
14 |
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | // THE SOFTWARE.
22 |
23 | using System;
24 |
25 | namespace AwesomeTiles
26 | {
27 | ///
28 | /// Represents a tile.
29 | ///
30 | public class Tile
31 | {
32 | private readonly ulong _id;
33 |
34 | ///
35 | /// Creates a new tile from a given id.
36 | ///
37 | ///
38 | public Tile(ulong id)
39 | {
40 | _id = id;
41 |
42 | var tile = Tile.CalculateTile(id);
43 | this.X = tile.X;
44 | this.Y = tile.Y;
45 | this.Zoom = tile.Zoom;
46 | this.CalculateBounds();
47 | }
48 |
49 | ///
50 | /// Creates a new tile.
51 | ///
52 | public Tile(int x, int y, int zoom)
53 | {
54 | this.X = x;
55 | this.Y = y;
56 | this.Zoom = zoom;
57 |
58 | _id = CalculateTileId(zoom, x, y);
59 | this.CalculateBounds();
60 | }
61 |
62 | private void CalculateBounds()
63 | {
64 | var n = Math.PI - ((2.0 * Math.PI * this.Y) / Math.Pow(2.0, this.Zoom));
65 | this.Left = (float)((this.X / Math.Pow(2.0, this.Zoom) * 360.0) - 180.0);
66 | this.Top = (float)(180.0 / Math.PI * Math.Atan(Math.Sinh(n)));
67 |
68 | n = Math.PI - ((2.0 * Math.PI * (this.Y + 1)) / Math.Pow(2.0, this.Zoom));
69 | this.Right = (float)(((this.X + 1) / Math.Pow(2.0, this.Zoom) * 360.0) - 180.0);
70 | this.Bottom = (float)(180.0 / Math.PI * Math.Atan(Math.Sinh(n)));
71 |
72 | this.CenterLat = (float)((this.Top + this.Bottom) / 2.0);
73 | this.CenterLon = (float)((this.Left + this.Right) / 2.0);
74 | }
75 |
76 | ///
77 | /// The X position of the tile.
78 | ///
79 | public int X { get; private set; }
80 |
81 | ///
82 | /// The Y position of the tile.
83 | ///
84 | public int Y { get; private set; }
85 |
86 | ///
87 | /// The zoom level for this tile.
88 | ///
89 | public int Zoom { get; private set; }
90 |
91 | ///
92 | /// Gets the top.
93 | ///
94 | public float Top { get; private set; }
95 |
96 | ///
97 | /// Get the bottom.
98 | ///
99 | public float Bottom { get; private set; }
100 |
101 | ///
102 | /// Get the left.
103 | ///
104 | public float Left { get; private set; }
105 |
106 | ///
107 | /// Gets the right.
108 | ///
109 | public float Right { get; private set; }
110 |
111 | ///
112 | /// Gets the center lat.
113 | ///
114 | public float CenterLat { get; private set; }
115 |
116 | ///
117 | /// Gets the center lon.
118 | ///
119 | public float CenterLon { get; private set; }
120 |
121 | ///
122 | /// Gets the parent tile.
123 | ///
124 | public Tile Parent => new Tile(this.X / 2, this.Y / 2, this.Zoom - 1);
125 |
126 | ///
127 | /// Returns a hashcode for this tile position.
128 | ///
129 | ///
130 | public override int GetHashCode()
131 | {
132 | return this.X.GetHashCode() ^
133 | this.Y.GetHashCode() ^
134 | this.Zoom.GetHashCode();
135 | }
136 |
137 | ///
138 | /// Returns true if the given object represents the same tile.
139 | ///
140 | ///
141 | ///
142 | public override bool Equals(object obj)
143 | {
144 | var other = (obj as Tile);
145 | if (other != null)
146 | {
147 | return other.X == this.X &&
148 | other.Y == this.Y &&
149 | other.Zoom == this.Zoom;
150 | }
151 | return false;
152 | }
153 |
154 | ///
155 | /// Returns a description for this tile.
156 | ///
157 | ///
158 | public override string ToString()
159 | {
160 | return string.Format("{0}x-{1}y@{2}z", this.X, this.Y, this.Zoom);
161 | }
162 |
163 | ///
164 | /// Calculates the tile id of the tile at position (0, 0) for the given zoom.
165 | ///
166 | ///
167 | ///
168 | private static ulong CalculateTileId(int zoom)
169 | {
170 | switch (zoom)
171 | {
172 | case 0: // zoom level 0: {0}.
173 | return 0;
174 | case 1:
175 | return 1;
176 | case 2:
177 | return 5;
178 | case 3:
179 | return 21;
180 | case 4:
181 | return 85;
182 | case 5:
183 | return 341;
184 | case 6:
185 | return 1365;
186 | case 7:
187 | return 5461;
188 | case 8:
189 | return 21845;
190 | case 9:
191 | return 87381;
192 | case 10:
193 | return 349525;
194 | case 11:
195 | return 1398101;
196 | case 12:
197 | return 5592405;
198 | case 13:
199 | return 22369621;
200 | case 14:
201 | return 89478485;
202 | case 15:
203 | return 357913941;
204 | case 16:
205 | return 1431655765;
206 | case 17:
207 | return 5726623061;
208 | case 18:
209 | return 22906492245;
210 | }
211 |
212 | var size = (ulong)Math.Pow(2, 2 * (zoom - 1));
213 | var tileId = Tile.CalculateTileId(zoom - 1) + size;
214 | return tileId;
215 | }
216 |
217 | ///
218 | /// Calculates the tile id of the tile at position (x, y) for the given zoom.
219 | ///
220 | ///
221 | ///
222 | ///
223 | ///
224 | private static ulong CalculateTileId(int zoom, int x, int y)
225 | {
226 | var id = Tile.CalculateTileId(zoom);
227 | var width = (long)System.Math.Pow(2, zoom);
228 | return id + (ulong)x + (ulong)(y * width);
229 | }
230 |
231 | ///
232 | /// Calculate the tile given the id.
233 | ///
234 | ///
235 | ///
236 | private static Tile CalculateTile(ulong id)
237 | {
238 | // find out the zoom level first.
239 | var zoom = 0;
240 | if (id > 0)
241 | { // only if the id is at least at zoom level 1.
242 | while (id >= Tile.CalculateTileId(zoom))
243 | {
244 | // move to the next zoom level and keep searching.
245 | zoom++;
246 | }
247 | zoom--;
248 | }
249 |
250 | // calculate the x-y.
251 | var local = id - Tile.CalculateTileId(zoom);
252 | var width = (ulong)System.Math.Pow(2, zoom);
253 | var x = (int)(local % width);
254 | var y = (int)(local / width);
255 |
256 | return new Tile(x, y, zoom);
257 | }
258 |
259 | ///
260 | /// Returns the id of this tile.
261 | ///
262 | public ulong Id => _id;
263 |
264 | ///
265 | /// Returns true if this tile is valid.
266 | ///
267 | public bool IsValid
268 | {
269 | get
270 | {
271 | if (this.X >= 0 &&
272 | this.Y >= 0 &&
273 | this.Zoom >= 0)
274 | { // some are negative.
275 | var size = System.Math.Pow(2, this.Zoom);
276 | return this.X < size && this.Y < size;
277 | }
278 | return false;
279 | }
280 | }
281 |
282 | ///
283 | /// Returns the tile at the given location at the given zoom.
284 | ///
285 | public static Tile CreateAroundLocation(double lat, double lon, int zoom)
286 | {
287 | int x, y;
288 | if (!Tile.CreateAroundLocation(lat, lon, zoom, out x, out y))
289 | {
290 | return null;
291 | }
292 | return new Tile(x, y, zoom);
293 | }
294 |
295 | ///
296 | /// Returns the tile at the given location at the given zoom.
297 | ///
298 | public static ulong CreateAroundLocationId(double lat, double lon, int zoom)
299 | {
300 | int x, y;
301 | if (!Tile.CreateAroundLocation(lat, lon, zoom, out x, out y))
302 | {
303 | return ulong.MaxValue;
304 | }
305 | return Tile.CalculateTileId(zoom, x, y);
306 | }
307 |
308 | ///
309 | /// A fast method of calculating x-y without creating a tile object.
310 | ///
311 | public static bool CreateAroundLocation(double lat, double lon, int zoom, out int x, out int y)
312 | {
313 | if (lon == 180)
314 | {
315 | lon = lon - 0.000001;
316 | }
317 | if (lat > 85.0511 || lat < -85.0511)
318 | {
319 | x = 0;
320 | y = 0;
321 | return false;
322 | }
323 |
324 | var n = (int)System.Math.Floor(System.Math.Pow(2, zoom));
325 |
326 | x = (int)((lon + 180.0) / 360.0 * (1 << zoom));
327 | var latRad = lat * Math.PI / 180.0;
328 | y = (int)((1.0 - Math.Log(Math.Tan(latRad) +
329 | 1.0 / Math.Cos(latRad)) / Math.PI) / 2.0 * (1 << zoom));
330 | return true;
331 | }
332 |
333 | ///
334 | /// Gets the tile id the given lat/lon belongs to one zoom level lower.
335 | ///
336 | public ulong GetSubTileIdFor(double lat, double lon)
337 | {
338 | var factor = 2;
339 | var zoom = this.Zoom + 1;
340 | int x = 0, y = 0;
341 | if (lat >= this.CenterLat && lon < this.CenterLon)
342 | {
343 | x = this.X * factor;
344 | y = this.Y * factor;
345 | }
346 | else if(lat >= this.CenterLat && lon >= this.CenterLon)
347 | {
348 | x = this.X * factor + factor - 1;
349 | y = this.Y * factor;
350 | }
351 | else if (lat < this.CenterLat && lon < this.CenterLon)
352 | {
353 | x = this.X * factor;
354 | y = this.Y * factor + factor - 1;
355 | }
356 | else if (lat < this.CenterLat && lon >= this.CenterLon)
357 | {
358 | x = this.X * factor + factor - 1;
359 | y = this.Y * factor + factor - 1;
360 | }
361 |
362 | return Tile.CalculateTileId(zoom, x, y);
363 | }
364 |
365 | ///
366 | /// Returns the subtiles of this tile at the given zoom.
367 | ///
368 | public TileRange GetSubTiles(int zoom)
369 | {
370 | if (this.Zoom > zoom) { throw new ArgumentOutOfRangeException("zoom", "Subtiles can only be calculated for higher zooms."); }
371 |
372 | if (this.Zoom == zoom)
373 | { // just return a range of one tile.
374 | return new TileRange(this.X, this.Y, this.X, this.Y, this.Zoom);
375 | }
376 |
377 | var factor = 1 << (zoom - this.Zoom);
378 |
379 | return new TileRange(
380 | this.X * factor,
381 | this.Y * factor,
382 | this.X * factor + factor - 1,
383 | this.Y * factor + factor - 1,
384 | zoom);
385 | }
386 |
387 | ///
388 | /// Inverts the X-coordinate.
389 | ///
390 | ///
391 | public Tile InvertX()
392 | {
393 | var n = (int)System.Math.Floor(System.Math.Pow(2, this.Zoom));
394 |
395 | return new Tile(n - this.X - 1, this.Y, this.Zoom);
396 | }
397 |
398 | ///
399 | /// Inverts the Y-coordinate.
400 | ///
401 | ///
402 | public Tile InvertY()
403 | {
404 | var n = (int)System.Math.Floor(System.Math.Pow(2, this.Zoom));
405 |
406 | return new Tile(this.X, n - this.Y - 1, this.Zoom);
407 | }
408 | }
409 | }
410 |
--------------------------------------------------------------------------------
/src/AwesomeTiles/TileRange.cs:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT)
2 |
3 | // Copyright (c) 2016 Ben Abelshausen
4 |
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 |
12 | // The above copyright notice and this permission notice shall be included in
13 | // all copies or substantial portions of the Software.
14 |
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | // THE SOFTWARE.
22 |
23 | using System.Collections.Generic;
24 |
25 | namespace AwesomeTiles
26 | {
27 | ///
28 | /// Represents a range of tiles.
29 | ///
30 | public class TileRange : IEnumerable
31 | {
32 | ///
33 | /// Creates a new tile range.
34 | ///
35 | public TileRange(int xMin, int yMin, int xMax, int yMax, int zoom)
36 | {
37 | this.XMin = xMin;
38 | this.XMax = xMax;
39 | this.YMin = yMin;
40 | this.YMax = yMax;
41 |
42 | this.Zoom = zoom;
43 | }
44 |
45 | ///
46 | /// The minimum X of this range.
47 | ///
48 | public int XMin { get; private set; }
49 |
50 | ///
51 | /// The minimum Y of this range.
52 | ///
53 | public int YMin { get; private set; }
54 |
55 | ///
56 | /// The maximum X of this range.
57 | ///
58 | public int XMax { get; private set; }
59 |
60 | ///
61 | /// The maximum Y of this range.
62 | ///
63 | public int YMax { get; private set; }
64 |
65 | ///
66 | /// The zoom of this range.
67 | ///
68 | public int Zoom { get; private set; }
69 |
70 | ///
71 | /// Returns the number of tiles in this range.
72 | ///
73 | public int Count
74 | {
75 | get
76 | {
77 | return System.Math.Abs(this.XMax - this.XMin + 1) *
78 | System.Math.Abs(this.YMax - this.YMin + 1);
79 | }
80 | }
81 |
82 | ///
83 | /// Returns true if the given tile exists in this range.
84 | ///
85 | ///
86 | ///
87 | public bool Contains(Tile tile)
88 | {
89 | return this.XMax >= tile.X && this.XMin <= tile.X &&
90 | this.YMax >= tile.Y && this.YMin <= tile.Y;
91 | }
92 |
93 | #region Functions
94 |
95 | ///
96 | /// Returns true if the given tile lies at the border of this range.
97 | ///
98 | ///
99 | ///
100 | ///
101 | ///
102 | public bool IsBorderAt(int x, int y, int zoom)
103 | {
104 | return ((x == this.XMin) || (x == this.XMax)
105 | || (y == this.YMin) || (y == this.YMin)) &&
106 | this.Zoom == zoom;
107 | }
108 |
109 | ///
110 | /// Returns true if the given tile lies at the border of this range.
111 | ///
112 | ///
113 | ///
114 | public bool IsBorderAt(Tile tile)
115 | {
116 | return IsBorderAt(tile.X, tile.Y, tile.Zoom);
117 | }
118 |
119 | #endregion
120 |
121 | ///
122 | /// Returns en enumerator of tiles.
123 | ///
124 | ///
125 | public IEnumerator GetEnumerator()
126 | {
127 | return new TileRangeEnumerator(this);
128 | }
129 |
130 | ///
131 | /// Returns en enumerator of tiles.
132 | ///
133 | ///
134 | System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
135 | {
136 | return this.GetEnumerator();
137 | }
138 |
139 | ///
140 | /// Simple enumerator.
141 | ///
142 | private class TileRangeEnumerator : IEnumerator
143 | {
144 | private TileRange _range;
145 |
146 | private Tile _current;
147 |
148 | public TileRangeEnumerator(TileRange range)
149 | {
150 | _range = range;
151 | }
152 |
153 | public Tile Current
154 | {
155 | get
156 | {
157 | return _current;
158 | }
159 | }
160 |
161 | public void Dispose()
162 | {
163 | _range = null;
164 | }
165 |
166 | object System.Collections.IEnumerator.Current
167 | {
168 | get { return this.Current; }
169 | }
170 |
171 | public bool MoveNext()
172 | {
173 | if (_current == null)
174 | {
175 | _current = new Tile(_range.XMin, _range.YMin, _range.Zoom);
176 | return true;
177 | }
178 |
179 | int x = _current.X;
180 | int y = _current.Y;
181 |
182 | if (x == _range.XMax)
183 | {
184 | if (y == _range.YMax)
185 | {
186 | return false;
187 | }
188 | y++;
189 | x = _range.XMin;
190 | }
191 | else
192 | {
193 | x++;
194 | }
195 | _current = new Tile(x, y, _current.Zoom);
196 | return true;
197 | }
198 |
199 | public void Reset()
200 | {
201 | _current = null;
202 | }
203 | }
204 |
205 | ///
206 | /// Defines an enumerator that start at the center of the range and moves out in a spiral.
207 | ///
208 | public class TileRangeCenteredEnumerator : IEnumerator
209 | {
210 | ///
211 | /// Holds the range to enumerate.
212 | ///
213 | private TileRange _range;
214 |
215 | ///
216 | /// Holds the current tile.
217 | ///
218 | private Tile _current;
219 |
220 | ///
221 | /// Holds the enumerated tiles.
222 | ///
223 | private HashSet _enumeratedTiles = new HashSet();
224 |
225 | ///
226 | /// Creates the enumerator.
227 | ///
228 | ///
229 | public TileRangeCenteredEnumerator(TileRange range)
230 | {
231 | _range = range;
232 | }
233 |
234 | ///
235 | /// Returns the current tile.
236 | ///
237 | public Tile Current
238 | {
239 | get
240 | {
241 | return _current;
242 | }
243 | }
244 |
245 | ///
246 | /// Disposes of all resources associated with this object.
247 | ///
248 | public void Dispose()
249 | {
250 | _range = null;
251 | }
252 |
253 | ///
254 | /// Returns the current tile.
255 | ///
256 | object System.Collections.IEnumerator.Current
257 | {
258 | get { return this.Current; }
259 | }
260 |
261 | ///
262 | /// Holds the current desired direction.
263 | ///
264 | private DirectionEnum _direction = DirectionEnum.Up;
265 |
266 | private enum DirectionEnum
267 | {
268 | Up = 0,
269 | Right = 1,
270 | Down = 2,
271 | Left = 3
272 | }
273 |
274 | ///
275 | /// Move to the next tile.
276 | ///
277 | ///
278 | public bool MoveNext()
279 | {
280 | if (_current == null)
281 | { // start with the center tile.
282 | int centerX = (int)System.Math.Floor((_range.XMax + _range.XMin) / 2.0);
283 | int centerY = (int)System.Math.Ceiling((_range.YMax + _range.YMin) / 2.0);
284 | _current = new Tile(centerX, centerY, _range.Zoom);
285 | _enumeratedTiles.Add(_current);
286 | return true;
287 | }
288 |
289 | // check if there are more tiles to be enumerated.
290 | if (_range.Count <= _enumeratedTiles.Count)
291 | { // no more tiles left.
292 | return false;
293 | }
294 |
295 | // try to move in the desired direction.
296 | Tile next = null;
297 | while (next == null)
298 | { // try until a valid tile is found.
299 | switch (_direction)
300 | {
301 | case DirectionEnum.Up: // up
302 | next = new Tile(_current.X, _current.Y - 1, _range.Zoom);
303 | if (_enumeratedTiles.Contains(next))
304 | { // moving up does not work, try to move left.
305 | _direction = DirectionEnum.Left;
306 | next = null;
307 | }
308 | else
309 | { // moved up, try right.
310 | _direction = DirectionEnum.Right;
311 | }
312 | break;
313 | case DirectionEnum.Left: // left
314 | next = new Tile(_current.X - 1, _current.Y, _range.Zoom);
315 | if (_enumeratedTiles.Contains(next))
316 | { // moving left does not work, try to move down.
317 | _direction = DirectionEnum.Down;
318 | next = null;
319 | }
320 | else
321 | { // moved left, try up.
322 | _direction = DirectionEnum.Up;
323 | }
324 | break;
325 | case DirectionEnum.Down: // down
326 | next = new Tile(_current.X, _current.Y + 1, _range.Zoom);
327 | if (_enumeratedTiles.Contains(next))
328 | { // moving down does not work, try to move right.
329 | _direction = DirectionEnum.Right;
330 | next = null;
331 | }
332 | else
333 | { // moved down, try left.
334 | _direction = DirectionEnum.Left;
335 | }
336 | break;
337 | case DirectionEnum.Right: // right
338 | next = new Tile(_current.X + 1, _current.Y, _range.Zoom);
339 | if (_enumeratedTiles.Contains(next))
340 | { // moving right does not work, try to move up.
341 | _direction = DirectionEnum.Up;
342 | next = null;
343 | }
344 | else
345 | { // moved right, try down.
346 | _direction = DirectionEnum.Down;
347 | }
348 | break;
349 | }
350 |
351 | // test if the next is in range.
352 | if (next != null && !_range.Contains(next))
353 | { // not in range, do not enumerate but move to next.
354 | _current = next; // pretend the next has been enumerated.
355 | next = null;
356 | }
357 | }
358 |
359 | // ok, next was found.
360 | _current = next;
361 | _enumeratedTiles.Add(_current);
362 | return true;
363 | }
364 |
365 | ///
366 | /// Resets this enumerator.
367 | ///
368 | public void Reset()
369 | {
370 | _current = null;
371 | _enumeratedTiles.Clear();
372 | }
373 | }
374 |
375 | ///
376 | /// Returns an enumerable that enumerates tiles with the center first.
377 | ///
378 | ///
379 | public IEnumerable EnumerateInCenterFirst()
380 | {
381 | return new TileRangeCenterFirst(this);
382 | }
383 |
384 | ///
385 | /// Tile range center first enumerable.
386 | ///
387 | private class TileRangeCenterFirst : IEnumerable
388 | {
389 | ///
390 | /// Holds the tile range to be enumerated.
391 | ///
392 | private TileRange _tileRange;
393 |
394 | ///
395 | /// Creates a new range center first enumerable.
396 | ///
397 | ///
398 | public TileRangeCenterFirst(TileRange tileRange)
399 | {
400 | _tileRange = tileRange;
401 | }
402 |
403 | ///
404 | /// Returns the enumerator.
405 | ///
406 | ///
407 | public IEnumerator GetEnumerator()
408 | {
409 | return new TileRangeCenteredEnumerator(_tileRange);
410 | }
411 |
412 | ///
413 | /// Returns the enumerator.
414 | ///
415 | ///
416 | System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
417 | {
418 | return new TileRangeCenteredEnumerator(_tileRange);
419 | }
420 | }
421 | }
422 | }
423 |
--------------------------------------------------------------------------------