Shiki

I'm so bad with words that I can't even make a cool tagline

Offline maps in iOS using OpenStreetMap and Route-Me

This tutorial is heavily based on Rupert’s article here. However, the steps in this article are very different on some points. There are some steps that I had to figure out using other sources.

Generating OpenStreetMap tiles database

We’re going to use downloadosmtiles.pl to download OSM tiles for a specific region.

  1. Download, compile, and install GEO-OSM-Tiles 0.04. In Terminal:

    wget http://search.cpan.org/CPAN/authors/id/R/RO/ROTKRAUT/Geo-OSM-Tiles-0.04.tar.gz
    tar -xf Geo-OSM-Tiles-0.04.tar.gz
    cd Geo-OSM-Tiles-0.04
    
    perl Makefile.PL # make sure there are no errors/warnings
    make
    make test
    make install # you might have to use sudo
    

    If you get errors like this on Makefile.PL: “Warning: prerequisite YAML 0 not found.“, install the missing Perl libraries first before continuing.

  2. Determine the region you want to download. You can use OSM: go to http://openstreetmap.org and select Export from the top menu. You should be able to see 4 fields specifying the selected region coordinates. Click on Manually select a different area to define your own region.

  3. Execute downloadosmtiles.pl with the coordinates and your desired zoom levels to download the tiles:

    downloadosmtiles.pl --lat=0.871:1.79 --long=103.342:104.3 --zoom=0:15 --destdir=/your/tiles/folder
    

    When specifying values for lat and long, enter the lowest value first. Call downloadosmtiles.pl help to see more options.

    The destination folder should now contain image files organized like this:

  4. Download and run map2sqlite to convert the tile set to a sqlite database. You can download the source here and compile it in XCode. Or you can download the compiled binary here. We’ll use the compiled binary in this example.

    wget http://shikii.net/blog/downloads/map2sqlite-bin.zip
    tar -xf map2sqlite-bin.zip
    
    ./map2sqlite -db /your/mymap.sqlite -mapdir /your/tiles/folder
    

Using the offline map in Route-Me

For a quick example, we’ll use a sample project included in route-me.

  1. Download and extract the latest route-me library from GitHub: https://github.com/route-me/route-me
  2. Open the sample project at samples/SimpleMap/SimpleMap.xcodeproj. Select the SimpleMap scheme and test to make sure it runs before we do anything to it.
  3. Add the map database you just created to the project’s resources. Make sure that it is also added to the Copy Bundle Resources.

  4. In MapViewViewController.m, add an import for RMDBMapSource.h.

  5. Add this to the bottom of -viewDidLoad of MapViewViewController.m:

    // Use the bundled database as our map source
    RMDBMapSource *mapSrc = [[[RMDBMapSource alloc] initWithPath:@"mymap.sqlite"] autorelease];
    [[[RMMapContents alloc] initWithView:mapView tilesource:mapSrc] autorelease];
    
    // Constrain our map so the user can only browse through our exported map tiles
    [mapView setConstraintsSW:CLLocationCoordinate2DMake(mapSrc.bottomRightOfCoverage.latitude, mapSrc.topLeftOfCoverage.longitude)
                           NE:CLLocationCoordinate2DMake(mapSrc.topLeftOfCoverage.latitude, mapSrc.bottomRightOfCoverage.longitude)];
    // Move to the center of our exported map
    [mapView moveToLatLong:mapSrc.centerOfCoverage];
    

    The first 2 lines instruct the map view (RMMapView) to use the offline map as the map source. The -setConstraintsSW:NE call is not necessary but I think it’s a good idea to only show the map region that we have exported. The user will see empty gray spaces for regions that we don’t have map tiles for without the constraints.

And we’re done!

Android Button background image pressed/highlighted and disabled states without using multiple images

In Android, if you provide custom background images for buttons, you will lose the pressed and disabled image effects. The common way to fix that is to provide additional images for those states. I’m lazy and I find this inconvenient especially during the prototyping phase of app development.

