Transcript
>> 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 ]