Customizing Maps with Overlays

Session 127 WWDC 2010

The Map Kit framework lets you embed annotated maps directly into your app. Additional support for draggable map annotations and map overlays provide further ways to enhance your app's map functionality. Learn how to reposition annotations and use overlays to layer information such as bus routes and weather maps to present truly customized location-based information.

All right.

Good morning everyone.

[ Applause ]

Welcome to Customizing Maps with Overlays.

Thank you.

I'm James Howard, I'm a software engineer on the Map Kit Team, and I'm going to show you in this presentation today how to display geographic data that covers an area on top of your map.

So we're going to start with just a quick review of Map Kit so we see where we came from, and then we're going to go into what's new in Map Kit, which is, primarily, overlays.

And for this session I've prepared five demos.

At this time I'd like you to go to the session website, so if you go into session list and follow it down to this session, you'll see there's a list of the sample code on the bottom.

If you can, and you have your laptop, I'd encourage you to go download that sample code so you can follow along.

And I think the sample codes is going to be really useful to you in your development.

So get a hold of that right now.

So while you're downloading that, let's do a quick review.

What is Map Kit?

Originally, it came out in iPhone OS 3.0.

It lets you put a map in your application.

You can programmatically control that map.

So have it pan around.

The user can interact with it by pinching or panning, double tapping, two finger tapping.

You can annotate the map however you like.

So you can put pins or other annotations, custom views, on top of map.

And finally, there's reverse geocoding.

So reverse geocoding is, you have a point, so a latitude and longitude, and you want to find out what is the address that corresponds to that.

So that's reverse geocoding.

So I've put together a couple of videos here illustrating these points.

So there's pan and zoom; annotations; user locations, so you get that blue gem representing the user's location so we integrate with Core Location to do that.

And then there's reverse geocoding, so we dropped pin at that point and then figured out what is the address that corresponds to it.

So you can do that all last year.

Let's move on to what you can do this year.

Overlays. So this is you have an area and you want to draw something in it.

An example of this is like a transit map or a traffic overlay or national park overlay or a state or local boundaries, any of those sorts of things weren't really possible to do with Map Kit before, now they are.

We also have draggable annotations, so it's much easier to put a pin on the map and allow the user to press and hold and then drag that thing around.

So that's really nice.

We also have a handful of new delegate messages that make it easier to keep track of the user location, as well as when your annotations are selected and deselected.

So that's what's new.

So on to annotations and overlays.

So if you're familiar with Map Kit, if you've programmed with it before, you know about annotations.

They represent a single point.

So we have this pin here stuck in California.

It corresponds to a single latitude and longitude.

New in iOS 4 is overlays.

So I've put an overlay here that represents the state of California, so it's this [ Applause ]

Yeah, it's great.

It's really great.

It's even better when you see it in action, which I'm going to get to in just a moment.

So yeah, it's covering this area, so as you pinch on the map, it tracks the map and sticks with it.

So it's really a very seamless experience.

So that's overlays.

So we have a handful of built in overlays, so let me just run through those.

There's circles, so you have a center and a radius.

This is good for drawing.

You can draw things like you have an accuracy or just any sort of radius that you want to draw about some center, so circles.

There's polylines, so if you have a route or a path, a list of points, you can draw a polyline.

Polygons. I have the polygon outlining the Presidio, in San Francisco here.

We also support polygons that have cutouts in them, so Arlington, Texas is an example of a place that a polygon has a cutout.

So we do that as well.

And then finally, there's custom overlays.

So you can really draw whatever geographic data you want on top of the map.

So this is an example of earthquake danger in the United States that I've drawn on the map.

So layer ordering.

So initially when you put an MKMapView in your application, you have the base map.

So this is base map imagery, and then above that go any overlays that you add so they sit right on top of map, and then above that are any annotations.

So the thing to take away from this is your annotations are always on top of your overlays are always on top of the base map.

So how do you add an overlay to the map?

Just like with annotations, you must add in model objects.

So we have model objects corresponding to all our overlays.

We add those to the map initially using addOverlay.

So in this example we have a circle, we set it with a center coordinate and a radius, so this is describing where in the world this circle lives.

We add that to the map.

That doesn't necessarily put it on screen.

