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

Advertisements

WMS on MapKit with iOS7

Updated WMS over MapKit sample code for iOS7 , available on github  I have added cadastral maps of Czech Republic, used camera API to set the view and tested, check also WMS on Google  Maps SDK on iOS mentioned here

iOS7 introduced new class MKTileOverlay sample derives from this class WMSTileOverlay

Key method to custom tile loading (and cache control) is loadTileAtPath:result

- (void)loadTileAtPath:(MKTileOverlayPath)path result:(void (^)(NSData *tileData, NSError *error))  result

this method is called by MapKit (or better by MKTileOverlayRenderer ) when it needs to draw a tile . It asks for NSData (and error) from x,y,z tile coordinates. In this method you can  load NSData either from local cache or from NSURLConnection and pass resulting NSData (when ready)  back to MapKit, for example like this (reading from cache)

result ([NSData dataWithContentsOfFile:filePath], nil);

if you do not need to use cache and you do not provide loadTileAtPath method , you can use another hook (callback) that is provided by MKTileOverlay, URLForTilePath:path

- (NSURL *)URLForTilePath:(MKTileOverlayPath)path

this method enables to custom format URL required to load tile, thus you can use WMS HTTP-GET parameters, for example :

NSString * resolvedUrl = [NSString stringWithFormat:@"%@&BBOX=%f,%f,%f,%f",self.url,left,bottom,right,top];

if there is neither method in the derived class, then you probably do not need to derive at all from MKTileOverlay and directly use it with initWithUrlTemplate (not case for WMS, but for any other x,y,z  sources)

IMG_0021

Bad news is that  MapKit on iOS7 doesn’t support   tilt/pinch in Satellite/Hybrid mode in MapKit on iOS7

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.

From .NET to iOS developer

What it takes to transfer from Microsoft .NET to Apple iOS  developer ?

Update 2015: more longer and similar story/explanation  found  here

 

So this was me ‘before’ with all my friends:  Windows, C#, .NET, Visual Studio, MSDN :

transcript:
– easy ‘managed’ life,
– can smile and make ceremony
– implicit friends and friends of friends you can’t get rid of them
– shallow water, no deep dive
– few freezes
– slightly detuned, but good enough for many
– moon shots

…and me ‘after’  passing through the  fire of iOS, Objective-C, Cocoa Touch, XCode, Documentation, Mac, Certificates, AppStore

transcript:
– different game, different rules
– lot of explosive material (resources)
– focused, not distracted by ‘friends’
– intelligence and luck needed
– dangerous and too explicit
– very rewarding

 

 

Leaflet 0.7 vs. OpenLayers 3 beta 1 on iOS

leafol2Made quick test of these 2 +1 HTML5 renderers on iOS running inside iOS app in the WebView, that is without Nitro acceleration.  All run on iPad Air

Leaflet 0.7 : great , works fine, everywhere, doesn’t load while dragging map (on mobile only) , runs on Microsoft  Surface too.

OpenLayers 3 beta 1 : runs fine too, loads map during dragging, seems like smaller framerate, can over zoom OSM, doesn’t run in Microsoft Surface well.

Seznam Mapy Api v 4 – proprietary renderer from Seznam , bad rendering  on iOS, missing tiles, nice map sources

Videos and original web pages used: – screencasted by AirPlay – that is directly from iPad Air:

B. OpenLayers 3 beta1
C. Mapy Api v 4.8

Mobile Considerations

Quite good (and lengthy) chapter worth of weekend reading for mobile development
Aral Balkan, the author,  describes many important  concerns or considerations before you start mobile development :
http://mobile.smashingmagazine.com/2012/06/18/mobile-considerations-in-user-experience-design-web-or-native/

My favourites quotes from the article:
If your choice of platforms and technologies is based simply on your perceived short-term business needs or on the current competencies of your team, then you are making a decision that solves your own problems, not the user’s problems. This may have short-term advantages, but you will not be able to compete in the long term with those who solve the user’s problems first. Your choice of technologies and platforms should be based on how best you can meet the user’s needs, not on ideological bias or on obtaining short-term gain at the risk of long-term loss.

A common mistake I see many designers make is to assume that by using cross-platform authoring technologies they will be able to write once, run anywhere. This is a myth. And acting on the myth can lead to rather costly underestimations. Your application might run on multiple platforms, but this rarely—if ever—means that it will run well on multiple platforms.

Designers who do not take the unique cultures, customs, language and norms of their respective platforms into consideration risk making their applications look and sound out of place. The applications will appear noticeably foreign, unnecessarily loud and usually rather arrogant, simply because they are culturally insensitive.

The worst thing you could do, of course, is disrespect all of your users by creating a lowest-common-denominator application that gives every user on every platform an unoptimized user experience. At that point, you would be at your most vulnerable.

Supporting multiple platforms is not a feature unless you can support them all well. You may have first-to-market advantage, but that will last only until you are outdone by your best-in-market competitor.

So, write once, run anywhere is a dangerous myth. Cross-platform applications that compete successfully are write once, optimize everywhere. You must understand the implications this will have on your budget and schedule and plan for optimizing, testing and supporting your application on every platform you choose to support.

In summary, be careful when creating native binaries that simply wrap applications that do not use native components. These apps have a tendency to look like native applications, but they cannot behave like native applications because they do not use native components in native frameworks. A PhoneGap application that uses the jQTouch framework might display what looks like an iOS table view when running on an iPhone, but this is simply an HTML look alike brought to life by clever use of CSS and JavaScript. It pretends to be an iOS table view, but it cannot meet the behavioral characteristics of a real table view component from the Cocoa Touch framework, and thus it ends up creating expectations that it cannot meet.

The advantages of building native applications using native technologies are numerous. For one thing, you have complete flexibility in optimizing the application and user experience. When you use native components and adhere to the human interface guidelines for your chosen platform, your application will conform to the culture, language and norms of that platform. It will be easier for users to learn and use.