/MapView/Map/RMProjection.m
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