I’ve always liked the way iOS automatically handles pressed and disabled states for custom button backgrounds so I made a Button subclass that automatically darkens the background image when the button is pressed, and makes the background transparent when it is disabled. This is done by using a custom LayerDrawable for the button that contains the original background Drawable. The LayerDrawable has to be stateful and should change the background properties depending on the current state in onStateChange(). The full source explains it better:

package net.shikii.widgets;

import android.content.Context;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.LightingColorFilter;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.util.AttributeSet;
import android.widget.Button;

/**
 * Applies a pressed state color filter or disabled state alpha for the button's background
 * drawable.
 *
 * @author shiki
 */
public class SAutoBgButton extends Button {

  public SAutoBgButton(Context context) {
    super(context);
  }

  public SAutoBgButton(Context context, AttributeSet attrs) {
    super(context, attrs);
  }

  public SAutoBgButton(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
  }

  @Override
  public void setBackgroundDrawable(Drawable d) {
    // Replace the original background drawable (e.g. image) with a LayerDrawable that
    // contains the original drawable.
    SAutoBgButtonBackgroundDrawable layer = new SAutoBgButtonBackgroundDrawable(d);
    super.setBackgroundDrawable(layer);
  }

  /**
   * The stateful LayerDrawable used by this button.
   */
  protected class SAutoBgButtonBackgroundDrawable extends LayerDrawable {

    // The color filter to apply when the button is pressed
    protected ColorFilter _pressedFilter = new LightingColorFilter(Color.LTGRAY, 1);
    // Alpha value when the button is disabled
    protected int _disabledAlpha = 100;

    public SAutoBgButtonBackgroundDrawable(Drawable d) {
      super(new Drawable[] { d });
    }

    @Override
    protected boolean onStateChange(int[] states) {
      boolean enabled = false;
      boolean pressed = false;

      for (int state : states) {
        if (state == android.R.attr.state_enabled)
          enabled = true;
        else if (state == android.R.attr.state_pressed)
          pressed = true;
      }

      mutate();
      if (enabled && pressed) {
        setColorFilter(_pressedFilter);
      } else if (!enabled) {
        setColorFilter(null);
        setAlpha(_disabledAlpha);
      } else {
        setColorFilter(null);
      }

      invalidateSelf();

      return super.onStateChange(states);
    }

    @Override
    public boolean isStateful() {
      return true;
    }
  }

}

To use this, just replace your original button declarations like this:

<Button
  android:layout_width="wrap_content" android:layout_height="wrap_content"
  android:background="@drawable/button_blue_bg"
  android:text="Button with background image" />

To this:

<net.shikii.widgets.SAutoBgButton
  android:layout_width="wrap_content" android:layout_height="wrap_content"
  android:background="@drawable/button_blue_bg"
  android:text="Button with background image" />

Here’s a sample output using this custom button:

The code is also available on GitHub.

Removing extra separator lines for empty rows in UITableView

I discovered this by accident. It’s probably been there for a long time but I just noticed it now. Normally, if you have very few rows in your table, it would look like this:

Notice that I only have 2 rows. The other empty ones still have empty separator lines. Sometimes that’s not what you want, especially if you have custom row designs. To remove those lines, you just have to supply a value for tableFooterView. An empty UIView will work fine:

- (void) viewDidLoad
{
  [super viewDidLoad];

  self.tableView.tableFooterView = [[[UIView alloc] init] autorelease];
}

The table should now look like this:

FirePHP on Nginx: 502 Bad Gateway

We have switched from Apache to Nginx a month ago. PHP is running on FastCGI using PHP-FPM. I have just recently tried to debug our app using FirePHP and got a 502 Bad Gateway response from Nginx. It looks like Nginx by default limits the header output. This is the error in Nginx’s error log:

2011/09/21 09:36:16 [error] 816#0: *5 upstream sent too big header while reading response header from upstream, 
  client: 192.168.56.1, server: v.piclyf.com, request: "GET /pics HTTP/1.1", 
  upstream: "fastcgi://127.0.0.1:9000", host: "v.piclyf.com", referrer: "http://v.piclyf.com/dashboard"

