WMS overlay in iOS MapKit

update May 2014: iOS7 version and notes available here: https://blog.sumbera.com/2014/05/17/wms-on-mapkit-with-ios7/

 

I have crafted really simple and quick code at ESA App dev camp for viewing WMS sources.  And as few people questioned  me on  how to do this, I am posting the full code of the MapView component that takes sample WMS service (Ozone) and overlays this above MapKit.

In github here : https://github.com/Sumbera/WMSoverMapKit

you will find MapViewController that accepts  WMS  sources stored as array with BBOX %f, %f, %f, %f

-(void) addWMSOverlays: (NSArray*) overlays

this can be called from your rootController:

MapViewController * mapViewController = [[MapViewController alloc] init];

WMSOverlay * wmsOverlay =[[WMSOverlay alloc]
 initWithName:@"Ozone" Url:@"http://wdc.dlr.de/ogc/produkt_t?LAYERS=GOME2_O3&
 TRANSPARENT=TRUE&
 FORMAT=image/png&
 STYLES=&
 SLD=http%3A%2F%2Fwdc.dlr.de%2Fsld%2FGOME2_O3_sld.xml&
 TIME=2012-02-12T00:00:00Z&
 SERVICE=WMS&
 VERSION=1.1.1&
 REQUEST=GetMap&
 SRS=EPSG:4326&
 WIDTH=256&
 HEIGHT=256" 
 Opacity:0.5];
  [mapViewController  addWMSOverlays:[NSArray arrayWithObjects:wmsOverlay, nil]];

please note:

#1. that sample code uses experimentaly MKNetworkKit, which has some occasional troubles. You can replace the download method in WMSOverlayView  class downloadTile

#2 it uses simple hash for storing tiles on cache.

enjoy

iKatastr2 on 6th place in Křišťálová lupa 2012

I am excited   that  project I have been working on at Intergraph called ” iKatastr2″  has placed on  6th position in category “Mobile service” in national competition called “Kříšťálová lupa 2012” (Czech Internet Prize)
  • First 3 places has been occupied by popular news applications or national TV app ( hundreds of thousands users).
  • 4th place positioned  AVG antivirus application for Android. (tens of millions users)
  • 5th place took Czech public  transit application. (hundreds of thousands users)
iKatastr2 was the only map/geospatial-related application  nominated
few reasons for success are :
  •  good content provided through  OGC standards  by national mapping agency (CUZK)
  •  great user experience  for getting job done
  •  application quality and stability
  •  focus on local (national)  services rather than global

Awarding ceremony was great fun  ! it was in the spirit of end of the world in 2012 so stage was styled as cemetery and winners received among other gravestones . Each winner had a chance to say something and then they passed into the black tunnel to ‘the other side’ as the end of the world is planned in 3 weeks.

ikatastr2 on AppStore

I have just release iKatastr2 app on AppStore (free) that  might be useful for you to look at as it uses WMS sources and  custom tile loading on top of google.

App does simple stuff – shows cadastral information by tapping on cadastral  map of the Czech Republic.

As all is driven by JSON configuration it is quite easy to render similar information from other sources.

http://itunes.apple.com/us/app/ikatastr2/id545292674?ls=1&mt=8

 

enjoy !

 

ESA App challenge winner

  I have participated in the first ESA (European Space Agency)  app dev challenge where 5 teams competed on best  concept/prototype that will bring GMES data sources to the public on mobile devices. Our team (Czech Republic, Germany, Macedonia) won and each member got iPad 3 . We won not because we were best in terms of  the best prototype,  concept or presentation, but because we fit best to the criteria imposed by this challenge and each piece of delivery (5 page long document describing concept, presentation, prototype demo)  was pretty good and simple enough to be feasible for final realization. Moreover a unique value of mobile devices plus unique value of  GMES satellites have been addressed.  Full article can be read here :   http://www.esa.int/esaEO/SEMIQOBXH3H_index_0.html

Update 08/08/2012 : there is also press release from my company  Intergraph : http://www.intergraph.com/assets/pressreleases/2012/08-01-2012.aspx

 

