Archive

Archive for the ‘tilecache’ Category

iPhone Note #20:Integrating Mapserver/TileCache to RouteMe

January 14th, 2010 rupert 2 comments

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

October 24th, 2008 rupert No comments

In Tilecache, metaTiling creates larger tiles and chops them up.
Note the difference between using metatile=yes and metatile=no.

metatile_no.png

Figure 1: metatile=no

metatile_yes.png

Figure 2: metatile=yes

Categories: tilecache Tags:

Recaching single tiles in Tilecache

September 22nd, 2008 rupert No comments

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

Picture 2.png

Well for completeness, I gave my ofcmate a DELETE tool which wipes out everything in the /tilecache/map_dir directory..

Categories: tilecache Tags:

Jpeg decoder problem on TileCache on MacOSX Leopard

May 12th, 2008 rupert No comments

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
Categories: tilecache Tags: , , ,

Using TileCache, OpenLayers, Mapserver for Projection 900913

April 8th, 2008 rupert No comments

I had a few problems with TileCache the other week which I am eager to blog about, since I knew for sure that later on, I might encounter the same. I don’t have the exact errors with me right now, so I’m jotting this down from my head…

  1. Classic Resolutions problem. Use extent_type=loose
  2. Can not set image type

UPDATED (JAN 11, 2010): Classic Resolutions problem:

How are resolutions calculated? Assuming we have:

Original:
Lower Left (LL) or minx, miny: 12453557, -5434940
Upper Right (UR) or max, maxy: 16980842, -1180729

maxResolution = (max – minx)/tilesize = (16980842 – 12453557)/512 = 8842.353
where tilesize = 512.

Therefore, we can set/guess for max so that we have maxResolution as a whole number.

Adjusted:
minx, miny: 12453557, -5434940
maxx, maxy: 16980661, -1180729

gives a maxResolution (whole number) of 8842.

Now, you can use 8842 in both the TileCache.cfg and OpenLayers Javascript. Read more…