The fix that I found is to increase the values of fastcgi_buffer_size and fastcgi_buffers. Add these 2 directives to your PHP FastCGI config in Nginx (i.e. /etc/nginx/sites-available/default):

location ~ \.php$ {
  root /your/site/root;
  fastcgi_index index.php;
  include fastcgi_params;
  fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
  fastcgi_pass 127.0.0.1:9000;

  # set these two:
  fastcgi_buffer_size 16k;
  fastcgi_buffers 4 16k;
}

Using an object’s id as a key in NSDictionary

I was working on a utility class and was playing with the idea of using the id of an object (e.g. NSObject) as a key in NSDictionary. Setting it directly doesn’t work though:

NSObject *obj = [[[NSObject alloc] init] autorelease]; // we'll use this as the key

NSMutableDictionary *dict = [[[NSMutableDictionary alloc] init] autorelease];
[dict setObject:@"a value" forKey:obj]; // won't work

The above will lead to an exception like this:

NSInvalidArgumentException, reason: '-[NSObject copyWithZone:]: unrecognized selector 
sent to instance 0x7ca6160'

The solution I arrived at was to use the string representation of the object. Simply casting the object to NSString will not work though:

[dict setObject:@"a value" forKey:(NSString *)obj]; // same exception

Using a new NSString instance will work however:

NSString *key = [NSString stringWithFormat:@"%@", (NSString *)obj]; // use a new NSString
[dict setObject:@"a value" forKey:key]; // works!

Update

curthard89 from Forrst pointed out that it would be better to use [NSObject hash] instead of typecasting to string. The reason is NSDictionary and NSArray string representations can get really long and would be inefficient. I’m now using this technique:

NSNumber *key = [NSNumber numberWithUnsignedInt:[obj hash]];
[dict setObject:@"a value" forKey:key];

A solution to Boot Camp partition error: “Files cannot be moved”

So I was able to partition a Macbook Pro 2010 (Snow Leopard) using Boot Camp in order to install Windows 7. I just want to point out this possible solution when you get this error when trying to create a partition:

Your Disk Cannot Be Partitioned Because Some Files Cannot Be Moved Back up the disk and use Disk Utility to format the disk as a single Mac OS Extended (Journaled) volume. Restore your information to the disk and try using Boot Camp Assistant again.

Note that I said this is a possible solution. That means that this may not work for you. Most of the solutions I found on the Internet point to using defrag tools like iDefrag. I didn’t want to go that way cause I really don’t have the money to buy those tools.

Here’s what I did to fix this error:

  1. Clean up the hard drive. Move large files to an external backup disk. There are tools like OmniDiskSweeper to that can help in finding large files.
  2. Reboot and boot up using your Mac OS X Install DVD. (Press C when after the startup sound to boot using the DVD)
  3. Choose Utilities > Disk Utility in the menu and Repair the hard drive.
  4. Restart and try partitioning using Boot Camp again.

I’m not ultimately sure if Step 1 is really required but I don’t have time to test it again.

Adding allowed open_basedir paths in a MediaTemple DV

This is a quick fix for PHP errors like these on a Media Temple DV:

Warning: include_once() [function.include-once]: open_basedir restriction in effect. 

I got this a few days ago when trying to setup an app. There are instructions in Media Temple’s site on how to fix this but this set of steps is what worked for me.

  1. Edit or create a vhost.conf file inside this folder: /var/www/vhosts/YOURDOMAIN.COM/conf/. Replace YOURDOMAIN.COM with the domain you wish to configure.
  2. Add this in vhost.conf:

    <Directory /var/www/vhosts/YOURDOMAIN.COM/httpdocs>
      <IfModule sapi_apache2.c>
        php_admin_value open_basedir "/var/www/vhosts/YOURDOMAIN.COM/httpdocs:/tmp:/ADDITIONAL/PATH"
      </IfModule>
      <IfModule mod_php5.c>
        php_admin_value open_basedir "/var/www/vhosts/YOURDOMAIN.COM/httpdocs:/tmp:/ADDITIONAL/PATH"
      </IfModule>
    </Directory>
    

    Replace /ADDITIONAL/PATH with a path you want added to the open_basedir whitelist. You can add more, just separate them with colons (:).

  3. Reconfigure the webserver so vhost.conf will be applied.

    /usr/local/psa/admin/sbin/httpdmng --reconfigure-domain YOURDOMAIN.COM
    
  4. Restart Apache

    /etc/init.d/httpd restart
    
  5. Done! Check the value of open_basedir in phpinfo() to see if it worked.

