Tuesday, January 15, 2013

New Feature: Auto layout

Label layout is a feature I've been contemplating for a while.  I finally had a client with an urgent enough need to get it done.


Label Layout


It's pretty simple.  You tell WhirlyGlobe how important your screen labels are and it'll do its best to draw them on the screen.

There's a new field in the MaplyScreenLabel so let's take a look.

                    
@interface MaplyScreenLabel : NSObject
{
    /// Put yer user data here
    NSObject *userObject;
    /// Location in geographic (lat/lon) in radians
    MaplyCoordinate loc;
    /// Size on the screen, in points.  In general, set the height, but not the width.
    CGSize size;
    /// Text to display
    NSString *text;
    /// If set, this is the image to use for the marker
    UIImage *iconImage;
    /// Offset the text on screen by this amount.  Defaults to zero.
    CGSize offset;
    /// If set, this color overrides the default
    UIColor *color;
    /// If set, this label can be selected.  On by default.
    bool selectable;
    /// For the label layout engine, this is the importance of this particular
    ///  label.  It's set to MAXFLOAT by defaut, which means it always shows up.
    /// Set it to another value to actually be laid out with constraints.
    float layoutImportance;
}


Yeah, that's getting a little busy.  Anyway, it's that last field: layoutImportance.  By default it's set to MAXFLOAT, which simulates the old behavior (display everything).  All the values are relative, so feel free to set it how you like.  I use a value of 1.0.

And that's all there is.  It's crazy simple to use.  The implementation... less simple.

Implementation


We're only laying out labels right now, but the support is much more generic.  It goes a little bit like this.

The label layer does the heavy lifting of rendering the text, creating textures and handing off geometry to the appropriate parts of the system.  For 3D labels, we create drawables.  For 2D labels, we hand off to the ScreenSpace Generator, a weird little module in the renderer.  Now we also talk to the layout layer.

It's the layout layer that handles (you guessed it) the layout.  After the label layer has created a single label (or more likely 2,000 of them), it bundles up the spatial info and hands that off to the layout layer.  It's up to the layout layer to make its decisions and tell the rendering subsystem what to enable or disable and what offsets to use.

Label layout can get arbitrarily tricky and has been proven to be O(Really Annoying), even if you do a half assed job (cough).  As a result, you don't want to run it every frame: The layout layer runs ever time you stop moving.  It kicks off a dispatch queue, does its calculations and comes up with a list of labels to enable/disable and what offsets to use.  Those are handed off to the rendering portion of the system and the changes show up pretty quickly.

WhirlyGlobe 2.1


This is a big 2.1 feature and it's working in the develop branch on github.  I'll do another binary distribution soon, but honestly, the develop branch isn't that hard to use.  Give it a try.

If you do, I've also turned on OpenGL ES 2.0 support (another big 2.1 feature).  If it causes you any problems (it probably will), go ahead and turn it off like so:

                    
[globeViewC setHints:@{kWGRendererOpenGLVersion: @(1)}];