Monday, January 7, 2013

Cylinders, Spheres and Great Circles - 3D Shape Support

It's a new year!  And that means it's time to catch up on the posts I should have done last year.  Let's start with shapes.

A few weeks ago I added support for 3D shapes.  The WhirlyGlobe API support went in first, followed immediately by Component support for WhirlyGlobe and Maply.  The Components are turning out to be very popular, so it's likely this will be the pattern going forward.

Five basic shapes are working now: Cylinders, Spheres, Great Circles, Lines, and Circles.  Cylinders and spheres are fairly obvious.  Great Circles take a height and do a very nice curve above the earth and lines allow you to scribble in 3-space around the globe.

Let's dig in.

Cylinders and Spheres


The base of each cylinder is tied to the earth, with the long axis pointed upward.  Spheres are just spheres with no real directionality.  You place both of these with lat/lon coordinates and you have the usual control over size, offset from the earth, and color.

So much for, what is that? DC?
That snapshot is from the test app and it's pretty easy to pop these things on the globe.  Let's start by looking at the data structures themselves.

                    
@interface MaplyShapeCylinder : NSObject
{
    /// Center of the base in local coordinates
    MaplyCoordinate baseCenter;
    /// Radius in display units (1.0 is the size of the earth)
    float radius;
    /// Height in display units
    float height;
}


The comments are fairly descriptive here.  You specify a lon/lat coordinate (in radians) for the baseCenter and you set the radius and height in display units.  Remember that display units are based on a sphere with a radius of 1.0; everything else is relative to that.
                    
@interface MaplyShapeSphere : NSObject
{
    /// Center of the sphere in local coordinates
    MaplyCoordinate center;
    /// Radius in display units (1.0 is the size of the earth)
    float radius;
    /// Offset from the globe (in display units)
    float height;
}

The spheres are even simpler.  You specify the center in radians (lon/lat) and the radius and (optional) height in display units.

Then you just collect these babies up in an NSArray and pass that on to the addShapes: method in the WhirlyGlobeViewController.  Here's an example where we add a blue cylinder over Washington, DC.

                    
[globeViewC setShapeDesc:@{kWGColor : [UIColor colorWithRed:0.0 green:0.0 blue:1.0 alpha:0.8]}];
MaplyShapeCylinder *cyl = [[MaplyShapeCylinder alloc] init];
cyl.baseCenter = WGCoordinateMakeWithDegrees(-77.036667, 38.895111);
cyl.radius = 0.01;
cyl.height = 0.06;
[globeViewC addShapes:@[cyl]];
[globeViewC setShapeDesc:@{kWGColor : [NSNull null]}];

That's all there is to it.  Remember to add as many things as possible at once.  The system is going to group those together and optimize the rendering.  Oh, and presently, selection is not supported for shapes.

Great Circles


Users have been asking for something like this for a long while.  The idea is pretty simple, but the implementation takes a little work.  You specify a start and end point on the globe, and a height.  The curve will run from the start to the end, reaching that height right in the middle.  So technically it's only a great circle if height = 0.

No, not technically great circles.  Also, shut up.
Let's take a look at how to specify one.  they're actually pretty simple.
                    
@interface MaplyShapeGreatCircle : NSObject
{
    /// Start and end points in geographic
    MaplyCoordinate startPt,endPt;
    /// Height is related to radius == 1.0 for the earth
    float height;
    /// Line width is in pixels
    float lineWidth;
}

The start and end points are in radians (lon/lat) as always.  A great circle reaches that height in its middle and that's specified in display units.  Remember the radius of the earth is 1.0.  Lastly, these are rendered as lines, so you get to specify a line width.

As with the other shapes, you call addShapes: on your globe view controller.  These will respond to the drawOffset and color parameters in the default shape description, so control them that way.

What's Next


Well that's it for shapes.  Mostly.  I didn't talk about lines or circles.  They're in the header file, you can figure them out, though they're not nearly as interesting.

Great things are afoot with WhirlyGlobe & Maply.  Up next are a couple of apps users have published  (without me, very exciting) and a really neat new feature for interactive auto-layout of labels.  That one needs a video, so it might be a while.