It's only when the map decides, "OK, this thing is getting close to being in view, or it's now going to be in view," at that point, we're going to call back to you on your Map Views delegate, and you're going to get viewForOverlay.

So at that point we're going to create the corresponding view, so because we put an MKCircle in there, we need an MKCircleView, customize how that looks, so let's make it red, and return that.

So let's do a demo.

So with my first demo, I'm just going to show you the very like most basic use of this.

So let me build and run.

All right.

So I've got the Presidio there, and I've got a little walking route actually from here at the Moscone Center over to Golden Gate Park.

And what I want to show you is, as you use your pinches on this and zooms in, you get very nice, seamless zooming.

It's as though it's a part of the map.

So it's not like it's removed and then redrawn, it's as though it's part of it, and if you were to double tap and zoom in you get a seamless cross fading with your overlays.

[ Applause ]

Yeah. It's nice, huh.

[ Laughter ]

And also, as you zoom in and pan along these things, they reveal.

So it's really as though it's a part of the map.

It's a very seamless experience.

So what's the code necessary to do this?

Let's hop into the view controller here really quickly.

Let's hide the implementation of viewForOverlay for a moment, just look at adding overlays.

So I have a list of points comprising that Presidio.

So I create a polygon corresponding to those points, and then I simply add it to the map.

And I do the same thing for the walking lines.

So I create a polyline for that, add that to the map.

So now I'm registering these two overlays with the map.

It doesn't mean they're going to be displayed right away, just that the map is aware that they live in that part one of the world.

And then, finally, I'm going to position the map so that those two things are both visible, and at that point, since the map is being positioned so they're visible, we're going to need views.

So viewForOverlay is going to be called, and then we're going to inspect, "OK, for each of these overlays, what kind is it?"

If it's a polyline, then we need a polylineView.

So we do that.

Return the corresponding polylineView.

And if it's a polygon, return the corresponding polygonView.

So just one more time.

Build and run.

And there we have them.

So I've got one more demo I want to show you before we go back to the slides, and this is a CAML Viewer.

So we don't have CAML viewing built into Map Kit, but I have this nice little sample.

And you can take this and use this in your program.

So what is CAML?

CAML is XML file format for describing geographic information.

I have a couple of CAML files here.

Let me just pop one of them open, you can see what it looks like.

So this is a route between Apple's campus and Antonio's Nut House in Palo Alto.

It's a bicycling route that I actually exported from Google's website.

And this CAML view, let me just build and run it.

[ Building code ]

Oh, that's not the one I wanted to show.

Let's just switch this route.

So there we go.

We're displaying CAML, and I've got the way points along here, and the route that we're showing.

So I think that's a pretty nice way to display CAML, and if you've downloaded the sample code, you'll be able to use it to display CAML in your own programs as well.

So just really quick, let's look at the interface for this.

You can parse a CAML file at a URL or a path, and then you're going to get a list back of all the overlays and all the points, or all the annotations in that.

And so how we use this in the view controller is, we parse that CAML file in our bundle, so you already saw me parsing a different one, and I just switched over to this one.

Parse that, then add all the overlays, add all the annotations, fly the map to an area that covers all those points, and then finally, when we need to get a view for an overlay or a view for an annotation, we just proxy that over to the CAML parser.

So this is a nice way to display some basic CAML files in your applications.

Let me just switch that back to the other one.

This is this other CAML file is a running route that I got from our Map Kit Evangelist Mark Malone who's sitting over there.

He did a run, and he has a little, like, runner's GPS, and so it exported this CAML file, so this is a run around a park in Japan.

So pretty neat.

Yeah. So that's the CAML Viewer.

[ Applause ]

So just to summarize: What is the process for adding overlays?

We take the model object and we add it to the map, and then when the map is ready, it's going to call us and ask us for a view, and we provide the corresponding view.

So very similar to the process for adding annotations.

All right.

So let's move on to the meat of it: Custom overlays.

So if you want to implement a custom overlay, you need to have a model object that implements the MKOverlay protocol.

And the property that you need to implement for this is the boundingMapRect.

So you need to tell Map Kit, what is the bounds of this thing.

Where does it live in the world?

So you return a boundingMapRect.

So this is a new type, MKMapRect.

I'm going to describe what this type is, but it's a new coordinate system for Map Kit in iOS 4 and that's the Core Net system you're going to use to return the boundingMapRect.

