| 1 | // License: GPL. Copyright 2009 by Dave Hansen, others |
|---|
| 2 | package org.openstreetmap.josm.data.coor; |
|---|
| 3 | |
|---|
| 4 | public class QuadTiling |
|---|
| 5 | { |
|---|
| 6 | public static final int NR_LEVELS = 24; |
|---|
| 7 | public static final double WORLD_PARTS = (1 << NR_LEVELS); |
|---|
| 8 | |
|---|
| 9 | public static final int TILES_PER_LEVEL_SHIFT = 2; // Has to be 2. Other parts of QuadBuckets code rely on it |
|---|
| 10 | public static final int TILES_PER_LEVEL = 1<<TILES_PER_LEVEL_SHIFT; |
|---|
| 11 | static public final int X_PARTS = 360; |
|---|
| 12 | static public final int X_BIAS = -180; |
|---|
| 13 | |
|---|
| 14 | static public final int Y_PARTS = 180; |
|---|
| 15 | static public final int Y_BIAS = -90; |
|---|
| 16 | |
|---|
| 17 | public static LatLon tile2LatLon(long quad) |
|---|
| 18 | { |
|---|
| 19 | // The world is divided up into X_PARTS,Y_PARTS. |
|---|
| 20 | // The question is how far we move for each bit |
|---|
| 21 | // being set. In the case of the top level, we |
|---|
| 22 | // move half of the world. |
|---|
| 23 | double x_unit = X_PARTS/2; |
|---|
| 24 | double y_unit = Y_PARTS/2; |
|---|
| 25 | long shift = (NR_LEVELS*2)-2; |
|---|
| 26 | |
|---|
| 27 | double x = 0; |
|---|
| 28 | double y = 0; |
|---|
| 29 | for (int i = 0; i < NR_LEVELS; i++) { |
|---|
| 30 | long bits = (quad >> shift) & 0x3; |
|---|
| 31 | // remember x is the MSB |
|---|
| 32 | if ((bits & 0x2) != 0) { |
|---|
| 33 | x += x_unit; |
|---|
| 34 | } |
|---|
| 35 | if ((bits & 0x1) != 0) { |
|---|
| 36 | y += y_unit; |
|---|
| 37 | } |
|---|
| 38 | x_unit /= 2; |
|---|
| 39 | y_unit /= 2; |
|---|
| 40 | shift -= 2; |
|---|
| 41 | } |
|---|
| 42 | x += X_BIAS; |
|---|
| 43 | y += Y_BIAS; |
|---|
| 44 | return new LatLon(y, x); |
|---|
| 45 | } |
|---|
| 46 | static long xy2tile(long x, long y) |
|---|
| 47 | { |
|---|
| 48 | long tile = 0; |
|---|
| 49 | int i; |
|---|
| 50 | for (i = NR_LEVELS-1; i >= 0; i--) |
|---|
| 51 | { |
|---|
| 52 | long xbit = ((x >> i) & 1); |
|---|
| 53 | long ybit = ((y >> i) & 1); |
|---|
| 54 | tile <<= 2; |
|---|
| 55 | // Note that x is the MSB |
|---|
| 56 | tile |= (xbit<<1) | ybit; |
|---|
| 57 | } |
|---|
| 58 | return tile; |
|---|
| 59 | } |
|---|
| 60 | static long coorToTile(LatLon coor) |
|---|
| 61 | { |
|---|
| 62 | return quadTile(coor); |
|---|
| 63 | } |
|---|
| 64 | static long lon2x(double lon) |
|---|
| 65 | { |
|---|
| 66 | //return Math.round((lon + 180.0) * QuadBuckets.WORLD_PARTS / 360.0)-1; |
|---|
| 67 | long ret = (long)((lon + 180.0) * WORLD_PARTS / 360.0); |
|---|
| 68 | if (ret == WORLD_PARTS) { |
|---|
| 69 | ret--; |
|---|
| 70 | } |
|---|
| 71 | return ret; |
|---|
| 72 | } |
|---|
| 73 | static long lat2y(double lat) |
|---|
| 74 | { |
|---|
| 75 | //return Math.round((lat + 90.0) * QuadBuckets.WORLD_PARTS / 180.0)-1; |
|---|
| 76 | long ret = (long)((lat + 90.0) * WORLD_PARTS / 180.0); |
|---|
| 77 | if (ret == WORLD_PARTS) { |
|---|
| 78 | ret--; |
|---|
| 79 | } |
|---|
| 80 | return ret; |
|---|
| 81 | } |
|---|
| 82 | static public long quadTile(LatLon coor) |
|---|
| 83 | { |
|---|
| 84 | return xy2tile(lon2x(coor.lon()), |
|---|
| 85 | lat2y(coor.lat())); |
|---|
| 86 | } |
|---|
| 87 | static public int index(int level, long quad) |
|---|
| 88 | { |
|---|
| 89 | long mask = 0x00000003; |
|---|
| 90 | int total_shift = TILES_PER_LEVEL_SHIFT*(NR_LEVELS-level-1); |
|---|
| 91 | return (int)(mask & (quad >> total_shift)); |
|---|
| 92 | } |
|---|
| 93 | static public int index(LatLon coor, int level) { |
|---|
| 94 | // The nodes that don't return coordinates will all get |
|---|
| 95 | // stuck in a single tile. Hopefully there are not too |
|---|
| 96 | // many of them |
|---|
| 97 | if (coor == null) |
|---|
| 98 | return 0; |
|---|
| 99 | |
|---|
| 100 | long x = lon2x(coor.lon()); |
|---|
| 101 | long y = lat2y(coor.lat()); |
|---|
| 102 | int shift = NR_LEVELS-level-1; |
|---|
| 103 | return (int)((x >> shift & 1) * 2 + (y >> shift & 1)); |
|---|
| 104 | } |
|---|
| 105 | } |
|---|