MapKit optimization

  1. MKMapRect and CGRect are the same !
  2. MapKit sends duplicate requests for CanDraw. Duplication found is 10 of 20 tiles (full iPad screen) are sent twice. Very interesting is that  drawRect in base map does that too (duplicated request is sent from the second running thread )
  3. MapKit is using CATiledLayer underneath
  4. Don’t do copy-paste 2 loops (see MapTile WWDC 2010 sample project from Apple) if your tiles fits exactly to the matrix of the Google tiles

// for (NSInteger x = minX; x < = maxX; x++) {{
//for (NSInteger y = minY; y < = maxY; y++) }}

Relation between MKMapRect and Spherical Mercator Resolution

MKMapRect projected coordinate system used in iOS MapKit  is not the same as Spherical Mercator used by Google, Bing and others. However there is a close linear relationship of the resolution in official spherical mercator and scale used by MapKit and that is   0.149291.

this number is constant for this relation :spherical mercator resolution (e.g. used by OpenLayers)  * zoomscale (used in MapKit)

spherical mercator resolution calculated as : spherical mercator width / view width

zoomscale in MapKit is calculated as : view width / mkMapRect.size.width

that is : spherical mercator width / mkMapRect.size.width = 0.149291  always.

So now any resolution from Spherical Mercator can be directly mapped into the MapKit internal coordinate system (mkMapRect) and vice versa without heavy calculations from mkMapRect to WGS84 and then to Spherical Mercator.

more about projection used in MapKit can be found here : LocationAwarenessPG

How to disable base Google Maps in MapKit

Sept 26th 2012 update :for erasing iOS6 base maps look here: https://blog.sumbera.com/2012/09/26/how-to-erase-ios6-maps-in-mapkit/ 

I have searched this and couldn’t find any answer, so here is my own research so far on this subject. You can disable MapKit base layers using 3 approaches:

#1 remove subview from MKMapView (quite bad as you will loose all overlays too)

#2 use undocumented function (bad too as this will be rejected by Apple approval process):

// get MKMapTileView from view hierarchy
UIView * mkMapTileView = [((UIView*) [ ((UIView*)[self.subviews objectAtIndex:0]).
subviews objectAtIndex:0]).subviews objectAtIndex:0];
// call undocumented method
if ( [mkMapTileView respondsToSelector:@selector(setDrawingEnabled:)]){
[mkMapTileView performSelector:@selector(setDrawingEnabled:) withObject:(id) NO];

#3 use method swizzle

inspired by http://www.mikeash.com/pyblog/friday-qa-2010-01-29-method-replacement-for-fun-and-profit.html

http://atastypixel.com/blog/making-uitoolbar-and-uinavigationbars-background-totally-transparent/

this method is fine as it is official method and enables you to ‘subclass’ a class that you don’t have access in compile time.

#import <objc/runtime.h>

// original method declaration
static void (*_origDrawLayerInContext)(id, SEL, CALayer*, CGContextRef);
// set up subclass in runtime..in some entering method
UIView * mkMapTileView = [((UIView*) [ ((UIView*)[self.subviews objectAtIndex:0]). subviews objectAtIndex:0]).subviews objectAtIndex:0];

Method  origMethod = class_getInstanceMethod([mkMapTileView class], @selector(drawLayer:inContext:));

_origDrawLayerInContext = (void *)method_getImplementation(origMethod);

if(!class_addMethod([mkMapTileView class], @selector(drawLayer:inContext:), (IMP)OverrideDrawLayerInContext, method_getTypeEncoding(origMethod)))

method_setImplementation(origMethod, (IMP)OverrideDrawLayerInContext);

// implement our Override
static void OverrideDrawLayerInContext(UIView *self, SEL _cmd, CALayer *layer, CGContextRef context) {
// possibly call original method if you leave it empty base google maps will not be displayed. You can draw custom content here as well...
//  _origDrawLayerInContext(self, _cmd, layer, context);

}