And then for the view, you're going to draw just a part of the view at a time, so Map Kit's going to say, "OK, draw draw this MapRect at this zoom scale."

So you're not necessarily drawing the whole thing.

If you think about think back to that example of the state of California, the outline of California, if you zoomed all the way in, that thing would be enormous.

It would be thousands perhaps millions of screen points that it's really consuming if you were to scroll along the length of that.

So we can't draw that all at once.

You can't make a view that's that big.

So we cut it up into tiles, and we draw it just the tiles corresponding to the area that we need.

So that's drawMapRect.

And there's one thing you need to know.

So when I said we're introducing MKMapPoint and MKMapRect as a new coordinate system, you need to know about the map projection that we use.

So in Map Kit we use the Mercator projection to map the 3D sphere of the Earth onto a 2D map.

And this has a property that, as you go towards the pole, the map is stretched in the latitudinal or the y direction.

So this image up here sort of illustrates that.

These circles are all the same size; they're all 500 kilometers in diameter or 250 kilometers in radius.

And they're all spaced equally; they're all 16 degrees apart.

So how do you take advantage of this?

You need to use MKMapPoint and MKMapRect to convert your latitudes and longitudes and your distances into map points, which you can then draw, so you get you'll get a nice overlay that corresponds to the base map.

Because that's really what you want to do.

You don't want to just draw something that's not going to line up with the base map.

This is real geographical data.

It corresponds to somewhere in the world, and so it needs to line up.

So how do you do this?

You use MKMapPoint.

So your overlays must be drawn using these.

And they can represent any point on the map, and they have a linear relationship with screen points.

So whereas latitude and longitude don't have a linear relationship with screen points, MKMapPoint does.

And MKMapPoint is a double precision type, so it has sufficient precision to represent anywhere in the world.

And you just use MKMapPoint for coordinate to convert your latitude and longitudes into map points which you can then go ahead and draw.

So one thing else I want to talk about is gridded data.

So I'm going to show a demo in just a minute of earthquake hazard data.

I showed that just as a screen shot earlier.

And this comes from the U.S. Geological Survey in a regular grid, so it's every .05 degrees of latitude and longitude they have a square, and say, what is the earthquake hazard likelihood in that square.

So if you think about that it looks something like this, like, you know, you can see in the middle here, this is a high hazard area, and then around that it's not.

So in this case these are 10 degree square.

What does that look like on a sphere?

So depending on what angle you're looking at the sphere of the Earth at, it may even look like a trapezoid.

So even though it was a square in terms of thinking about it in latitude and longitude, it's not necessarily a square depending on how you look at the Earth.

And it's not drawn as a square on the map, it's actually a rectangle, but by just converting the corner coordinates to map points and then creating a MapRect out of that, you'll get the correct MapRect that corresponds to the area that you're trying to draw.

So let me show you a demo of how this works.

[ Searching for demo ]

There it is.

So let me just build and run this so you can see what it looks like.

So I downloaded this off of the U.S. Geological Survey website, and it's that .05 degree grid, so you can see that in California here, we have a high earthquake likelihood.

This is this is the edge of the data, it's just the continental United States, so you can see there's sort of a sharp line there.

I assume that out in the Pacific Ocean they also have a high earthquake likelihood, but the U.S. just doesn't sample that, so this is what we're displaying.

And this is made up of tiny little squares of latitude and longitudes.

Those things actually become rectangles when they're MapRects.

So let me just show you my model object.

So this is my object that's implementing MKOverlay, and there's a bunch of coding here.

There's I think a little over 600,000 earthquake samples in the data file that we're reading.

But when we actually get down to an individual one, we're going to have, basically, an upper left and a lower right in terms of coordinates, so these are CL location coordinates 2D, latitude and longitude.

And we just need to convert that into a MapRect, so we find the upper left and the lower right in terms of map points, and we convert that into a MapRect, and so that's the thing that we're actually going to be able to draw.

So if we flip over to the implementation of the view, we're going to call that method, and so this is the method that goes in there.

There's a bit of code for doing a little nearest neighbor sub-sampling, because when we're zoomed way out, we don't need to display all 600,000 samples, we can display a little less and still look fine.

But basically what it's doing is the code I just highlighted.

