Archive

Archive for the ‘GIS’ 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 Comments off

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 Comments off

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:

Transform 900913 to 4326

September 4th, 2008 rupert 1 comment

Stumbled upon an error in reprojecting data from 900913 (Google Speherical Mercator) to 4326 (WGS84). I’m recalling this from my head now, the error was something related to “NAD sth”. Workaround was to convert it as follows:

1. 900913
2. 32650 (UTM for your area)
3. 4326

I’ll get the SQL from my ofcmate and paste it here…

Categories: postgis Tags:

Postgres PostGIS CheatSheet v2

August 29th, 2008 rupert Comments off

This is a quick-command list of Postres. If you want detailed instructions, please visit the Postgres Manual.

How do I Show all databases?
1. Using “psql -l”

2. Using

postgres=# \l
List of databases
Name       |  Owner   | Encoding
------------------+----------+----------
postgis          | postgres | UTF8
postgres         | postgres | UTF8
template0        | postgres | UTF8
template1        | postgres | UTF8
template_postgis | postgres | UTF8
(5 rows)

Note: Do not drop template databases if not necessary.

How do I run a script from the prompt?
psql -d cybersoftbj -u user -f myfile.sql

Its very useful in reloading user-defined functions.

How do I create a user/role?

CREATE ROLE lbs WITH LOGIN PASSWORD 'mypassword' SUPERUSER INHERIT CREATEDB CREATEROLE;

How do I change the password for a user/role?

ALTER ROLE lbs PASSWORD 'mynewpassword';

How to provide/restrict access privileges to tables?

GRANT SELECT ON TABLE table TO user;
REVOKE SELECT ON TABLE table FROM user;

How to dump database in a text file?

pg_dump -U lbs -d cybersoftbjv1 -h 127.0.0.1 -W > cybersoftbjv1.sql

How to dump database cleanly?

pg_dump -c -d -E UTF8 -h 127.0.0.1 -U lbs -W platform_v1 > platform_v1.sql

How to rename a database?

ALTER DATABASE beijing_app RENAME TO beijing_app_20080801;

How to update using two tables?

UPDATE road_for_update u
SET the_geom = r.the_geom
FROM roads r
WHERE r.rd_id = u.rd_id;

How to change a column type with Cast?

ALTER TABLE roads ALTER COLUMN class_new TYPE integer USING class_new::integer;

How to add a geometry column to a table?
EXAMPLE: SELECT AddGeometryColumn(‘public’, ‘poi’, ‘the_geom’, 4326, ‘POINT’, 2)

Changing column names with spaces?
ALTER TABLE class_aroundme RENAME "level 1" TO level_1;

Setting kernel shmmax for postgres
sysctl -w kernel.shmmax=134217728
Note: For permanent changes see /etc/sysctl.cfg11. How to backup table(s) from pg_dump?
pg_dump poi_beijing -t class -t poi_class -f $BACKUPDIR/test_$MYDATE.sql

Categories: postgis, postgres Tags: ,