Thursday, August 26, 2010

Raster Map Overlays for iPhone

The release of iOS 4.0 included the ability to add custom image overlays to maps. I recently took advantage of this in FastTrac. Previously, FastTrac was using OpenGL to handle the map. This worked well but required an unreasonable amount of memory. It also wasn’t easy to maintain. Changing to a map overlay fixed both problems. Session 127 of WWDC10 covered briefly how to implement such a map. Some sample code is also available to all developers that makes the process much easier. This session gives a good overview, but is missing some of the fine details. It is my intent to fill in the gaps. I will start with my own overview.

  1. Download and install GDAL (available at gdal.org)
  2. Download the TileMap sample project.
  3. Get the coordinates of your image’s bounding rect.
  4. Generate a .vrt file.
  5. Generate the mercator projected map tiles.
  6. Add the tiles to the sample project.
First, download and install Geospatial Data Abstraction Library (GDAL). This can be found at gdal.org. It can also be installed with MacPorts by using “sudo port install gdal”. Make sure you set your PATH variable properly. I won’t go into any details of how to do this. This shouldn’t be hard.

Next, download the TileMap sample project. This is available to all iOS developers through the WWDC10 website http://developer.apple.com/videos/wwdc/2010/. The sample code can be hard to find. It’s on the iTunes page where you download WWDC10 session videos. Between GDAL and this sample project, you as a developer won’t need to do much to get your map working.

This is where things get interesting. You will need to find the coordinates of the bounding rect of your image to overlay. Google Earth makes this easy. I got this tip from a fellow developer on the Apple developer forums. First, find the area you want to place the image overlay. Next, right click on ‘My Places’ (it’s on the left column). Select Add > Image Overlay. This will open a pop up window. From here you can open your image, change the opacity, and move/resize your image. Make your image transparent enough to see both your image and the underlying map. Once you have adjusted your image to match, click on the ‘Location’ tab. There you will find your latitudes/longitudes. You will need to convert those to a decimal value. A quick Google search will turn up a tool to make that conversion for you.

Now to put GDAL to work. The first part is to generate a .vrt (virtual raster) file. This is probably the hardest part of the process. It’s very easy to make a mistake. You will use gdal_translate to map your corner pixels to the coordinates you just found. The terminal command is as follows:

gdal_translate -of VRT -a_srs EPSG:4326 \
-gcp x1 y1 long1 lat1 \
-gcp x2 y2 long2 lat2 \
-gcp x3 y3 long3 lat3 \
-gcp x4 y4 long4 lat4 \
image.jpg image.vrt

There are a few things to note. First, your map image doesn’t have to be .jpg. Several formats are supported. Also, be very careful with your coordinates. Small errors make big problems. Notice that longitude comes first. I had them backwards and spent two days trying to figure out why it wouldn’t work. As for the x and y values, these are your pixel coordinates. (0,0) is the top left of the image.

If you have done everything correctly, the rest is easy. gdal2tiles.py will take your .vrt file and image overlay and generate mercator projected map tiles. The command is simple - “gdal2tiles.py -p mercator image.vrt.” You only need to give it the .vrt file. The .vrt file keeps a relative path to the image overlay. gdal2tiles.py will generate your map tiles along with other various files. If you don’t want these extra files there are command line options to skip them.

With your new map tiles, you simply need to add them to the TileMap project. First, delete the old tiles in the sample. Make sure they are deleted both in XCode and the source directory. If you have previously run the app, you may need to delete the app from your device/simulator. Next, copy your map tiles into your project. When copying your files, there is a vital gotcha to watch out for. You will need to create folder references instead of groups (see image below). Folder references will appear as a blue folder icon, whereas groups appear as a yellow folder icon. The reason for this is because the code is dependent on not just the files but also the directory structure.



If you named your map tiles directory any other than “Tiles,” you will need to change the code to use your directory instead. Finally, run the app and everything should work. If there is a problem, it likely occurred while generating your .vrt file.

Friday, August 13, 2010

Using a UITextField in a UIAlertView

In an app I have been working on, FastTrac WT, there is a UIAlertView with a UITextField added as a subview. Anytime the alert view was dismissed, the error “wait_fences: failed to receive reply:” would show up. It doesn’t make much sense without knowing what the problem is. That seems to be the irony of error messages. The problem turns out to be that the text field must resign its first responder status before it is released. Knowing this the error message makes more sense. The system couldn’t get a reply from the first responder. The solution to this is to resign the first responder status in the alertView:didDismissWithButtonIndex: method. Using it here makes a smooth animation. The alert view will be dismissed followed by the keyboard. An alternative is to resign the first responder status in alertView:willDismissWithButtonIndex:. The alert view and the keyboard will be dismissed at the same time and the alert view will fade and move downwards with the keyboard. This animation also looks nice and the transition is faster. However, resigning the first responder status in alertView:didDismissWithButtonIndex: does not create a smooth animation. The keyboard is dismissed before the alert view. This creates a sudden jump as the alert view re-centers itself to the viewable area. Either of the first two methods are good candidates for fixing this error. Try both and judge which animation fits best with your app.

Saturday, August 7, 2010

My First Quartz Composition

Quartz composer is often used for making screensavers. So, to learn QC, I decided to make a screensaver of my own. I started with a simple concept, two spheres orbiting around each other. I thought it would be simple anyway. All of the motion is accomplished through the use of LFOs (low-frequency oscillators). At first, I thought I could use three LFOs, one for each axis, to generate the desired motion. I couldn’t find anyway to get this to work. In the end, I found an alternative solution. Each sphere has two LFOs that cause the sphere to orbit in the yz-plane. From there, I used a 3D transformation patch to rotate the two spheres about the y and z axes. This achieved the movement I mentally envisioned. Much of my efforts were trial and error. There is very little documentation available for QC. I hope this to change in time.

Below is a link to my Quartz Composition for anyone interested.

http://dl.dropbox.com/u/2998162/Quartz%20Composer/Orbiting_Spheres.qtz

My First Quartz Composer Patches

While playing around with some of the standard Quartz Composer patches, I discovered the Apple remote patch. I just so happen to have an Apple remote, so I wanted to give it a try. I found it to work well. However, the remote only emitted a quick “true” signal. For what I wanted to do, I needed to store a boolean state. A previous electrical engineering course helped me come up with the solution. I built an RS flip flop patch. An RS flip flop is a simple electrical circuit that stores a single state. It consists of only two NOR gates that are cross-coupled. By setting one wire high (true) the stored bit will go high. When the other wire is set high, the stored bit will go low (false). While both wires are low, the bit will remain unchanged. However, both wires shouldn’t ever both be set high. The result is undefined.

This new patch suited me well. The only problem was that it required using two buttons and those buttons couldn’t be pressed simultaneously. I, of course, could do better. I added a few more components to my patch and changed it to a toggle switch. This permitted one button to toggle between the two states. I later discovered that there is a standard toggle patch, but it can toggle between any two numbers. Mine specifically uses boolean values.

Below are links to download the two patches I developed for those who are interested. Note that since these are patches, not macros, the underlying logic is not available to view.

http://dl.dropbox.com/u/2998162/Quartz%20Composer/RS_Flip_Flop.qtz
http://dl.dropbox.com/u/2998162/Quartz%20Composer/Toggle_Switch.qtz