Archives for September 2011

DM Weil art gallery in New Paltz, NY

My wife and I visited DM Weil’s gallery as part of our anniversary weekend away, somewhat randomly, looking for things to do in the Hudson River Valley area on TripAdvisor.

DM Weil's gallery

While we enjoy the finer things in life, we’re not art snobs. Often, the art gallery experience is dull — sure, there’s some great art, or at least that’s what everyone calls it — but it’s totally different at DM Weil. Ken, the gallery manager, makes you really feel welcome and will gladly answer your questions about the art, the products available, and so on. Donna shared her story with us about her art, that gave us a real connection to her gift and talent on display.

The art itself covers a broad range — busy, energetic, curious, transporting, nuanced, whimsy, vibrant. The gallery space is wonderful, airy, bright and crisp. It really gives you the opportunity to get lost in the art.

In comparison to upscale New York City galleries, DM Weil’s gallery is beautiful, peaceful and relaxed. As guests, you feel so welcome and the hosts are generous, offering refreshments and conversation.

We will certainly be back to see new works on display in the future. Even if you don’t think you care for art, if you’re in the area, and you can keep an open mind, definitely stop in when the gallery is open. Beyond the paints and canvas is a wonderful person who shares her time and talent with you, with everyone.

MySQL Geo Distance Code and Samples

Over the years, I’ve implemented and re-implemented the haversine formula whenever I’ve needed to compute great-circle distances, which is pretty common when you’re doing geospatial proximity searches, e.g., “What’s near me?” or “How far are these two locations from each other?”

For an implementation of the formula in MySQL, Alexander Rubin’s presentation is quite useful, and is what I’ve based the following code on this time around.

Hopefully, rather than re-implementing this yet again the next time I need it, I’ll find my own blog entry on it and just reuse this code. I keep forgetting where I’ve stashed the most recent copy, which is why I keep ending up re-implementing this every time I’ve needed it.

Here’s the code, tested on MySQL 5.1.41:

DELIMITER $$

DROP FUNCTION IF EXISTS geodist $$
CREATE FUNCTION geodist (
  src_lat DECIMAL(9,6), src_lon DECIMAL(9,6),
  dst_lat DECIMAL(9,6), dst_lon DECIMAL(9,6)
) RETURNS DECIMAL(6,2) DETERMINISTIC
BEGIN
  SET @dist := 3956 * 2 * ASIN(SQRT(
      POWER(SIN((src_lat - ABS(dst_lat)) * PI()/180 / 2), 2) +
      COS(src_lat * PI()/180) *
      COS(ABS(dst_lat) * PI()/180) *
      POWER(SIN((src_lon - dst_lon) * PI()/180 / 2), 2)
    ));
  RETURN @dist;
END $$

DROP FUNCTION IF EXISTS geodist_pt $$
CREATE FUNCTION geodist_pt (src POINT, dst POINT) 
RETURNS DECIMAL(6,2) DETERMINISTIC
BEGIN
  RETURN geodist(Y(src), X(src), Y(dst), X(dst));
END $$

DROP PROCEDURE IF EXISTS geobox $$
CREATE PROCEDURE geobox (
  IN src_lat DECIMAL(9,6), IN src_lon DECIMAL(9,6), IN dist DECIMAL(6,2),
  OUT lat_top DECIMAL(9,6), OUT lon_lft DECIMAL(9,6),
  OUT lat_bot DECIMAL(9,6), OUT lon_rgt DECIMAL(9,6)
) DETERMINISTIC
BEGIN
  /*
   * src_lat, src_lon -> center point of bounding box
   * dist -> distance from center in miles
   * lat_top, lon_lft -> top left corner of bounding box
   * lat_bot, lon_rgt -> bottom right corner of bounding box
   */
  SET lat_top := src_lat - (dist / 69);
  SET lon_lft := src_lon - (dist / ABS(COS(RADIANS(src_lat)) * 69));
  SET lat_bot := src_lat + (dist / 69);
  SET lon_rgt := src_lon + (dist / ABS(COS(RADIANS(src_lat)) * 69));
END $$

DROP PROCEDURE IF EXISTS geobox_pt $$
CREATE PROCEDURE geobox_pt (
  IN pt POINT, IN dist DECIMAL(6,2),
  OUT top_lft POINT, OUT bot_rgt POINT
) DETERMINISTIC
BEGIN
  /*
   * pt -> center point of bounding box
   * dist -> distance from center in miles
   * top_lft -> top left corner of bounding box
   * bot_rgt -> bottom right corner of bounding box
   */
  CALL geobox(Y(pt), X(pt), dist, @lat_top, @lon_lft, @lat_bot, @lon_rgt);
  SET top_lft := POINT(@lon_lft, @lat_top);
  SET bot_rgt := POINT(@lon_rgt, @lat_bot);
