In this tutorial we’re going to do something fun and complex. We’ll query buildings from a remote spatial database, hosted by CartoDB. We’ll fetch them much like we do remote image maps and we’ll toss them on some satellite imagery.
Let’s look at the data source.
CartoDB is a cloud based spatial database. Built on top of PostgreSQL with a whole lot of front end logic, it’s a nice way to interact with spatial data sets.
That’s kind of huge, even just for one borough. We can’t display it all at once and it would be rude to query the whole thing. So we need to break the queries down into tiles.
Quad Paging Layer
To keep things small and fast on a mobile device, we must load only what we’re looking at. Easer to say than to do. We can do that for image maps, as you’ve seen from the examples. The MaplyQuadPagingLayer is how we do it for everything else.
Since we don’t really know what’s in a vector data set, what format it’s in, or how it should look, we need you to fill in the details. The MaplyQuadPagingLayer will handle the tiling logic, but you have to actually fetch and display the data. It’s easier than it sounds.
You make a new class that implements the MaplyPagingDelegate. There’s one important call to go get your data and then a bunch of WhirlyGlobe-Maply methods to display it. The MaplyQuadPagingLayer does the rest.
You’ll need a sample project for this tutorial. Go back and start with the Hello Earth tutorial and work up to at least Remote Image Layer. We’ll want that satellite imagery for a background.
If you haven’t got one here is a suitable ViewController file to start with (for Objective-C or Swift).
We’ll create a new class called CartoDBLayer. Its job is to talk to CartoDB and display what it gets back. The toolkit will call for every tile the user can see.
That will create two new files, CartoDBLayer.h and CartoDBLayer.m, or just a simple CartoDBLayer.swift if you prefer.
Here’s what CartoDBLayer header should look like. For Swift, you can see only a small portion of the class:
The interesting part here is the MaplyPagingDelegate. We’ll need to implement a few methods for it like so.
minZoom is the minimum zoom level we’ll support (in Swift, we need getter and setter methods and backing variables)
maxZoom is the maximum zoom level we’ll support.
startFetchForTile:forLayer: is the method that does the actual work.
We’ll be using that NSOperationQueue and filling in the init method later. Let’s’ hook this layer into our app.
Adding a Paging Layer
We want to set up a paging layer in the main view controller and start the viewer near our data. Open up your ViewController implementation file.
In Objective-C, at the top, add a new import.
Now look for where it says “start up over San Francisco”. We want to start over Manhattan instead, zoomed in.
Next, move to the end of your viewDidLoad method and call the following method call, which we’ll fill in shortly.
Put the new addBuildings method near the end of your ViewController.
That SQL query is pretty interesting, but we’ll come back to it later. Here’s how we set things up to display.
Create the CartoDBLayer with the search string.
Restrict the zoom levels to just level 15.
Declare that this data is in spherical mercator, which is normal with web maps.
Create a MaplyQuadPagingLayer with that coordinate system and cartoLayer delegate.
Add the MaplyQuadPagingLayer to the globe or map view controller so it starts working.
That’s pretty much it. The rest of the action is in the CartoDBLayer.
The good parts are in CartodDBLayer class. Yours should be empty, so let’s start with the init method.
We’re squirreling that search string away for later and we promise to explain it soon. We’ll need that NSOperationQueue later too.
Fetching Data Per Tile
The most important method in the MaplyPagingDelegate is startFetchForTile. This is how the toolkit tells us to go get our data for a given tile and display it. Let’s take a look.
We’re only going to get these requests for level 15, since we set our minZoom and maxZoom to 15. So you can avoid thinking about levels. That means a given tile request will come in and its our job to get the data for just that tile.
First, we ask the MaplyQuadPagingLayer what the bounds are for the tile. We’ll need those to construct the query, which we’ll talk about shortly.
Once we’ve got the query from the constructRequest method, we kick off an asychronous call to NSURLConnection to go get the data. We’re expecting GeoJSON back. The connection will call our little code block when it finishes and we’ll be on a thread associated with the NSOperationQueue we created earlier.
Mobile apps are heavily threaded and WhirlyGlobe-Maply is built for it. We can add and remove display data on any thread we like. Sure enough, in that little execution block we turn the data into a MaplyVectorObject by parsing the GeoJSON and we display it in two different ways: (1) as a filled transparent polygon and (2) as a solid outline.
The display version is now created, but we’re not done yet. We still need to tell the MaplyQuadPagingLayer what we did.
That’s what the addData: call is for. The MaplyQuadPagingLayer now knows about those two objects and can delete them when they’re no longer needed.
Lastly, we tell the layer that the tileDidLoad: and we’re done.
Building CartoDB Requests
Let’s circle back to the CartoDB Request. We have the bounding box for a tile and we need the NSURLRequest to go get the data. Add this to your CartoDBLayer class:
This is filling in the bounding box from the tile and constructing a real search string. It’s appending it onto the URL for the CartoDB site and dealing with the encoding issues for characters. Then it returns a usable NSURLRequest, which we need to talk to the server.
Let’s look at that search string.
You might recall we fed in a search string that looked like this.
Okay, this is SQL so it is as complex as it looks. I recommend perusing the CartoDB docs for the full explanation of what’s going on here. But to put it simply.
We’re asking for data from the Manhattan PLUTO table.
We want the geometry (polygons), the address, owner, and number of floors.
But only within the given bounding box.
Oh, and keep it down to 2000 objects max.
We fill in the bounding box in the constructRequest method and pass back a functional NSURLRequest. From that we can fetch the data.
Whew! That was a lot of code for a tutorial. Let’s run the app and see what happens.
If you don’t see that or it didn’t compile, take a look at the code snippets. Or you can just look at the complete files.