The iPhone has plenty of neat features to use, one of the more recent features is using the built in Google Maps support. The specific SDK library we are looking at is MapKit.
The application we are looking at building today is going to display the last 300 earthquakes from around the world - data pulled from USGS. The events are shown on the map, with larger earthquakes being displayed larger in size and a darker color. This shows off putting custom annotations on the map with a custom drawn view for each. Below is a video of the application in action. When it loads it will request the data and update the map.
To get moving on this application, I setup a basic View Based Application (named EarthquakeMap in my case), opened the view nib in Interface Builder and dropped a Map View on it. Back in XCode we need to add a reference to MapKit.framework, which should show up by default in the list to the right - for reference
/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator3.0.sdk/. This completes all the quick setup.
Diving into the code, we need to add to our view controller header an IBOutlet for the map view (typed
MKMapView) - don't forget to add an import for
MKMapViewprovides a delegate protocol, therefore at this point we declare that our view controller will implement some of the delegate,
MKMapViewDelegate. To finish the header file we add an
NSMutableArrayto hold our collection of seismic events that we build from the data we get.
Make sure to jump over into Interface Builder and hook up the
IBOutletto the map view and the delegate of the map view to the view controller,
One item we need to build is a value object to hold the data about our seismic events. This is a pretty normal object, the only thing specific for this is that we are going to implement the
MKAnnotationprotocol. This protocol defines properties for an object that wants to be used as an annotation on a map. There is one property we want to implement which is
CLLocationCoordinate2D. The header for our object looks like the following.
The implementation file isn't much more, the only thing we do is make sure to return a
CLLocationCoordinate2Dmade up of our latitude and longitude for the getter of
coordinate. I also dropped in the
descriptionmethod for making debugging easier.
The next task is going out and getting the data from the USGS site. I should mention that I have no idea if you're allowed to use the data I am pulling for external applications or not. I simply found the file link and used it. The data comes in way of a comma separated file from the url http://neic.usgs.gov/neis/gis/qed.asc. We jump into the implementation file for the view controller and work inside
viewDidLoadfor loading the file. Below is the entire method, I will go over the code right after.
At the top we first build an
NSURLfor the page and then pull in the information into a string. We then need parse the information, this is going to be done with a combination of
NSScannerand separating the string using
NSString. Next up: initializing scanner, events collection, and declaring some variables. Then, we need to loop through the file, using a
whileloop checking if the scanner has hit the end of the file. Using scanner to grab the string for an entire line we check to make sure it's not the first line (headers). If it isn't the first line we chop up the string at the commas. With those values we create a new
SeismicEventand set the appropriate properties on the object, pulling out the correct value for each. The object is then added to the array of events. Still inside the loop we check if we have added 300 and if so break out - we don't want to overcrowd the map. The last thing done in the function is we add the events as annotations on the map.
If everything is correct you should get something like the image below, where there are a ton of pins all over the map showing where earthquakes have occurred.
That is pretty cool, especially with the amount of code we have written so far. But what would be cooler? Well, showing the magnitude of the earthquake by changing the pin to a circle that gets larger and more red with increasing magnitude. Ok, so to do this we take advantage of one of the delegate methods on the map view delegate. The method we are looking at is:
The method lets us use a custom view for an annotation. The view that is returned from the method has be a
MKAnnotationViewor a view that extends it. We are going to build a custom view that extends
MKAnnotationView- I named mine
EarthquakeEventView. The only thing in the header for this is an instance variable named
eventthat is a
SeismicEventwhich is going to be set when the annotation is set. The complete header code is below.
Jumping over to the implementation file the first thing to do is override the init function,
initWithAnnotation. In the method I call the super and simply set the background color to clear or transparent - this is important because it allows us to draw semi-transparent graphics and allows the view to have alpha. The next method in our file is going to be to override
setAnnotation. This is where we grab the
SeismicEventas it comes in and set it to our instance variable. We also set the size of our view as this point to be a height and width of our magnitude squared times 0.75, this makes it a non linear sizing algorithm (tidbit: Richter Scale is logarithmic). We also need to override
drawRectwhich is where we actually draw our circle. This is done by grabbing the graphics context for the object, setting the color, and drawing the circle. We start with a yellow color and modify it to be more red depending on the magnitude. Finally, it's nice to override
deallocfunction to clean up our memory. The entire implementation file follows.
To get this view being used we jump back to our controller implementation file and hook in the delegate function mentioned earlier. So, we can go ahead and add the following to our file.
What is going on above is we use
dequeueReusableAnnotationViewWithIdentifierto grab an already created view to make reuse of our annotation views. If one isn't returned we create a new one. Then we just set the annotation on it and return the view. Simple as that. Assuming everything is perfect, don't forget to import the correct headers, you should have a fully working application that shows the most recent 300 earthquakes around the world.