END $$

DROP PROCEDURE IF EXISTS geobox_pt_bbox $$
CREATE PROCEDURE geobox_pt_bbox (
  IN pt POINT, IN dist DECIMAL(6,2), OUT bbox POLYGON
) DETERMINISTIC
BEGIN
  /*
   * pt -> center point of bounding box
   * dist -> distance from center in miles
   * bbox -> bounding box, dist miles from pt
   */
  CALL geobox_pt(pt, dist, @top_lft, @bot_rgt);
  SET bbox := Envelope(LineString(@top_lft, @bot_rgt));
END $$

DELIMITER ;

And, here’s examples on how to use the above function and procedures:

SELECT @src := pt FROM exp_geo
WHERE postal_code = '07405' AND city = 'Butler';

CALL GEOBOX_PT(@src, 10.0, @top_lft, @bot_rgt);
SELECT Y(@top_lft) AS lat_top, X(@top_lft) AS lon_lft,
       Y(@bot_rgt) AS lat_bot, X(@bot_rgt) AS lon_rgt;

CALL GEOBOX_PT_BBOX(@src, 10.0, @bbox);
SELECT AsWKT(@bbox);

-- Assuming INDEX on (lat, lon) and SPATIAL INDEX on (pt) ...

-- Fast:
SELECT g.postal_code, g.city, g.state,
       GEODIST(Y(@src), X(@src), lat, lon) AS dist
FROM exp_geo g
WHERE lat BETWEEN Y(@top_lft) AND Y(@bot_rgt)
AND lon BETWEEN X(@top_lft) AND X(@bot_rgt)
HAVING dist < 5.0
ORDER BY dist;

-- Also fast:
SELECT g.postal_code, g.city, g.state, GEODIST_PT(@src, g.pt) AS dist
FROM exp_geo g
WHERE lat BETWEEN Y(@top_lft) AND Y(@bot_rgt)
AND lon BETWEEN X(@top_lft) AND X(@bot_rgt)
HAVING dist < 5.0
ORDER BY dist;

-- Slow:
SELECT g.postal_code, g.city, g.state, GEODIST_PT(@src, g.pt) AS dist
FROM exp_geo g
WHERE Y(pt) BETWEEN Y(@top_lft) AND Y(@bot_rgt)
AND X(pt) BETWEEN X(@top_lft) AND X(@bot_rgt)
HAVING dist < 5.0
ORDER BY dist;

-- Also slow:
SELECT g.postal_code, g.city, g.state, GEODIST_PT(@src, g.pt) AS dist
FROM exp_geo g
WHERE MBRContains(@bbox, pt) = 1
HAVING dist < 5.0
ORDER BY dist;

Normally, I try to explain the stuff I post, but this time I’m doing it for somewhat selfish reasons — so I don’t lose this work yet again. I’m just throwing this up here so Google will index it, so I can find it the next time I need it.

I suppose if you read this and have questions, please ask them in the comments below. I’ll do my best to answer, if I can.

How To Upgrade an AT&T Captivate to Gingerbread with Cognition 5 on a Mac

Disclaimer: Everything you see here is at-your-own-risk. If this doesn’t work for you, or it ends up bricking your phone, etc., it’s your own damn fault. Sorry. All I can say is that it worked great for me.

Prerequisites

You’ll need to download all of these files before you get started. If figuring out how to download these things proves to be too hard for you to figure out, then you should not be attempting this project. You probably don’t understand enough of the basics, and will make a mistake that will likely brick your device.

Now, let’s get started …

1. BACK UP EVERYTHING FIRST!

Use Titanium Backup to back up all of your data and apps. Use ClockworkMod ROM Manager to back up your current ROM.

Don’t say I didn’t warn you …

2. Copy Cognition5-v2.zip to your internal SD card

You’ll need to do this first, before you start the following steps. Hook up your phone using your USB cable and use “mass storage mode” to copy the zip file over.

3. Reboot into download mode

Throughout this process, you’ll need to keep putting your phone into “download mode.” You’ll know you’re in download mode because the phone will display a screen with a big yellow triangle with the Android robot holding a shovel, and the word “Downloading…” underneath it, and the words “Do not turn of Target!!!” at the bottom of the screen.

Android download mode screen

To put your phone into “download mode,” use the following steps:

1. Disconnect the USB cable, remove your battery.
2. Wait 5 seconds.
3. Connect the USB cable.
4. Press and hold the volume up and volume down buttons.
5. Insert the battery.

If you’ve done this right, you should be in download mode.

4. Flash stock Gingerbread using Heimdall

I’m not thrilled with the Heimdall frontend, so I just use the command-line interface using Terminal.app.

First, verify that Heimdall can see your Captivate in download mode. You should see:

$ heimdall detect
Device detected

