PageRenderTime 4ms CodeModel.GetById 43ms app.highlight 25ms RepoModel.GetById 1ms app.codeStats 1ms

/MapView/Map/RMProjection.m

http://github.com/route-me/route-me
Objective C | 202 lines | 135 code | 37 blank | 30 comment | 21 complexity | d16b609ac19155d3fe70132ea85d927c MD5 | raw file
  1//
  2//  RMProjection.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#import "RMGlobalConstants.h"
 28#import "proj_api.h"
 29#import "RMProjection.h"
 30
 31
 32NS_INLINE RMLatLong RMPixelPointAsLatLong(RMProjectedPoint xypoint) {
 33    union _ {RMProjectedPoint xy; RMLatLong latLong;};
 34    return ((union _ *)&xypoint)->latLong;
 35}
 36
 37
 38@implementation RMProjection
 39
 40@synthesize internalProjection;
 41@synthesize planetBounds;
 42@synthesize projectionWrapsHorizontally;
 43
 44- (id) initWithString: (NSString*)params InBounds: (RMProjectedRect) projBounds
 45{
 46	if (![super init])
 47		return nil;
 48	
 49	internalProjection = pj_init_plus([params UTF8String]);
 50	if (internalProjection == NULL)
 51	{
 52		RMLog(@"Unhandled error creating projection. String is %@", params);
 53		[self release];
 54		return nil;
 55	}
 56	
 57	planetBounds = projBounds;
 58
 59	projectionWrapsHorizontally = YES;
 60	
 61	return self;
 62}
 63
 64- (id) initWithString: (NSString*)params
 65{
 66	RMProjectedRect theBounds;
 67	theBounds = RMMakeProjectedRect(0,0,0,0);
 68	return [self initWithString:params InBounds:theBounds];
 69}
 70
 71-(id)init
 72{
 73	return [self initWithString:@"+proj=latlong +ellps=WGS84"];
 74}
 75
 76-(void)dealloc
 77{
 78	if (internalProjection)
 79		pj_free(internalProjection);
 80	
 81	[super dealloc];
 82}
 83
 84- (RMProjectedPoint) wrapPointHorizontally: (RMProjectedPoint) aPoint
 85{
 86	if (!projectionWrapsHorizontally
 87		|| planetBounds.size.width == 0.0f || planetBounds.size.height == 0.0f)
 88		return aPoint;
 89	
 90	while (aPoint.easting < planetBounds.origin.easting)
 91		aPoint.easting += planetBounds.size.width;
 92	while (aPoint.easting > (planetBounds.origin.easting + planetBounds.size.width))
 93		aPoint.easting -= planetBounds.size.width;
 94	
 95	return aPoint;
 96}
 97
 98-(RMProjectedPoint) constrainPointToBounds: (RMProjectedPoint) aPoint
 99{
100	if (planetBounds.size.width == 0.0f || planetBounds.size.height == 0.0f)
101		return aPoint;
102	
103	[self wrapPointHorizontally:aPoint];
104	
105	if (aPoint.northing < planetBounds.origin.northing)
106		aPoint.northing = planetBounds.origin.northing;
107	else if (aPoint.northing > (planetBounds.origin.northing + planetBounds.size.height))
108		aPoint.northing = planetBounds.origin.northing + planetBounds.size.height;
109	
110	return aPoint;
111}
112
113- (RMProjectedPoint)latLongToPoint:(RMLatLong)aLatLong
114{
115	projUV uv = {
116		aLatLong.longitude * DEG_TO_RAD,
117		aLatLong.latitude * DEG_TO_RAD
118	};
119	
120	projUV result = pj_fwd(uv, internalProjection);
121	
122	RMProjectedPoint result_point = {
123		result.u,
124		result.v,
125	};
126	
127	return result_point;
128}
129
130- (RMLatLong)pointToLatLong:(RMProjectedPoint)aPoint
131{
132	projUV uv = {
133		aPoint.easting,
134		aPoint.northing,
135	};
136	
137	projUV result = pj_inv(uv, internalProjection);
138	
139	RMLatLong result_coordinate = {
140		result.v * RAD_TO_DEG,
141		result.u * RAD_TO_DEG,
142	};
143	
144	return result_coordinate;
145}
146
147static RMProjection* _google = nil;
148static RMProjection* _latlong = nil;
149static RMProjection* _osgb = nil;
150
151+ (RMProjection*)googleProjection
152{
153	if (_google)
154	{
155		return _google;
156	}
157	else
158	{
159/// \bug magic numbers embedded in code, this one's probably ok
160		RMProjectedRect theBounds = RMMakeProjectedRect(-20037508.34, -20037508.34, 20037508.34 * 2, 20037508.34 * 2);
161		
162		_google = [[RMProjection alloc] initWithString:@"+title= Google Mercator EPSG:900913\
163				   +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +no_defs"
164											  InBounds: theBounds];
165		return _google;
166	}
167}
168
169+ (RMProjection*)EPSGLatLong
170{
171	if (_latlong)
172	{
173		return _latlong;
174	}
175	else
176	{
177		RMProjectedRect theBounds = RMMakeProjectedRect(-kMaxLong, -kMaxLat, 360, kMaxLong);
178		
179		/// \bug magic numbers embedded in code, this one's probably ok
180		_latlong = [[RMProjection alloc] initWithString:@"+proj=latlong +ellps=WGS84" InBounds: theBounds];
181		return _latlong;
182	}
183}
184
185+(RMProjection*) OSGB
186{
187	if (_osgb)
188	{
189		return _osgb;
190	}
191	else
192	{// OSGB36 and tmerc
193		/// \bug TODO: This should use the new initWithString:InBounds: method... but I don't know what the bounds are!
194		/// \bug magic numbers embedded in code, this one's probably ok
195		_osgb = [[RMProjection alloc] initWithString:@"+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.999601 +x_0=400000 +y_0=-100000 +ellps=airy +datum=OSGB36 +units=m +no_defs"];
196		_osgb.projectionWrapsHorizontally = NO;
197		return _osgb;
198	}
199}
200
201
202@end