Getting a list of a Facebook user’s friends with their email addresses

This article assumes that you are familiar with the Facebook SDK Core Concepts and know how to request a user’s access token. This also uses the Facebook PHP SDK.

Getting the list of friends

You can get the friends of a Facebook user using this FQL query:

SELECT uid FROM user WHERE uid IN (SELECT uid2 FROM friend WHERE uid1 = $userId)

This request requires the access token of the user whose friends you want to access. Here’s an example using the Facebook SDK for PHP:

$client = new Facebook(array('appId'  => 'YOUR_APP_ID', 'secret' => 'YOUR_APP_SECRET'));

$userId = 123;
$fql = "SELECT uid, first_name, last_name FROM user "
     . "WHERE uid in (SELECT uid2 FROM friend where uid1 = $userId)";
$friends = $client->api(array(
  'method'       => 'fql.query',
  'access_token' => 'USER_ACCESS_TOKEN',
  'query'        => $fql,
));

// we now have an array containing the friends of the user
print_r($friends); 

You can look at the user table reference for other fields besides first_name and last_name. Note that there are some fields such as email that require user specific permissions. If the user/friend has not given your app email permissions, chances are you won’t be able to get the email address. For instances like these, you can opt to filter just the friends who have given your app permissions. This can be done using the is_app_user field:

SELECT uid, first_name, last_name FROM user 
  WHERE is_app_user = 1 AND uid IN (SELECT uid2 FROM friend WHERE uid1 = $userId)

The case with email addresses

From my tests, it looks like it’s more reliable to get the email addresses of friends if you don’t put in any user access token when calling $client->api(). Here’s a complete example of getting a list of friends and getting all their email addresses:

$appId     = 'YOUR_APP_ID';
$appSecret = 'YOUR_APP_SECRET';

$userId          = 'A_FACEBOOK_USER_ID';
$userAccessToken = 'THE_FACEBOOK_USER_ACCESS_TOKEN';

$client = new Facebook(array('appId' => $appId, 'secret' => $appSecret));

// get all friends who has given our app permissions to access their data
$fql = "SELECT uid, first_name, last_name, email FROM user "
     . "WHERE is_app_user = 1 AND uid IN (SELECT uid2 FROM friend WHERE uid1 = $userId)";
$friends = $client->api(array(
  'method'       => 'fql.query',
  'access_token' => $userAccessToken,
  'query'        => $fql,
));

// make an array of all friend ids
$friendIds = array();
foreach ($friends as $friend) {
  $friendIds[] = $friend['uid'];
}

// get info of all the friends without using any access token
$friendIds = implode(',', $friendIds);
$fql = "SELECT uid, first_name, last_name, email FROM user WHERE uid IN ($friendIds)";
$friends = $client->api(array(
  'method' => 'fql.query',
  'query'  => $fql,
  // don't use an access token
));

// we should now have a list of friend infos with their email addresses
print_r($friends);

Installing GraphicsMagick and Gmagick PHP extension on CentOS 5.6

This was tested on CentOS 5.6 on a fresh MediaTemple (dv) setup. I figure it should be the same for any CentOS install. This set of instructions is a result of hours spent on installation troubleshooting; google searches; and trial and errors. Hopefully this can help other people.

Install dependencies

These are needed to enable GraphicsMagick add-ons. In here, I only included what I think are commonly required. But feel free to include other libraries that you need.

yum install -y gcc libpng libjpeg libpng-devel libjpeg-devel ghostscript
yum install -y libtiff libtiff-devel freetype freetype-devel