So we get that list of earthquake values and their boundaries, and then for each of those, we can just loop over them, so we have a boundary, and we have the color that we need to color that.

And at that point, now that we have as a MapRect, we can just convert it into local coordinates, so this is a linear relationship between our MapRect and our local coordinates, it's just offset a little bit, and at that point, we have a CGRect which we can finally draw.

So that's how you would go from latitude and longitude to map points to a Rect that you can finally draw.

And just to show you in the view controller how this is used, it's just like with the built in overlay.

We create our hazard map which is a model class, we add it to the map, and then provide the corresponding view.

So let me just run this one more time.

And this time what I want to do is I want to zoom in really close in on this, so .05 degrees seems pretty accurate, but you can actually zoom in way farther than a .05 degree resolution map because you actually start to see look, here's the actual rectangles in that data they've sampled, and what you can see is they're not precisely squares anymore, they've become rectangles when they're projected on the map.

But the nice thing is because we're doing this, we're converting the map points, we know that this area right here, this is really what they meant when they said, this is the sample that corresponds to that, so our data that we got from the USGS is lining up with our base map.

So that is the earthquake hazard map demo.

So we'll quit that.

[ Applause ]

Thank you.

[ Applause ]

Close this.

And let me go back to the slides.

So subclassing MKOverlayView, that's the first subclass I showed of MKOverlayView.

There's a few things to know about doing this.

The most important of which is drawing is asynchronous, so that drawMapRect method is coming in asynchronously.

In that demo it was pretty simple because the data that we loaded was it was fixed, it didn't change over time.

So we loaded off of disk, had it in memory, and it's not mutable.

So that's nice, we don't need to do any locking on that data.

But if your data does change, you're going to need to protect it with a lock because you drawing is asynchronous.

There may be one thread calling drawMapRect, there may be two threads, there may be multiple threads, but none of those are your main application thread, they're a background thread created by Map Kit, so you need to be aware of that.

I use the Core Graphics drawing methods as you saw in there to draw, so you're given a CG context to draw that into.

The UIKit graphic context is so is not automatically available, so if you using any of the UIKit drawing functions, you need to push that graphics context using the UIGraphics content UIGraphicsPushContext.

I recommend just using the Core Graphics drawing API, but if there's something in UIKit that you really want to use, and you know it's thread safe, go ahead and use this to make that available.

And then the other nice thing is, there is what you've already drawn are cached, so as we zoomed in and panned around on that map, what we've drawn, so the list of rectangles, colored rectangles that we'd already drawn, when we pan back to them, they don't have to be drawn again, it's stored in the cache for you.

You can invalidate that using setNeedsDisplayInMapRect, but do so sparingly.

If it hasn't changed, there's no point invalidating, you want to keep that cache around as long as possible.

So mutable overlays.

I know a lot of people have asked on the forums or in bug reports, "How do you do this?"

Well, as you know, or I'm telling you now, all of the overlays in Map Kit are immutable, they don't change with time.

So once you've created one and add it to the map, it doesn't change.

But you want to do it.

Right? Well, I know at least some of you want to do this.

So to give mutability, you're going need to build your own custom overlay.

Fortunately I've got sample code of available for this session that does it, and we're going to get into that in just a moment.

And as I mentioned, we're only going to want to update the part of the map that's changed when we when we mutate the data.

And finally, because drawing is asynchronous, so drawMapRect comes in asynchronously, if you're updating from the main thread, you need to protect your model data with a lock.

So let me show you a demo of a mutable overlay.

[ Loading demo ]

So I call this the "Breadcrumb Sample."

It's an app for Hansel and Gretel.

And they're going to they've upgraded, they've got iPhones now, so they don't use breadcrumbs anymore.

And they've also upgraded they don't walk, they have a BMW, and they're driving up the 280 here, and they're leaving a nice trail behind them.

So this is accomplished by using a mutable overlay.

And I've called that the "Crumb Path."

So to show the implementation of this.

This is my model object, it implements MKOverlay, and it's storing a list of points which can be modified and is protected with a read-write lock.

So when we get our first location update, we're going to create it about that center coordinate, and, then, for each subsequent location update, we're going to add a coordinate.

And so at that point we're going to need to acquire the write lock.

And then when we're drawing, we'll get the read lock.

So we may have multiple drawing threads.

