RMFractalTileProjection.m
4.09 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
//
// FractalTileProjection.m
// Images
//
// Created by Joseph Gentle on 27/08/08.
// Copyright 2008 __MyCompanyName__. All rights reserved.
//
#import "RMFractalTileProjection.h"
#import "RMMercatorToScreenProjection.h"
#import "RMProjection.h"
#import <math.h>
@implementation RMFractalTileProjection
@synthesize maxZoom, tileSideLength, bounds;
-(id) initFromProjection:(RMProjection*)projection tileSideLength:(int)aTileSideLength maxZoom: (int) aMaxZoom
{
if (![super init])
return nil;
// We don't care about the rest of the projection... just the bounds is important.
bounds = [projection bounds];
if (bounds.size.width == 0.0f || bounds.size.height == 0.0f)
{
@throw [NSException exceptionWithName:@"RMUnknownBoundsException"
reason:@"RMFractalTileProjection was initialised with a projection with unknown bounds"
userInfo:nil];
}
tileSideLength = aTileSideLength;
maxZoom = aMaxZoom;
scaleFactor = log2(bounds.size.width / tileSideLength);
return self;
}
- (float) normaliseZoom: (float) zoom
{
float normalised_zoom = roundf(zoom);
if (normalised_zoom > maxZoom)
normalised_zoom = maxZoom;
if (normalised_zoom < 0)
normalised_zoom = 0;
return normalised_zoom;
}
- (float) limitFromNormalisedZoom: (float) zoom
{
return exp2f(zoom);
}
- (RMTile) normaliseTile: (RMTile) tile
{
// The mask contains a 1 for every valid x-coordinate bit.
uint32_t mask = 1;
for (int i = 0; i < tile.zoom; i++)
mask <<= 1;
mask -= 1;
tile.x &= mask;
// If the tile's y coordinate is off the screen
if (tile.y & (~mask))
{
return RMTileDummy();
}
return tile;
}
- (RMXYPoint) constrainPointHorizontally: (RMXYPoint) aPoint
{
while (aPoint.x < bounds.origin.x)
aPoint.x += bounds.size.width;
while (aPoint.x > (bounds.origin.x + bounds.size.width))
aPoint.x -= bounds.size.width;
return aPoint;
}
- (RMTilePoint) projectInternal: (RMXYPoint)aPoint normalisedZoom:(float)zoom limit:(float) limit
{
RMTilePoint tile;
RMXYPoint newPoint = [self constrainPointHorizontally:aPoint];
double x = (newPoint.x - bounds.origin.x) / bounds.size.width * limit;
// Unfortunately, y is indexed from the bottom left.. hence we have to translate it.
double y = (double)limit * ((bounds.origin.y - newPoint.y) / bounds.size.height + 1);
tile.tile.x = (uint32_t)x;
tile.tile.y = (uint32_t)y;
tile.tile.zoom = zoom;
tile.offset.x = (float)x - tile.tile.x;
tile.offset.y = (float)y - tile.tile.y;
return tile;
}
- (RMTilePoint) project: (RMXYPoint)aPoint atZoom:(float)zoom
{
float normalised_zoom = [self normaliseZoom:zoom];
float limit = [self limitFromNormalisedZoom:normalised_zoom];
return [self projectInternal:aPoint normalisedZoom:normalised_zoom limit:limit];
}
- (RMTileRect) projectRect: (RMXYRect)aRect atZoom:(float)zoom
{
int normalised_zoom = [self normaliseZoom:zoom];
float limit = [self limitFromNormalisedZoom:normalised_zoom];
RMTileRect tileRect;
// The origin for projectInternal will have to be the top left instead of the bottom left.
RMXYPoint topLeft = aRect.origin;
topLeft.y += aRect.size.height;
tileRect.origin = [self projectInternal:topLeft normalisedZoom:normalised_zoom limit:limit];
tileRect.size.width = aRect.size.width / bounds.size.width * limit;
tileRect.size.height = aRect.size.height / bounds.size.height * limit;
return tileRect;
}
-(RMTilePoint) project: (RMXYPoint)aPoint atScale:(float)scale
{
return [self project:aPoint atZoom:[self calculateZoomFromScale:scale]];
}
-(RMTileRect) projectRect: (RMXYRect)aRect atScale:(float)scale
{
return [self projectRect:aRect atZoom:[self calculateZoomFromScale:scale]];
}
-(RMTileRect) project: (RMMercatorToScreenProjection*)screen;
{
return [self projectRect:[screen XYBounds] atScale:[screen scale]];
}
-(float) calculateZoomFromScale: (float) scale
{ // zoom = log2(bounds.width/tileSideLength) - log2(s)
return scaleFactor - log2(scale);
}
-(float) calculateNormalisedZoomFromScale: (float) scale
{
return [self normaliseZoom:[self calculateZoomFromScale:scale]];
}
-(float) calculateScaleFromZoom: (float) zoom
{
return bounds.size.width / tileSideLength / exp2(zoom);
}
@end