Download and install GraphicsMagick

We will need to download and compile GraphicsMagick ourselves. It looks like it is not available in yum yet. MediaTemple users are advised to use chroot to prevent noexec and /tmp related errors. Execute these on the console:

# work on a temporary dir of your preference
cd /root/tmp

# download source tarball. See here for other versions: ftp://ftp.graphicsmagick.org/pub/GraphicsMagick/
wget ftp://ftp.graphicsmagick.org/pub/GraphicsMagick/1.3/GraphicsMagick-1.3.9.tar.gz

# extract and open the extracted folder
tar -xvf GraphicsMagick-1.3.9.tar.gz
cd GraphicsMagick-1.3.9

./configure --enable-shared

Using --enable-shared above has helped prevent errors like this when installing gmagick through pecl:

Build Error: /usr/bin/ld: /usr/local/lib/libGraphicsMagickWand.a(drawing_wand.o):
relocation R_X86_64_32 against `a local symbol' can not be used when making a shared object;
recompile with -fPIC

After ./configure is done, make sure that the Configured value of the add-ons you need are set to Yes. Also make sure that enable-shared is set to Yes. You can see this at the end part of configure‘s output. It should look like this:

Option            Configure option                Configured value
------------------------------------------------------------------
Shared libraries  --enable-shared=yes             yes
Static libraries  --enable-static=yes             yes
GNU ld            --with-gnu-ld=yes               yes
Quantum depth     --with-quantum-depth=8          8

Delegate Configuration:
BZLIB             --with-bzlib=yes                no
DPS               --with-dps=yes                  no
FlashPIX          --with-fpx=no                   no
FreeType 2.0      --with-ttf=yes                  yes
Ghostscript       None                            gs (8.70)
Ghostscript fonts --with-gs-font-dir=default      /usr/share/fonts/default/Type1/
Ghostscript lib   --with-gslib=no                 no
JBIG              --with-jbig=yes                 no
JPEG v1           --with-jpeg=yes                 yes
JPEG-2000         --with-jp2=yes                  no
LCMS              --with-lcms=yes                 no
Magick++          --with-magick-plus-plus=yes     yes
PERL              --with-perl=no                  no
PNG               --with-png=yes                  yes
TIFF              --with-tiff=yes                 yes
TRIO              --with-trio=yes                 no
Windows fonts     --with-windows-font-dir=        none
WMF               --with-wmf=yes                  no
X11               --with-x=                       no
XML               --with-xml=yes                  no
ZLIB              --with-zlib=yes                 yes

Next, compile and install:

make
make install 

You can then test if GraphicsMagick has been successfully installed by executing commands like:

gm version

Install Gmagick PHP extension

We’ll install gmagick using PECL. MediaTemple users are still advised to use chroot.

pecl install gmagick 

If you get an error about gmagick not having a stable version, you can specify the version instead:

pecl install gmagick-1.0.8b2

Next, add extension=gmagick.so to php.ini and restart Apache if necessary. This is outside the scope of this article though. Please Google it if you don’t know how.

And we’re done!

P.S. If you’re having trouble, it might help to look/search for solutions using “ImageMagick” instead. There are fewer resources for GraphicsMagick compared to ImageMagick. I found some articles for ImageMagick that can be used for GraphicsMagick too.

Autoloading namespaced libraries in Yii Framework

To integrate libraries using namespaces into a Yii Framework project, you can simply use Yii::setPathOfAlias to have the classes autoloaded by Yii when needed. Just specify the root namespace as the alias and the physical location of that root alias as the path.

Using Predis as an example, if we put the code in:

/vendors/predis/lib/Predis

We’ll add it to Yii as an alias named Predis so classes under it can be autoloaded:

Yii::setPathOfAlias('Predis', '/vendors/predis/lib/Predis');

Now when we call new Predis\Client(), Yii will look for a file named Client.php in /vendors/predis/lib/Predis. The same holds true for other classes under subfolders (e.g. Predis\Commands\Append).