PageRenderTime 81ms CodeModel.GetById 19ms app.highlight 59ms RepoModel.GetById 1ms app.codeStats 0ms

/MapView/Map/RMFractalTileProjection.m

http://github.com/route-me/route-me
Objective C | 201 lines | 128 code | 40 blank | 33 comment | 11 complexity | b1f571d9dfe056107c44297ebd5adb0c MD5 | raw file
  1//
  2//  RMFractalTileProjection.m
  3//
  4// Copyright (c) 2008-2009, Route-Me Contributors
  5// All rights reserved.
  6//
  7// Redistribution and use in source and binary forms, with or without
  8// modification, are permitted provided that the following conditions are met:
  9//
 10// * Redistributions of source code must retain the above copyright notice, this
 11//   list of conditions and the following disclaimer.
 12// * Redistributions in binary form must reproduce the above copyright notice,
 13//   this list of conditions and the following disclaimer in the documentation
 14//   and/or other materials provided with the distribution.
 15//
 16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 17// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 18// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 19// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 20// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 21// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 22// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 23// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 24// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 25// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 26// POSSIBILITY OF SUCH DAMAGE.
 27
 28#import "RMFractalTileProjection.h"
 29#import "RMMercatorToScreenProjection.h"
 30#import "RMProjection.h"
 31#import <math.h>
 32
 33@implementation RMFractalTileProjection
 34
 35@synthesize maxZoom, minZoom;
 36@synthesize tileSideLength;
 37@synthesize planetBounds;
 38
 39-(id) initFromProjection:(RMProjection*)projection tileSideLength:(NSUInteger)aTileSideLength maxZoom: (NSUInteger) aMaxZoom minZoom: (NSUInteger) aMinZoom
 40{
 41	if (![super init])
 42		return nil;
 43	
 44	// We don't care about the rest of the projection... just the bounds is important.
 45	planetBounds = [projection planetBounds];
 46	
 47	if (planetBounds.size.width == 0.0f || planetBounds.size.height == 0.0f)
 48	{
 49		/// \bug magic string literals
 50		@throw [NSException exceptionWithName:@"RMUnknownBoundsException"
 51									   reason:@"RMFractalTileProjection was initialised with a projection with unknown bounds"
 52									 userInfo:nil];
 53	}
 54	
 55	tileSideLength = aTileSideLength;
 56	maxZoom = aMaxZoom;
 57	minZoom = aMinZoom;
 58	
 59	scaleFactor = log2(planetBounds.size.width / tileSideLength);
 60	
 61	return self;
 62}
 63
 64- (void) setTileSideLength: (NSUInteger) aTileSideLength
 65{
 66	tileSideLength = aTileSideLength;
 67
 68	scaleFactor = log2(planetBounds.size.width / tileSideLength);
 69}
 70
 71- (void) setMinZoom: (NSUInteger) aMinZoom
 72{
 73	minZoom = aMinZoom;
 74}
 75
 76- (void) setMaxZoom: (NSUInteger) aMaxZoom
 77{
 78	maxZoom = aMaxZoom;
 79}
 80
 81- (float) normaliseZoom: (float) zoom
 82{
 83	float normalised_zoom = roundf(zoom);
 84
 85	if (normalised_zoom > maxZoom)
 86		normalised_zoom = maxZoom;
 87	if (normalised_zoom < minZoom)
 88		normalised_zoom = minZoom;
 89	
 90	return normalised_zoom;
 91}
 92
 93- (float) limitFromNormalisedZoom: (float) zoom
 94{
 95	return exp2f(zoom);
 96}
 97
 98- (RMTile) normaliseTile: (RMTile) tile
 99{
100	// The mask contains a 1 for every valid x-coordinate bit.
101	uint32_t mask = 1;
102	for (int i = 0; i < tile.zoom; i++)
103		mask <<= 1;
104	
105	mask -= 1;
106	
107	tile.x &= mask;
108	
109	// If the tile's y coordinate is off the screen
110	if (tile.y & (~mask))
111	{
112		return RMTileDummy();
113	}
114	
115	return tile;
116}
117
118- (RMProjectedPoint) constrainPointHorizontally: (RMProjectedPoint) aPoint
119{
120	while (aPoint.easting < planetBounds.origin.easting)
121		aPoint.easting += planetBounds.size.width;
122	while (aPoint.easting > (planetBounds.origin.easting + planetBounds.size.width))
123		aPoint.easting -= planetBounds.size.width;
124	
125	return aPoint;
126}
127
128- (RMTilePoint) projectInternal: (RMProjectedPoint)aPoint normalisedZoom:(float)zoom limit:(float) limit
129{
130	RMTilePoint tile;
131	RMProjectedPoint newPoint = [self constrainPointHorizontally:aPoint];
132	
133	double x = (newPoint.easting - planetBounds.origin.easting) / planetBounds.size.width * limit;
134	// Unfortunately, y is indexed from the bottom left.. hence we have to translate it.
135	double y = (double)limit * ((planetBounds.origin.northing - newPoint.northing) / planetBounds.size.height + 1);
136	
137	tile.tile.x = (uint32_t)x;
138	tile.tile.y = (uint32_t)y;
139	tile.tile.zoom = zoom;
140	tile.offset.x = (float)x - tile.tile.x;
141	tile.offset.y = (float)y - tile.tile.y;
142	
143	return tile;
144}
145
146- (RMTilePoint) project: (RMProjectedPoint)aPoint atZoom:(float)zoom
147{
148	float normalised_zoom = [self normaliseZoom:zoom];
149	float limit = [self limitFromNormalisedZoom:normalised_zoom];
150	
151	return [self projectInternal:aPoint normalisedZoom:normalised_zoom limit:limit];
152}
153
154- (RMTileRect) projectRect: (RMProjectedRect)aRect atZoom:(float)zoom
155{
156	/// \bug assignment of float to int, WTF?
157	int normalised_zoom = [self normaliseZoom:zoom];
158	float limit = [self limitFromNormalisedZoom:normalised_zoom];
159
160	RMTileRect tileRect;
161	// The origin for projectInternal will have to be the top left instead of the bottom left.
162	RMProjectedPoint topLeft = aRect.origin;
163	topLeft.northing += aRect.size.height;
164	tileRect.origin = [self projectInternal:topLeft normalisedZoom:normalised_zoom limit:limit];
165
166	tileRect.size.width = aRect.size.width / planetBounds.size.width * limit;
167	tileRect.size.height = aRect.size.height / planetBounds.size.height * limit;
168	
169	return tileRect;
170}
171
172-(RMTilePoint) project: (RMProjectedPoint)aPoint atScale:(float)scale
173{
174	return [self project:aPoint atZoom:[self calculateZoomFromScale:scale]];
175}
176-(RMTileRect) projectRect: (RMProjectedRect)aRect atScale:(float)scale
177{
178	return [self projectRect:aRect atZoom:[self calculateZoomFromScale:scale]];
179}
180
181-(RMTileRect) project: (RMMercatorToScreenProjection*)screen;
182{
183	return [self projectRect:[screen projectedBounds] atScale:[screen metersPerPixel]];
184}
185
186-(float) calculateZoomFromScale: (float) scale
187{	// zoom = log2(bounds.width/tileSideLength) - log2(s)
188	return scaleFactor - log2(scale);
189}
190
191-(float) calculateNormalisedZoomFromScale: (float) scale
192{
193	return [self normaliseZoom:[self calculateZoomFromScale:scale]];
194}
195
196-(float) calculateScaleFromZoom: (float) zoom
197{
198	return planetBounds.size.width / tileSideLength / exp2(zoom);	
199}
200
201@end