Posted by: murrayhking | July 7, 2010

Route-me native iphone mapping framework


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.

http://developer.apple.com/iphone/library/documentation/iPhone/Conceptual/iPhone101/Articles/00_Introduction.html

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

Route-me iphone app with historic map

Route-me iphone app with historic map

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

Route-me iphone app with historic map


Responses

  1. Have you looked at this library – http://sourceforge.net/projects/touchmaplite/

    • yes we have! We think its great! We’ll be posting a blog shortly on this,

  2. yes great post i like it

  3. thanks for the blog
    it is so informative

  4. 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

    • 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

      • 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

      • Created a while back using xcode 3.0 but here it is ..
        http://mab.edina.ac.uk/coordomatic/NLSHistoricMap.zip

  5. Very impressed, but I am having a hard time reproducing your code. Can you send me the project dir?

  6. 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?


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Categories

Follow

Get every new post delivered to your Inbox.