They can all draw simultaneously as long as they're holding the read lock, and so then that read lock protects this list of points.

So let me show you just in the view here.

[ View running ]

We're going to acquire that read lock.

We're going to create a path based on those points, and then we're going to let go of the read lock, and go ahead and start that path.

So there's one method in here this is nice helper method that you'll be able to use in your own programs independent of this even, and this is this Create Path for Points method.

This guy given a list of points and a clipping of MapRect and a map zoom scale, this will allow you to create a CGpath that is the fastest way to draw that path.

So this is it's doing two things.

One is it's clipping.

So it's saying, "Look, if we have 5000 points that we're trying draw, and there's only 3 points that are in this MapRect, we only need to draw those 3 points."

And then it's also doing simplification.

So if you have 5000 map points again, and you're trying to draw them into an area that's 100 screen points and we know that because we're given the zoom scale, and we know the zoom scale has a linear relationship with screen points we can say, "Look, we don't need to draw 5000 points into 100 point area.

We can do some simplification, we can skip some points.

And get something that looks just as good but draws much quicker."

So there's there's this nice method that you'll be able to incorporate into your own programs.

And, then, just to flip over to the implementation of the view controller, so we're using as I mentioned earlier, we have a new delegate callback for when that blue dot updates.

This is going to be getUpdateToUserLocation, and when that comes in, we're going to look at the user location, and if it's the first one, we're going to create our crumb path, so this is our MKOverlay of model object, and we're going to add that to the map.

And we're also going to zoom the map to show the user's location.

And then on subsequent updates, we're not going to move the map, but what we are going to do is we're going to add a coordinate to that crumb path, so this is going to acquire that write lock, add it to the list of points, and then it's going to release the write lock when it's done, and it's going to give us back an updateRect, so this is this is just had area that's changed.

And if it's not null oops, if it's not null, then what we can do here's another nice one liner.

We can find the current zoom scale, figure out how wide we draw that line at that zoom scale, and we can just outset our updateRect by that, and then now let's just update that invalidated area.

So if we're going to build and run this again...

So what's nice is, this guy's moving along, and it's exposing a little bit more of that line.

We're only redrawing just a tiny little part of the screen.

We don't have to redraw all of this every time that updates, so it makes it much more efficient to do it this way.

So that's how you do a mutable overlay using Map Kit.

[ Applause ]

So what did we learn in that demo?

You can do a mutable polyline.

And the thing to take away from this is, I used a single custom overlay rather than adding and re-adding a polyline.

I know, I'd seen some bug reports and, like, some forum posts.

People were, like, "Well, every second, I'm going to create a new MKPolyline.

I'm going to remove my old one, I'm going to add a new one."

That's not very efficient.

It's much more efficient to use the scheme that I've shown in the sample.

And the data has to be protected with a lock.

I'm using read-write lock because it's very convenient for this sort of data.

DrawMapRect is asynchronous, so you need to protect your data with a lock because you're updating it from the main thread.

And finally, simplify and clip to your list of points.

Core Graphics will do clipping for you, so when you're asked drawMapRect, there is already a clipping boundary set up, but it's a lot faster if you just avoid creating such a large path in the first place.

Yes. Raster map overlays.

So I know some people are interested in doing this.

This is you have an image, and you want to display it on top of the map, so it may be available as a PDF, or you have it as a GeoTIFF or just a big image.

Perhaps it's on a piece of paper and you need to scan it in.

But you want to display your own maps, so this may be your college campus map or your national park map or something like that, and you want to overlay that on top of your map view.

So one thing to know about this is, your map has to be in the Mercator projection.

We only have the Mercator projection in Map Kit, so if your map's not in the Mercator projection, it has to be warped.

Fortunately, there's a nice tool that's available that will let you do this.

So you can warp your image if it's not already in Mercator projection.

And your image is going to be cut into tiles.

So if you have an image and in my example I'm going to show in just a minute, I have an image that's like 120 MB image you're not going to load 120 MB image into memory on an iPhone.

It's not going to fit.

So it needs to be cut into tiles that are small enough, little bite-sized tiles that we can just load those individually and display just what we need.

And finally, the tiles they can be loaded another from your applications bundle or you get them off of the network.

In the example I'm going to show, we're going to load them from the applications bundle, but you can do it either way.

So this is the map that I want to put on top of Map Kit.

