By Rupert
Posts tagged tilecache
iPhone Note #20:Integrating Mapserver/TileCache to RouteMe
Jan 14th
Below is a summary of how I was able to implement Mapserver, TileCache and Route-Me iPhone Mapping Framework.
1. 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
2. Grab the RMGenericMercatorWMSSource from http://groups.google.com/group/route-me-map/browse_thread/thread/b58fa1d20cf15823/e30c42d9c90a8170?lnk=gst&q=Generic#e30c42d9c90a8170
3. 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.
4. 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];
5. 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
6. Download TileCache_RouteMe.zip Note: You need a valid mapserver and tilecache running.
TileCache: Getting Cutoff on the edges
Oct 24th
In Tilecache, metaTiling creates larger tiles and chops them up.
Note the difference between using metatile=yes and metatile=no.

Figure 1: metatile=no

Figure 2: metatile=yes
Recaching single tiles in Tilecache
Sep 22nd
Got this one from IRC this morning.. In order to recache single tiles in TC, as long as it is not metatile (metatile=false), then we can simply append “FORCE=1″ in the URL..
http://192.168.2.14/tilecache/tilecache.py?FORCE=1&LAYERS=beijing_900913_wide_en&MAP=%2Fmyhome%2Fmap%2Fbeijing%2Fnew%2Fbeijing_900913_wide_en%2Fbeijing.map&FORMAT=jpg&TRANSPARENT=true&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&STYLES=&EXCEPTIONS=application%2Fvnd.ogc.se_inimage&SRS=EPSG%3A900913&BBOX=12951949.03168994,4852222.893873828,12952101.905746482,4852375.767930372&WIDTH=256&HEIGHT=256

Well for completeness, I gave my ofcmate a DELETE tool which wipes out everything in the /tilecache/map_dir directory..
Using GDALWARP to reproject raster that will fit with Google Satellite
Jul 29th
Just a couple of notes to onself using gdal: Use gdalwarp to reproject your GeoTIFF files! I wanted to use my own satellite images acquired from GeoEye, however, on some areas I wanted to use google sat images as well since I don’t have the coverage. In order to do so, I need to reproject the sat images to 900913. Note you need to specify this in your epsg file in my previous post.
rupert:beijing_900913_satellite rupert$ gdalinfo Mosaic_RGB.tif Driver: GTiff/GeoTIFF Files: Mosaic_RGB.tif Size is 4248, 4553 Coordinate System is: GEOGCS["WGS 84", DATUM["WGS_1984", SPHEROID["WGS 84",6378137,298.2572235630016, AUTHORITY["EPSG","7030"]], AUTHORITY["EPSG","6326"]], PRIMEM["Greenwich",0], UNIT["degree",0.0174532925199433], AUTHORITY["EPSG","4326"]] Origin = (116.291476140000000,40.025198500000002) Pixel Size = (0.000046860000000,-0.000035970000000) Metadata: AREA_OR_POINT=Area TIFFTAG_XRESOLUTION=100 TIFFTAG_YRESOLUTION=100 Image Structure Metadata: INTERLEAVE=BAND Corner Coordinates: Upper Left ( 116.2914761, 40.0251985) (116d17'29.31"E, 40d 1'30.71"N) Lower Left ( 116.2914761, 39.8614271) (116d17'29.31"E, 39d51'41.14"N) Upper Right ( 116.4905374, 40.0251985) (116d29'25.93"E, 40d 1'30.71"N) Lower Right ( 116.4905374, 39.8614271) (116d29'25.93"E, 39d51'41.14"N) Center ( 116.3910068, 39.9433128) (116d23'27.62"E, 39d56'35.93"N) Band 1 Block=4248x1 Type=Byte, ColorInterp=Red Band 2 Block=4248x1 Type=Byte, ColorInterp=Green Band 3 Block=4248x1 Type=Byte, ColorInterp=Blue
rupert:beijing_900913_satellite rupert$ gdalwarp -s_srs epsg:4326 -t_srs epsg:900913 Mosaic_RGB.tif sat_4m_rgb.tif Creating output file that is 4245P x 4556L. Processing input file Mosaic_RGB.tif. 0...10...20...30...40...50...60...70...80...90...100 - done.
rupert:beijing_900913_satellite rupert$ gdalinfo sat_4m_rgb.tif Driver: GTiff/GeoTIFF Files: sat_4m_rgb.tif Size is 4245, 4556 Coordinate System is: PROJCS["Google Maps Global Mercator", GEOGCS["WGS 84", DATUM["WGS_1984", SPHEROID["WGS 84",6378137,298.2572235630016, AUTHORITY["EPSG","7030"]], AUTHORITY["EPSG","6326"]], PRIMEM["Greenwich",0], UNIT["degree",0.0174532925199433], AUTHORITY["EPSG","4326"]], PROJECTION["Mercator_1SP"], PARAMETER["central_meridian",0], PARAMETER["scale_factor",1], PARAMETER["false_easting",0], PARAMETER["false_northing",0], UNIT["metre",1, AUTHORITY["EPSG","9001"]]] Origin = (12945507.907502911984921,4869604.732793668285012) Pixel Size = (5.219801430503303,-5.219801430503303) Metadata: AREA_OR_POINT=Area Image Structure Metadata: INTERLEAVE=PIXEL Corner Coordinates: Upper Left (12945507.908, 4869604.733) (116d17'29.31"E, 40d12'53.10"N) Lower Left (12945507.908, 4845823.317) (116d17'29.31"E, 40d 3'2.78"N) Upper Right (12967665.965, 4869604.733) (116d29'25.89"E, 40d12'53.10"N) Lower Right (12967665.965, 4845823.317) (116d29'25.89"E, 40d 3'2.78"N) Center (12956586.936, 4857714.025) (116d23'27.60"E, 40d 7'58.12"N) Band 1 Block=4245x1 Type=Byte, ColorInterp=Red Band 2 Block=4245x1 Type=Byte, ColorInterp=Green Band 3 Block=4245x1 Type=Byte, ColorInterp=Blue
Jpeg decoder problem on TileCache on MacOSX Leopard
May 12th
There is a problem with a “jpeg decoder” because Python Imaging Library (PIL) cannot find the jpeg libraries. Remember that we installed kyngchaos UnixIO libraries, therefore the PIL setup.py script should point to use those libraries.
Actually, I even installed the jpegsrc manually. I believe there is no need to do this, since UnixIO Image libraries is sufficient enough for PIL to install. What is important is to ensure that python tests succeeded. Once you get passed that, then installing PIL should work.
1. Extract Imaging-1.1.6.tar.gz 2. Edit setup.py to reflect: JPEG_ROOT="/Library/Frameworks/UnixImageIO.framework/unix" 3. python setup.py build_ext -i 4. If no test fails, then go ahead and install 5. python setup.py install
Comments