Flyover mode in MapKit with WMS

Suprise, iOS 9 MapKit finally enables  flyeover mode with WMS overlays ! I have used my github  MapKit sample and only  set in MapViewController    mkMapView.mapType = MKMapTypeHybridFlyover;

Advertisements

Drawing Shape File on MapKit

Simple & strightforward test of loading shape file and drawing it on MapKit on iOS8 using drawMapRect

GitHub:https://github.com/Sumbera/SHPonMapKit

 

  • draws only polygons so far
  • primitive optimization, no scale optimisation

Reading of shape file is performed by shapelib

//------------------------------------------------------------
NS_INLINE NSArray *getPolygonsFromShapeFile(NSString *shpFilePath){
   
    const char *path = [shpFilePath cStringUsingEncoding:NSUTF8StringEncoding];
    SHPHandle shp = SHPOpen(path, "rb");
    int numEntities;
    int shapeType;
    
    SHPGetInfo(shp, &numEntities, &shapeType, NULL, NULL);
    
    NSMutableArray *allPolygons = [[NSMutableArray alloc]init];
    for (int i=0; i<numEntities; i++){
       SHPObject *shpObject = SHPReadObject(shp, i);
       if (shpObject->nSHPType == SHPT_POLYGON ||
           shpObject->nSHPType == SHPT_POLYGONZ ||
           shpObject->nSHPType == SHPT_POLYGONM){

        
            int numParts = shpObject->nParts;
            int totalVertexCount = shpObject->nVertices;

            for (int n=0; n<numParts; n++)
            {
                int startVertex = shpObject->panPartStart[n];
                int partVertexCount = (n == numParts - 1) ? totalVertexCount - startVertex : shpObject->panPartStart[n+1] - startVertex;
                int endIndex = startVertex + partVertexCount;
                
                CLLocationCoordinate2D coords[partVertexCount];
                for (int pv = startVertex, i = 0; pv < endIndex; pv++,i++) {
                    coords[i] =CLLocationCoordinate2DMake(shpObject->padfY[pv],
                                                          shpObject->padfX[pv]);
                }
                // -- this actually converts lat lon to mkmappoints projection
                MKPolygon *singlePolygon = [MKPolygon polygonWithCoordinates:coords count:partVertexCount];
                [allPolygons addObject:singlePolygon];
            }
       }
       
     SHPDestroyObject(shpObject);
       
  }
    SHPClose(shp);
    
    return [allPolygons copy];
}

credits/inspiration:

drawing : http://stackoverflow.com/questions/17673410/mkmapview-with-multiple-overlays-memory-issue
parsing : http://www.al-tyus.com/blog/2013/10/14/mapkit-and-esri-shapefiles
shapelib: http://shapelib.maptools.org
dala: http://www.geoportalpraha.cz

WMS with Google Maps on iOS

Screen Shot 2014-04-21 at 00.49.02Sample for using WMS sources in Google Maps SDK for iOS. available on github here: https://github.com/Sumbera/WMS_iOS_GoogleMapSDK
Provide your API key in the WMSController.h

  • Google Maps for iOS used : 1.7.2 (April 2014)
  • used XCode 5.1.1 (April 2014)
  • iPad Air, iOS 7.1 (should run in iOS6.0 too)

 

There are two ways of overlaying WMS in the Google Maps for iOS SDK:

“Method B”: use GMSTileURLConstructor

   // -- method B. WMS tile layer with GMSTileURLConstructor
      GMSTileURLConstructor urls = 
         ^(NSUInteger x, NSUInteger y, NSUInteger z) {
           BBox bbox = bboxFromXYZ(x,y,z);
           NSString *urlKN = 
             [NSString stringWithFormat:@"Your WMS url&BBOX=%f,%f,%f,%f",
                                           bbox.left,
                                           bbox.bottom,
                                           bbox.right,
                                           bbox.top];

          return [NSURL URLWithString:urlKN];
      };

“Method A”: use custom TileLayer derived from GMSTileLayer

  1. your derived class from GMSTileLayer (here WMSTileLayer.h) will receive tile request
     -(void)requestTileForX:(NSUInteger)x 
                                     y:(NSUInteger)y
                                  zoom:(NSUInteger)z 
                              receiver:(id<GMSTileReceiver>)receiver
    
  2. WMSTileLayer first checks for cached tile and if found calls :
      [self drawTileAtX:x y:y zoom:z Url:urlStr Receiver:receiver] ;
    
  3. if tile is not cached we download it, save it to the file system (using MD5 hash) and call to draw it
      [data  writeToFile: filePath  atomically:YES];
      [self drawTileAtX:x y: y zoom: z Url:urlStr Receiver:receiver] ;
    
  4. drawTileAtX is very simple:
      -(void) drawTileAtX: (NSUInteger) x 
                                   y:(NSUInteger) y
                                zoom:(NSUInteger)zoom
                                 Url:(NSString*) url
                            Receiver: (id<GMSTileReceiver>) receiver {
           UIImage             *image   = TileLoad(url,NO); 
           [receiver receiveTileWithX:x y:y zoom:zoom image:image]; 
      }
    

}

both ways are used in this sample.

Instantiate view on iOS with mix of XIB and View class

How avoided storyboard and overloading XIB with just right split between what is visually defined and what is programatically programmed:

  1. visual set-up of the view in Interface builder – only view, no controllers and setting up outlets but no actions. AddNoteView.xib . Justify controls, set up autoscaling etc..
  2. create AddNoteView.h with outlets
  3. create AddNoteView.m and hook awakeFromNib and setup additional settings on view- (void)awakeFromNib{

    [super awakeFromNib];

    CALayer * layer = [self.locationThumb layer];

    [layer setMasksToBounds:YES];

    [layer setCornerRadius:10.0];

  4. create AddNoteViewController.m and define loadView- (void)loadView {

    self.view =  [[UINib nibWithNibName:@”AddNoteView” bundle:nil]  instantiateWithOwner:nil options:nil][0];

    }

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);

}