It is a nautical chart of the San Francisco Bay.

I got it from the National Oceanic and Atmospheric Administration.

And nice nautical chart here.

And this thing's going to need to be cut into tiles.

So there's a utility that does this, and the output that it creates looks a little bit like this.

So it has it corresponds to zoom levels.

So at zoom level 10, it's just a tiny little guy.

Zoom in, it gets twice as big.

Zoom in again, it gets twice as big again.

So it's going by powers of two.

And so what you want to do is, there's this great open source project, Geospatial Data Abstraction Library.

I encourage you to download that.

It's in MacPorts, so you can just do "port install gdal" if you use MacPorts, and get this thing installed.

And it comes with a great utility that someone did, I think as some Google Summer of Code project last year.

It's called GDAL2Tiles, and you put your image in it, it will warp it to the Mercator projection if it's not already in a Mercator projection, and cut it into tiles.

And then finally, it's going to it's going to export tiles in a directory structure, and you can use those things with if you're already using, like, Google Maps JavaScript API on the web, you can use those tiles with that.

If you're using Microsoft's Virtual Earth JavaScript API on the web, you can use the tiles with that.

And now you're going to be able to use tiles with Map Kit on the iPhone.

So this is a really great utility, I recommend using it.

So let me show you demo of that nautical chart on top of Map View.

[ Demo running ]

Tile, Map.

[ Demo running ]

So there's our nautical chart overlaid on the map, and so it's semi-transparent.

You can zoom in and see that the thing increases its detail as you zoom in.

You can pinch, and it tracks the coastline precisely as you go.

You can actually even see ships navigating this chart.

They're a little bit blurry, but this is the satellite photo, you can see there's ship there.

It's pretty cool.

Zoom out. So one of the neat things about this demo is you can actually if you were to download this sample code, and you run GDAL2Tiles on your own program, this tiles directory in the resource port resources here, just delete that and put your own tiles directory, and now you have your own raster map on top of map kits.

So it's - no code to modify it.

[ Applause ]

Yeah. That's pretty neat.

I just want to show you a little bit about how this works, though, in case you do download this code and you want to extend it, go further with it.

So let me just show you the model class.

So this is a tile overlay model class.

It scans a directory of tiles, so scans that directory of tiles out of the resources directory; figures out what is the boundingMapRect for all of those tiles; and then for the view, it's able to return a list of tiles for a given MapRect because we're going to get drawMapRect, get the MapRect in the ZoomScale, we need to know what are the images that correspond to this.

And so we're going to need to get those.

And there's one really useful function in here that I want to call your attention to.

Most of it is just details with reading files and so on, but there's let's see where did I put that thing?

Ah, I put it right here.

ZoomScale to the zoom level.

So if you've used Google Maps JavaScript on the web, or you've used Virtual Earth JavaScript API on the web, you're familiar with zoom levels.

So we don't use zoom levels in Map Kit, we use ZoomScale.

ZoomScale is nice because it does have that linear relationship between map points and screen points, so that's why we use it.

But if you have data that's using Zoom Level as these raster tiles do, you can convert between Map Kit ZoomScale and the Google or Microsoft convention of zoom level.

So this is a really nice little function that you can use in our own programs to do that if you need to do so.

And then in the overlay view, let me show you what this guy looks like.

So when you're asked to draw a MapRect, it may not exactly correspond to one of your tiles, it may be a list of tiles.

That's OK.

We're getting back this list of tiles as an array, and we're going to loop over all of those things, and then we're just going to find, OK, what is the image for it, what is the Rect that bounds it, and draw that image into that Rect.

One thing I noticed was, on the sample code that I initially uploaded, and, I think, which is available on the session site right now, doesn't have this Image Release at the end here.

You're going to want to add that, otherwise there's going to be a leak.

So I'm sorry about that but, yeah, add that.

And then let me just show you the view controller here.

It's pretty straight forward.

We just initialize with our tile directory, create our overlay model object, and add it to the map.

And we'll zoom the map just so that the tiles are visible.

I zoom in one zoom level past the one that just bounds it.

And then we Create and Return to View and View for Overlay, and I set the tile alpha, so this is really the only property on the view is the tile alpha, so it's 60% opacity.

You can make it fully opaque or semitransparent if you like, whatever works for you.

