Thursday, November 1, 2012

WhirlyGlobe Component: Vectors

I'm running a bit behind on new describing new functionality.    A few weeks ago I added vector support to the WhirlyGlobe Component.

The toolkit itself has had vector support since 1.0.  That's kinda my thing.  Images are everyone else's thing, though so that's what went in to the Component first.

Some Vectors, no doubt.
So great, how do they work?

WGVectorObject

The WGVectorObject is a wrapper around the more complex classes in the WhirlyGlobe API.  If you want better control, go use those.  If you want to do a few simple operations, WGVectorObject will do.

A WGVectorObject can represent one or more features.  It's a little goofy, but makes sense once you start slinging them around.  Just treat them as somewhat opaque.

First thing you'll want to do is load them.  There are a variety of ways.

Vectors From GeoJSON

As the heading implies, you can load GeoJSON.  Here's a quick example that looks for a named file in the bundle, loads that into a WGVectorObject and then adds it to the globe.

                    
NSString *fileName = [[NSBundle mainBundle] pathForResource:name ofType:@"geojson"];
if (fileName)
{
  NSData *jsonData = [NSData dataWithContentsOfFile:fileName];
  if (jsonData)
  {
    WGVectorObject *wgVecObj = [WGVectorObject VectorObjectFromGeoJSON:jsonData];
    WGComponentObject *compObj = [globeViewC addVectors:[NSArray arrayWithObject:wgVecObj]];
  }
}

If you're generating the files yourself or reading from a remote service, it's nice, but it's slow.  Who would think parsing geometry out of a text file would be slow?  I'm shocked.

Vectors From Scratch

You can also create vector data from scratch.  You can make points, linears, and areals.  The following methods are part of WGVectorObject.
                    
/// Construct with a single point
- (id)initWithPoint:(WGCoordinate *)coord attributes:(NSDictionary *)attr;

/// Construct with a linear feature (e.g. line string)
- (id)initWithLineString:(WGCoordinate *)coords numCoords:(int)numCoords attributes:(NSDictionary *)attr;

/// Construct as an areal with an exterior
- (id)initWithAreal:(WGCoordinate *)coords numCoords:(int)numCoords attributes:(NSDictionary *)attr;

/// Add a hole to an existing areal feature
- (void)addHole:(WGCoordinate *)coords numCoords:(int)numCoords;

Just gather your WGCoordinate structures into an array and pass them in for linear or areal.  For points, just pass in the one and if you need to create holes (in an areal), call that method after you've created the areal.

Vectors From Shapefiles

Lastly, you can read vector data out of Shapefiles.  I wrap these things in a database-like object so I can do (somewhat) fast lookups.  The object you want is called a MaplyVectorDatabase, but it's only in the source tree at the moment.  I haven't updated the pre-compiled Component.

I'm exposing functionality as I need it, so the MaplyVectorDatabase object just does the following for now.
                    
/// Construct from a shapefile in the bundle
+ (MaplyVectorDatabase *) vectorDatabaseWithShape:(NSString *)shapeName;

/// Return vectors that match the given SQL query
- (MaplyVectorObject *)fetchMatchingVectors:(NSString *)sqlQuery;

/// Search for all the areals that surround the given point (in geographic)
- (MaplyVectorObject *)fetchArealsForPoint:(MaplyCoordinate)coord;

Maply?  Yes, Maply.  All new functionality is Maply, with WhirlyGlobe reserved for things that are globe specific.  Surprisingly, that's very little.

Anyway, the basic idea with a MaplyVectorDatabase is that you create it with that shapefile name and then run simple queries against it.  I've exposed the fetch matching vectors and fetch areals for point (e.g. point in poly search).  That's enough to reimplement the WhirlyGlobeApp, but I'm sure I'll expose more in the future.

As with the WhirlyGlobe API, the Component is going to build cache files next to your shapefiles.  There will be one name.mbr and one name.sqlite file.  If they don't exist, the toolkit will build them and that'll be sloooow.  So grab them out of the simulator and stick them in your bundle.

Displaying Vectors

Once you've got some vectors, you can display them.  As always, bigger batches are better, smaller are slower.  The call to add vectors to a WhirlyGlobeViewController is just like so.

WGComponentObject *compObj = [globeViewC addVectors:[NSArray arrayWithObject:wgVecObj]];

Vectors are then eligible for selection and they'll turn up in your globe view controller delegate like everything else.  They show up after labels and markers in priority, as their search is run last.

Upcoming Stuff

There's a lot more in the pipeline for WhirlyGlobe, Maply, and the Components.  Most of my time is devoted to WhirlyGlobe/Maply projects for clients and they're all spinning stuff back in to the toolkits.  Some of it is structural, such as OpenGL ES 2.0 support and some is pure features, such as a Shape layer.

Coming up next is WhirlyGlobe 2.1, which will be a minor release of what's in the master branch.  Primarily tweaks for retina, caps for the Spherical Mercator layers, and random bug fixes.

6 comments:

  1. I am new to iOS and i am trying to make an application like your's whirlyglobe using your framework, I've been having problems trying to put my own map on the globe. I used a tiled map from Mapbox still when executing in XCODE it said image not found.. Can you please help me?.

    ReplyDelete
    Replies
    1. it is also showing file not found error, :whirlyglobecomponent.h not found.

      Delete
    2. You need to set up the project with the right pages.
      Check out this post for details one the header path and the right libraries to link in:
      http://mousebirdconsulting.blogspot.com/2012/08/whirlyglobe-component-announcement.html

      Delete
  2. Hi Steve

    I've been using WhirlyGlobe for a few months now and it's awesome.

    I am trying to resolve an issue with it which I've been "working
    around" for a long time which is that I can never get Xcode to
    resolve the header files for the framework, unless I supply the
    entire path of

    WhirlyGlobeMaplyComponent.framework/Versions/A/Headers

    rather than the generic

    WhirlyGlobeMaplyComponent.framework/Headers

    framework file.

    The problem is reproduced for me by taking the following steps:

    1. Download WhirlyGlobe framework binary distribution
    2. Unzip it
    3. Open WhirlyGlobeComponentTester.xcodeproj
    4. Try to compile it
    5. Result: TestViewController.h > Lexical or Preprocessor issue >
    'WhirlyGlobeComponent.h' file not found

    I have been through ALL of your blogs and build instructions
    several times and STILL can't get it to work!

    Any pointers would be very gratefully appreciated,

    Many thanks

    Andy

    ReplyDelete
    Replies
    1. Thanks for the bug report. I'll take a look at this.

      Delete
    2. It sounds like the link isn't being set up when you extract the archive. The Headers directory is just a link to Versions/A/Headers.

      I just verified that it's correctly linked in the 2.3 binary distribution. If you get a chance to try 2.3 it would be interesting to know if you run into the same problem.

      Delete