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
Hi Murray, I tried to view your project in the link you posted but it is down, I am not sure if this is permanent or not, but is it possible to find the zip file elsewhere? I have been trying for a couple of weeks to get something similar working but failed completely so I would love to see how you did it. Thanks!
By: Pete on May 27, 2013
at 1:04 pm
Hi pete , the server was down it should be up now.
cheers
murray
By: murrayhking on May 27, 2013
at 1:12 pm
Murray, server is back up so don’t worry thanks!
By: Pete on May 27, 2013
at 7:38 pm
Hi Murray, thanks very much for getting the server up again – I didn’t see your post before I replied. Its going to be some work to get the project working by the looks of things (I had trouble linking to libMapView so I just copied the .a file in the project folder to build the project, but it seems to be locked in a loop when I run it, and as a xcode beginner it will take me some time to figure out why I think). So I wonder if you could tell me if you are using the OSGB grid for your tiles, if not I guess theres no point me working at this. I want to be able to show tiles from both OS openspace and OSM ideally, but my main problem is working out how to modify route-me’s tile projection to fit the OSGB projection. Did you do this in your project? Highly appreciate your help BTW! Thanks, Pete.
By: Pete on May 27, 2013
at 9:22 pm
Hi pete, You can get route-me working with OSGB but still requires quite a lot a hacking at the internals rather than just adding a proj4 OSGB string I have found out.
I have found the openlayers > 2.12 now does a fairly good job on the mobile with BNG heres our example of using openlayers with BGS http://fieldtripgb.blogs.edina.ac.uk/ its on the app store and google play.
By: murrayhking on May 28, 2013
at 11:37 am
Hi Murray, thanks for the suggestion, the app looks really nice too and I will probably install it, but I’ve been hacking away at this route-me problem for about 2 weeks now (all day, evening and weekend) and to give in would feel like letting it beat me! Also its largely as an Xcode learning exercise I am doing it, and as a base for future modifications. Did you manage to ever get route-me to use OSGB and if so do you remember any pointers which might help me? I can easily get the current location in OSGB format using proj4 but I just can’t work out how the code which calculates which tiles to request works. IE how to show the map (I can load the OS tiles but not work out how to put them in the right place). Im wondering whether to just pretend the OS tile server is a google tile server and use a bit of maths to adjust the tile XY coorinates and scale, but wondering if this would work because of the fixed 1km OS squares vs the google tiles (projection) which uses tiles with a variable scale. Anyway thanks hugely for your advise so far, Pete
By: Pete on May 28, 2013
at 12:36 pm
Sorry been away, I did get it working but it was a bit of a hack! , I Didn’t bother refactoring the framework just altered the RMMercatorWebSource, RMFractalTileProjection directly. I think it was due of the aspect of the BNG 130000 by 700000 I was using a TMS not a WMS datasource. You could try and map the os to google tiles although the geolocation stuff would be out.
I dont know what youre after however this version of http://os.openstreetmap.org/ which is based on os open data its in the spherical mercator projection so you wouldn’t have to muck about with the library. There is no terrain data though.
By: murrayhking on June 7, 2013
at 4:02 pm
Hi Murray,
I managed to get it working in the end via a series of hacks too, by the sounds of things different to yours! I decided to try to completely ignore the bits of route-me I did not understand (the projection bits), and changed the google projection size to suit my tile size / number of tiles etc, did a bit of trivial transformation maths to handle the backwards tile numbering scheme used by the OSM system, and then used proj4 to convert coordinates. So I need the transform function every time I do anything, I can’t use lat/long directly, I am not sure how inefficient this is. I might try your method (look at RMFractalTileProjection and try to work out how to alter it). So currently my tiles are numbered using the OSGB numbering scheme /500. IE around some point in the cornish sea they start at 0,0 and go up to 1400,0 at the kent end of the OSGB super tile etc. The accuracy is absolutely bang on from what I can tell, from being out and about and checking landmarks, so I am pleased at that. Its been an interesting project and I know Xcode a lot better now so it was worth it! Thanks very much for your help.
Cheers, Pete
By: Pete on June 12, 2013
at 3:33 pm