So that's that's the Tile Map demo.

[ Applause ]

Let me just run that one more time.

It's cool.

[ Laughter ]

It's nice.

[ Laughter ]

[ Applause ]

So back to the slides.

What did we learn from this?

The image has to be cut into tiles, use GDAL to do that; you can convert your ZoomScales to zoom levels, so if you have zoom level and you need to get ZoomScale or vice versa, you can set up a mapping between those two; and you don't necessarily need to have an image that is so detailed that you cover every zoom level that Map Kit does.

So my image, even at 120 MB, and I think about 20,000 pixels on a side, still isn't enough to zoom all the way in or zoom all the way out, but that's OK.

It will just show up when the map is positioned in the right region; and the Map Kit tile size is independent from your tile size, so you may be using 256 pixel tiles, that's what the GDAL2Tiles program outputs.

Map Kit might be using a different size, but that's OK, you just draw the according number of images that you need into that space.

So loading your tiles on demand.

That sample doesn't load its tiles on demand.

It loads them out of the applications mode, but you can load them on demand, and, in fact, we've built in some facility to MKOverlayView to make this practical, so loading network resources and then drawing them as needed.

And the key thing to know about this is canDrawMapRect.

So canDrawMapRect is a method that you can over ride in your MKOverlayView class.

And when you get this, this is an indication to you that a tile or a list of tiles corresponding to that area is needed.

It means Map Kit's interested, so you should go off and go to your web server and fetch that data.

And what you're going to is while you're fetching the data, you're going to return No, and say, "I can't draw that right now."

And so we're not going to bother calling drawMapRect, we're just going to wait for you to go ahead and download that data.

Then once you have the data, you're going to call setNeedsDisplayInMapRect and that's going to indicate, OK, things have changed.

We're going to reevaluate.

You're going to get canDrawMapRect again, and this time you're going to return Yes because you have the data cached locally, and then you're going to get drawMapRect, and you're going to have a chance to draw your own data.

So there's a couple of things to know about this.

You want to coalesce your request to your web server into a fixed size stack.

If the user pans really quickly, they could pan from, you know, San Francisco all the way over to Idaho, and there's a lot of intermediate tiles that were passed by, but they were on screen for just a fraction of a second.

You don't want make a request to your web server for those.

So if you coalesce your tile request and go to fixed size stack, and then fire those off to your web server on a short timer, say like 0.1, 0.2 second timer, you can avoid drawing things or requesting things that the user didn't really want to see.

The most recent request is the most important, so use a stack not a queue, right.

If they pan over to Idaho, they want to see tiles in Idaho, they don't want to see things on the way in Oregon, for instance.

And, yeah, the old request may no longer be visible.

It's off screen, they don't care anymore.

And your stack size should be roughly twice the size of the number of tiles needed to cover the screen.

Why twice the size?

Well, in Map Kit you can pinch.

We don't have tightly delineated zoom levels.

You can pinch so that things are just sort of scaled down, and if you're right at the boundary, then you're going to need about twice as many as you would expect to cover the screen.

So that's why you use 2X.

And when a request falls off the back of the stack, so let's say you decide, "Oh, my stack should be 50 items deep," and you get to the 51st item, call setNeedsDisplayInMapRect again, and when you do that, it doesn't necessarily mean you're going to get a drawMapRect right away, it just means you want to get another canDrawMapRect, and so the time the user does pan back to that area, we're going to reevaluate, and then at that point, we may consider actually downloading and then finally displaying it.

So that's what you need to do to load your tiles on demand, and that pretty much wraps what I have to say about raster tile overlays.

For more information, you should send an email to Mark Malone, that's his email address there.

We have great documentation.

It's been fully updated for Map Kit on iOS 4.

I encourage you to read that.

And the Developer Forums, I've been hanging out on there, the other Map Kit engineers have been hanging out on there, so pop in there and post your questions.

So in summary: What can you do with Map Kit overlays?

You can add your lines and shapes to the map.

And with custom overlays you can really draw anything you want.

And so I really hope to see everyone who's been using Map Kit and has wished that they had had this in their applications, I'd like to see you update your applications to take advantage of overlays for iOS 4.

Thank you very much.

[ Applause ]

Apple, Inc. AAPL
1 Infinite Loop Cupertino CA 95014 US