iPhone Note #20:Integrating Mapserver/TileCache to RouteMe
Below is a summary of how I was able to implement Mapserver, TileCache and Route-Me iPhone Mapping Framework.
- Assuming you have a working Mapserver/TileCache setup. Take note of the ff parameters: resolution and bbox. Below is my tilecache.cfg:
61 [mapserver_australia_3857] 62 type=MapServerLayer 63 mapfile=/Users/rupert/projects/pelicancorp/DMOB/trunk/map/australia_3857.map 64 layers=all 65 extension=jpg 66 bbox=-20037508.34, -20037508.34, 20037508.34, 20037508.34 68 maxResolution=156543.033928041 70 levels=20 71 srs=EPSG:3857 72 tms_type=google 73 extent_type=loose 74 spherical_mercator=true |
-
Grab the RMGenericMercatorWMSSource from http://groups.google.com/group/route-me-map/browse_thread/thread/b58fa1d20cf15823/e30c42d9c90a8170?lnk=gst&q=Generic#e30c42d9c90a8170
-
By default without any changes, the RMGenericMercatorWMSSource could display Mapserver WMS Tiles. This is possible by passing an NSDictionary *parameters, which contains arrayValues and arrayKeys for creating an http 256×256 image request to the http://127.0.0.1/cgi-bin/mapserv binary.
//This would request to http://127.0.0.1/cgi-bin/mapserv NSArray *arrayValues = [[NSArray alloc] initWithObjects:@"/path-to/australia_3857.map", @"all", @"png", @"EPSG:3857",nil]; NSArray *arrayKeys = [[NSArray alloc] initWithObjects:@"MAP", @"LAYERS", @"FORMAT", @"SRS", nil]; NSDictionary *wmsParameters = [[NSDictionary alloc] initWithObjects: arrayValues forKeys:arrayKeys ]; |
Resulting http requests to the mapserv binary:
http://192.168.1.193:81/tilecache/tilecache.py?LAYERS=australia_3857&SRS=EPSG:3857&REQUEST=GetMap&SERVICE=WMS&STYLES=&EXCEPTIONS=application%2Fvnd.ogc.se_inimage&FORMAT=png&VERSION=1.1.1&WIDTH=256&HEIGHT=256&BBOX=16828376.000000,-4006523.000000,16833268.000000,-4001631.000000 |
Note that the bbox values does not contain decimal places. Nevertheless, it still works on Mapserver.
- Now, assuming we have a valid tilecache running. And it is tested from browser, i.e http://127.0.0.1/map/tilecache_3857.html, open up firebug to see the requests. Below is a sample…
http://192.168.1.193:81/tilecache/tilecache.py?LAYERS=australia_3857&FORMAT=jpg&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&STYLES=&EXCEPTIONS=application%2Fvnd.ogc.se_inimage&SRS=EPSG%3A3857&BBOX=16123932.497377,-4539747.9811239,16143500.376618,-4520180.1018829&WIDTH=256&HEIGHT=256 |
But it seems, route-me is not supplying the bbox values accurately, as the decimal values is truncated (BBOX=16828376.000000,-4006523.000000,16833268.000000,-4001631.000000) from our previous request(3).
After changing the wmsParameter values to create a tilecache request, I noticed that route-me is not displaying the tiles correctly.
//Testing for Windows:TileCache - ? NSArray *arrayValues = [[NSArray alloc] initWithObjects:@"australia_3857", @"png", @"EPSG:3857",nil]; //for WIndows //TileCache URL Parameters: NSArray *arrayKeys = [[NSArray alloc] initWithObjects:@"LAYERS", @"FORMAT", @"SRS", nil]; |
- The workaround is to use “double” instead of “floats” in the RMGenericMercatorWMSSource. You can download the zip from RMGenericMercatorWMSSource.zip. I have modified “initialResolution” and “originShift” to both use “double”.
typedef struct { double x; double y; } CGDoublePoint; typedef struct { CGDoublePoint ul; CGDoublePoint lr; } CGXYRect; |
Afterwards, I copied the maxResolution and bbox (tilecache) and specified it for the initialResoultion and originShift respectively.
#import "RMGenericMercatorWMSSource.h" CGFloat DegreesToRadians(CGFloat degrees) {return degrees * M_PI / 180;}; CGFloat RadiansToDegrees(CGFloat radians) {return radians * 180/ M_PI;}; @implementation RMGenericMercatorWMSSource -(id) initWithBaseUrl:(NSString *)baseUrl parameters:(NSDictionary *)params { if (![super init]) return nil; // 156543.03392804062 for sideLength 256 pixels // initialResolution = 2 * M_PI * 6378137 / [self tileSideLength]; // specify here whatever the resolution is from tilecache. initialResolution = 156543.033928041; // 20037508.342789244 //originShift = 2 * M_PI * 6378137 / 2; //originShift = 20037508.342789244f; // specify here whatever the bbox is from tilecache. originShift = 20037508.32; NSLog(@"test initialResolution:%f originShift:%f", initialResolution, originShift); // setup default parameters // use official EPSG:3857 by default, user can override to 900913 if needed. wmsParameters = [[NSMutableDictionary alloc] initWithObjects:[[[NSArray alloc] initWithObjects:@"EPSG:3857",@"image/png",@"GetMap",@"1.1.1",@"WMS",nil] autorelease] forKeys:[[[NSArray alloc] initWithObjects:@"SRS",@"FORMAT",@"REQUEST",@"VERSION",@"SERVICE",nil] autorelease]]; [wmsParameters addEntriesFromDictionary:params]; // build WMS request URL template urlTemplate = [NSString stringWithString:baseUrl]; NSEnumerator *e = [wmsParameters keyEnumerator]; NSString *key; NSString *delimiter = @""; while (key = [e nextObject]) { urlTemplate = [urlTemplate stringByAppendingFormat:@"%@%@=%@", delimiter, [[key uppercaseString] stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding], [[wmsParameters objectForKey:key] stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding]]; delimiter = @"&"; } int sideLength = [self tileSideLength]; urlTemplate = [[urlTemplate stringByAppendingFormat:@"&WIDTH=%d&HEIGHT=%d",sideLength,sideLength] retain]; return self; } // implement in subclass? -(NSString*) uniqueTilecacheKey { return @"AbstractMercatorWMSSource"; } -(NSString *)shortName { return @"Generic WMS Source"; } -(NSString *)longDescription { return @"Generic WMS Source"; } -(NSString *)shortAttribution { return @"Generic WMS Source"; } -(NSString *)longAttribution { return @"Generic WMS Source"; } -(float) minZoom { return 1.0f; } -(float) maxZoom { return 19.0f; } // Converts given lat/lon in WGS84 Datum to XY in Spherical Mercator EPSG:3857 -(CGPoint) LatLonToMeters: (CLLocationCoordinate2D) latlon { CGPoint meters; meters.x = latlon.longitude * originShift / 180; meters.y = (log( tan((90.0 + latlon.latitude) * M_PI / 360.0 )) / (M_PI / 180.0)) * originShift / 180; return meters; } //Converts XY point from Spherical Mercator EPSG:3857 to lat/lon in WGS84 Datum -(CLLocationCoordinate2D) MetersToLatLon: (CGPoint) meters { CLLocationCoordinate2D latlon; latlon.longitude = (meters.x / originShift) * 180.0; latlon.latitude = (meters.y / originShift) * 180.0; //latlon.latitude = - 180 / M_PI * (2 * atan( exp( latlon.latitude * M_PI / 180.0)) - M_PI / 2.0); latlon.latitude = 180 / M_PI * (2 * atan( exp( latlon.latitude * M_PI / 180.0)) - M_PI / 2.0); return latlon; } // Converts pixel coordinates in given zoom level of pyramid to EPSG:3857 -(CGDoublePoint) PixelsToMeters: (int) px PixelY:(int)py atZoom:(int)zoom { double resolution = [self ResolutionAtZoom: zoom]; CGDoublePoint meters; double x = (px * resolution - originShift); double y = (py * resolution - originShift); meters.x = x; meters.y = y; NSLog(@"px: %d py: %d resolution: %f originShift: %f x: %f y: %f", px, py, resolution, originShift, x, y); return meters; } -(NSString*) tileURL: (RMTile) tile { //RMLatLongBounds tileBounds = [self TileLatLonBounds:tile]; // Get BBOX coordinates in meters CGXYRect tileBounds = [self TileBounds:tile]; NSString *url = [urlTemplate stringByAppendingFormat:@"&BBOX=%f,%f,%f,%f", tileBounds.ul.x, tileBounds.lr.y, tileBounds.lr.x, tileBounds.ul.y]; NSLog(@"Tile %d,%d,%d yields %@",tile.zoom, tile.x, tile.y, url); return url; } //Returns bounds of the given tile in EPSG:3857 coordinates -(CGXYRect) TileBounds: (RMTile) tile { int sideLength = [self tileSideLength]; int zoom = tile.zoom; long twoToZoom = pow(2,zoom); CGXYRect tileBounds; tileBounds.ul = [self PixelsToMeters: (tile.x * sideLength) PixelY: ((twoToZoom-tile.y) * sideLength) atZoom: zoom ]; tileBounds.lr = [self PixelsToMeters: ((tile.x+1) * sideLength) PixelY: ((twoToZoom-tile.y-1) * sideLength) atZoom: zoom]; return tileBounds; } //Resolution (meters/pixel) for given zoom level (measured at Equator) -(double) ResolutionAtZoom : (int) zoom { return initialResolution / pow(2,zoom); } @end |
- Download TileCache_RouteMe.zip Note: You need a valid mapserver and tilecache running.