If, instead, you see this:

$ heimdall detect
Failed to detect compatible download-mode device.

Stop now. Go back, figure out what you did wrong. You cannot proceed until Heimdall can see your phone connected via USB and in download mode.

***

To flash the stock Gingerbread ROM, we’ll need to prepare a little bit, first. Start by extracting the I897UCKF1.rar file, which should contain the following files:

CODE_I897UCKF1_CL273832_REV02_user_low_ship.tar.md5
Kepler_odin_new_part_JE3_S1JE4.pit
MODEM_I897UCKF1_CL1017518_REV02.tar.md5
odin v1.81.zip
SGH-I897-CSC-ATTKF1.tar.md5

We will need to extract the files from two of these tar files:

$ tar xf CODE_I897UCKF1_CL273832_REV02_user_low_ship.tar.md5

$ tar xf MODEM_I897UCKF1_CL1017518_REV02.tar.md5

Now we should have the following files: Sbl.bin, boot.bin, cache.rfs, dbdata.rfs, factoryfs.rfs, modem.bin, param.lfs, zImage. With our Captivate in download mode, we will flash these using the following command:

$ heimdall flash --repartition --pit Kepler_odin_new_part_JE3_S1JE4.pit \
    --factoryfs factoryfs.rfs --cache cache.rfs --dbdata dbdata.rfs \
    --param param.lfs --primary-boot boot.bin --secondary-boot Sbl.bin \
    --kernel zImage --modem modem.bin

Heimdall v1.3.0, Copyright (c) 2010-2011, Benjamin Dobell, Glass Echidna
http://www.glassechidna.com.au

This software is provided free of charge. Copying and redistribution is
encouraged.

If you appreciate this software and you would like to support future
development please consider donating:
http://www.glassechidna.com.au/donate/

Initialising connection...
Detecting device...
Claiming interface...
Setting up interface...

Beginning session...
Handshaking with Loke...

Uploading PIT
PIT upload successful
Uploading KERNEL
100%
KERNEL upload successful
Uploading MODEM
100%
MODEM upload successful
Uploading FACTORYFS
100%
FACTORYFS upload successful
Uploading DBDATAFS
100%
DBDATAFS upload successful
Uploading CACHE
100%
CACHE upload successful
Uploading IBL+PBL
100%
IBL+PBL upload successful
Uploading SBL
100%
SBL upload successful
Uploading PARAM
100%
PARAM upload successful
Ending session...
Rebooting device...

After the device reboots, you should have a working Captivate running stock Gingerbread ROM.

5. Flash Cognition 5 v2 using Heimdall

Again, put yourself in download mode using the instructions from the previous step, then flash the Cognition 5 v2 initial kernel:

$ heimdall flash --kernel Cognition5-v2-initial-kernel.tar

[...]
Uploading KERNEL
100%
KERNEL upload successful
Ending session...
Rebooting device...

From here, follow the usual steps to get into ClockworkMod Recovery, and install the Cognition5-v2.zip update that we copied onto the internal SD earlier in step #3.

If all goes well, you should be able to reboot your phone after the update and be up and running on Cognition 5 v2!

***

If I’ve made a mistake in any of the steps, or have left out important details, feel free to help correct them by leaving a comment below.

Personal update and new family photo

I went through all the trouble of setting up a much improved email subscription for my blog using MailChimp almost 3 weeks ago, and of course I haven’t even posted anything to the blog since! That means a bunch of you signed up to see what I’d done, and didn’t get to see anything, yet. Sorry about that.

Personal Update

Despite the recent hurricane Irene, we spent a week up on Cape Cod, in Mashpee, for the week of August 27th through September 3rd. After dealing with very strong winds and a power outage for a few hours on Sunday, we all had a fantastic time and incredibly good weather. Here’s a family photo we were able to take while we were out at a mini-golf place:

Family photo on Cape Cod - 2011-08-30

We’re back home, the girls have started school again, and work has been keeping me particularly busy.

Work Update

At work, we’ve been testing a fully fault-tolerant design: redundant firewalls, redundant load balancers, a farm of web frontends and replicated database backends. We’ve done several load tests to verify that they’ll be able to handle the volume of traffic that we get, with a fairly large multiple of our highest peak volume.

Tonight, we’re deploying these servers. Even though I’ve done this many times over the course of my career, it’s still exciting in its own way. Sometimes you have something unexpected happen that you have to deal with. However, when things just go smoothly, according to plan, like it did tonight … it’s just a great feeling, watching everything just fall into place.

I hope everyone’s had as best a week they can, given the circumstances–the flooding on the east coast, etc. I’ll try to post updates more frequently. Oh, and if you have any comments or feedback about the new MailChimp-delivered blog email updates, definitely let me know! I really want to hear what everyone thinks of them. Thanks!