The route-me library investigation notes.
One of the main problems using the openlayers javascript library to display maps on mobile devices is slow rendering especially when panning and zooming. It doesn’t give the best user experience.
There is an enhancement to the performance of map dragging planned for the next release of the openlayers, which should help.
http://trac.openlayers.org/wiki/three
Google maps javascript library has a much better performance on the iPhone but again ties us up to the google maps terms and conditions so we can’t use our own *WMS with a different projection.
*WMS is a web map service http://en.wikipedia.org/wiki/Web_Map_Service. This type of map source is not supported by the google maps libraries.
Another problem is the caching of map image tiles. Its pretty suboptimal using either javascript library.
I suppose potentially these javascript libraries could be enhanced to use html5 or javascript sqlite storage to help with caching.
Obviously caching is especially important when you are out in areas with a poor 3g connection.
What would be nice is the possibility of pre-loading a detailed remote area map before travelling there. If we had complete control of the caching strategy we could ensure that the remote area map is never purged before required.
So we started looking at native options.
We briefly looked at Apples Webkit 2 however its rigid use of google maps quickly rules it out for us.
Heres the notice for Webkit use.
Important: The MapKit framework uses Google services to provide map data. Use of specific classes of this framework (and their associated interfaces) binds you to the Google Maps/Google Earth API terms of service. You can find these terms of service at http://code.google.com/apis/maps/iphone/terms.html.
We want to use our own wide range of maps which include wms sources and different projections.
This includes historic maps to highly detailed ordnance survey master maps.
We tried Route-me (http://code.google.com/p/route-me/) an opensource mapping library for the iphone.
It is completely written in objective c using the CoreAnimation library and has much better performance and control than the doing the equivalent in the mobile browser.
To use the route-me library follow the instructions carefully on
http://code.google.com/p/route-me/wiki/EmbeddingGuide
If you are new to objective c. I found the apple developer site helpful eg
This runs through how to create your first application.
On iTunes U there is a full course on iPhone application development from Stanford University (CS193P) which is great. There are 20 video lectures to watch, plus the course material on the stanford uni site.
I found that much quicker to absorb key principles rather that slogging through the thick objective c & cocoa reference book we have lying around here.
Using the route-me library.
The crux of it is adding your own map source class for use with the route me framework.
For example..
This class creates a tile source pointing towards the National Library of Scotland Historic Map. This is open and you can use it in mash-ups.
Details about using the Historic map can be found here
http://geo.nls.uk/maps/api/#howtowebsite
You can do this by creating a new MapSource Class
Create the header file ScotLibHistoricMapSource.h that extends RMAbstractMercatorWebSource.h
#import
#import "RMAbstractMercatorWebSource.h"
@interface ScotLibHistoricMapSource : RMAbstractMercatorWebSource {
}
@end
And the implementation …
#import "ScotLibHistoricMapSource.h"
@implementation ScotLibHistoricMapSource
-(NSString*) tileURL: (RMTile) tile
{
NSAssert4(((tile.zoom >= self.minZoom) && (tile.zoom <= self.maxZoom)),
@"%@ tried to retrieve tile with zoomLevel %d,
outside source's defined range %f to %f",
self, tile.zoom, self.minZoom, self.maxZoom);
//How many concurrent connections can the iphone have to same domain?
//faster eg host = 'http://t' + (x+y)%5 + 'uk.tileserver.com';
NSString* url = [NSString stringWithFormat:@"http://host/_os1/r0/%d/%d/%d.jpg",
tile.zoom,tile.x, tile.y];
NSLog(@" url here %@ ", url);
return url;
}
-(NSString*) uniqueTilecacheKey
{
return @"NLSHistoricMap";
}
-(NSString *)shortName
{
return @"NLS Historic Map";
}
-(NSString *)longDescription
{
return @"NLS Historic Map Test";
}
-(NSString *)shortAttribution
{
return @"© Edina";
}
-(NSString *)longAttribution
{
return @"NLS Historic Map";
}
@end
Screenshot of historic map app with a current Ordinance Survey Map Layer overlay.
The slider bar just lets you compare the two layers by fading the opacity of the overlay map


Have you looked at this library – http://sourceforge.net/projects/touchmaplite/
By: Jerry on July 11, 2010
at 1:37 pm
yes we have! We think its great! We’ll be posting a blog shortly on this,
By: benismobile on July 13, 2010
at 9:10 am
yes great post i like it
By: herupriadi on July 17, 2010
at 11:38 am
thanks for the blog
it is so informative
By: sinan şahin on January 29, 2011
at 10:27 pm
Hi there,
We are doing something similar with the Route Me library and I’m curious about how you managed to get both of your tile layers loaded at the same time. We’ve created our own custom overlay tiles via Mapnik, but so far we’re only able to toggle between the base map and the overlay — not see the semitransparent overlay on top of the base map.
Any pointers would be appreciated.
Thanks!
Jordan
By: Jordan Anderson on August 24, 2011
at 3:53 am
hi Jordan
I added two instances of the RMMapView to the main view eg one stacked directly on top of each other.
@private
RMMapView * upperMapView;
RMMapView * lowerMapView;
Then in the delegate of the upperMapView added in a couple of the optional protocol methods of the RMMapViewDelegate
These methods below update on the main thread to keep the lower map in sync with the top map.
-(void) updateMap:(RMMapView*) map {
CLLocationCoordinate2D center = map.contents.mapCenter;
[lowerMapView moveToLatLong:center];
}
-(void) updateZoom: (RMMapView*) map {
[lowerMapView zoomWithRMMercatorRectBounds:upperMapView.contents.projectedBounds];
}
- (void) afterMapMove: (RMMapView*) map {
[self performSelectorOnMainThread:@selector (updateMap:) withObject:map waitUntilDone:NO];
}
- (void) afterMapZoom: (RMMapView*) map byFactor: (float) zoomFactor near:(CGPoint) center {
[self performSelectorOnMainThread:@selector (updateZoom:) withObject:map waitUntilDone:NO];
}
make sure the [upperMapView setDelegate:self]; is set in the main controller to receive the map messages.
I’ll zip up the source and put it somewhere if its not clear,
cheers
murray
By: murrayhking on August 24, 2011
at 1:22 pm
Very nice work. Can you send me the project dir because i am having difficulty create a working project based on your code. Thanks. My email is ahjie87@gmail.com
By: jing jie on November 15, 2011
at 2:55 am
Created a while back using xcode 3.0 but here it is ..
http://mab.edina.ac.uk/coordomatic/NLSHistoricMap.zip
By: murrayhking on April 6, 2012
at 11:05 am
Very impressed, but I am having a hard time reproducing your code. Can you send me the project dir?
By: lukassen on October 14, 2011
at 4:14 pm
Thank you for this post..I have the same problem..I am not able to synchronize the two layers..Can you send me the zip with the example?
By: andreasv on March 15, 2012
at 11:10 pm
I have looked out my old project here it is …
http://mab.edina.ac.uk/coordomatic/NLSHistoricMap.zip
By: murrayhking on April 6, 2012
at 11:03 am