Archive for January, 2011

NYC yum yum

NYC yum yum is my web app for the NYC Big Apps 2.0 competition.

NYC yum yum

It’s a very simple app for quickly finding restaurants by cusine + location. For each resturaunt, it shows the restaurant inspection grade, to give you an idea of how clean and safe the food at the establishment is, and also pulls the Yelp star rating to give you and idea of how good the food is.

NYC yum yum front page

NYC yum yum results

Search by cuisine and location (specifically, neighborhood) I felt was really important. Searching by name is, of course, much simpler, but when it comes to finding a new restaurant your likely not to know the name beforehand. Even if you have been to the restaurant before, unless it’s a regular spot for you, you’d likely still has issues remembering the name given the number of places to eat in New York City.

That said, this was far more difficult than I anticipated and I’m sad to say the current implementation is far from ideal. Here’s why:

  • The raw data set from the NYC Data Mine contains no geographic coordinates (latitude, longitude), so to get an accurate location, the address must be geocoded. There are a number of services to do this, but they’re all pretty limited. Google’s Geocoding API was my first choice, but I was wary of the terms of use,

    … the Geocoding API may only be used in conjunction with a Google map; geocoding results without displaying them on a map is prohibited. For complete details on allowed usage, consult the Maps API Terms of Service License Restrictions.

    My second choice was geocoder.us, but it had issues geocoding locations outside of Manhattan. So I put aside the idea of doing a geocode.
  • My next attempt was to do queries by neighborhood. Surely, it would be easy to translate between neighborhoods and zip codes, right?! Nope. All I could find were commercial services (e.g. maponics) that did it, and I wasn’t willing to go down that road.
  • My final attempt and what’s implemented, is grabbing the zip code from the the marker location on the google map. This sorta works. The problem is that the area covered by a zip code doesn’t necessarily match up exactly with a neighborhood’s boundary. So a restaurant that may not be shown in the search if it falls into an adjacent zip code.

My big disappointment is how restrictive and inaccessible all of this geographic data is, and the lack of such data severely compromises the effectiveness of the search.

Another, somewhat major issue with the raw data set is how restaurants are categorized. I’m noticing tons of establishments are simply identified with the cuisine type of “American.” For example, B Cup is a café in the East Village. If I was looking for it, I would search for “Café/Coffee/Tea” not “American.”

NYC yum yum

In fact, I think “American” is way to generic to describe any sort of cuisine.

In any case, NYC yum yum still works pretty well and does hit of the target of being able to find good, clean places to eat at, quick and easily. I’ll likely be working on improvements to it in the near future.

Edit: mixed up geocoding and reverse geocoding

Old school

Came across this while clearing out some old stuff from my bookshelf,

visual basic 3.0 programmer's guide

jQuery toggle button

On most of the mobile platforms you’ve probably seen a toggle, switch-style, button used as a replacement for a checkbox. I took a stab at doing something similar in HTML, CSS and Javascript.

toggle button

You can see the final result here (it’s a pain in the ass to embed it)

Note that while I used jQuery, this is not a jQuery extension. It doesn’t use that much jQuery and I really don’t get the desire to make everything-and-the-kitchen-sink a jQuery plugin.

The button depends upon 2 images, a base, containing the design and both states of the button:

toggle button base

… and a frame (optional if you can get away with using CSS borders):

toggle button frame

(note, the middle is transparent, not white)

The HTML and CSS consists of a:

  • A div, which has the its background-image set to the base and sized to the button’s inner area, roughly half the width (in this case, plus a few pixels as some pixels were shared by both states of the button) of the base and the same height
  • A block-level anchor element within the div, which has its background-image set to the frame and sized to the same area as the frame image. The anchor allows the area to be clickable and we’ll respond to the click event that occurs on this element.
  • An input checkbox which will store the checked/unchecked state of the button.
<div style="margin:0; padding:0; background:url(base.png) -41px 1px no-repeat transparent; width:46px; height:20px;">
<a class="toggle-button" href="#" style="margin:0; padding:0; display:block; background:url(frame.png) 0 0 no-repeat transparent; width:48px; height:20px;">
<input style="display:none;" type="checkbox" />
</a>
</div>

The Javascript code to handle the click event, where the background is shifted left or right when the button’s state is toggled using jQuery’s animate function,

$('.toggle-button').click(function ()
{
if (!$('input', this).is(':checked')) {
$(
this).parent().animate({ "background-position": "0px 1px" }, "slow");
$(
'input', this).attr('checked', true);
}
else {

$(
this).parent().animate({ "background-position": "-41px 1px" }, "slow");
$(
'input', this).attr('checked', false);
}

return false;
});

This all works great, but it’s not-so-great as a reusable component, so I encapsulated the code so that I could easily transform a div, such as the one shown below, into the toggle button.

<div id="my_toggle_button"></div>

Central to this is creating a ToggleButtonFactory, which will make the button by inserting the necessary HTML/CSS code into the DOM and bind the anchor to the click event. There’s also a ToggleButton object created by the factory which will have methods to toggle the button state (.toggle) and get the state of the button (.val).

function ToggleButton(_element, _funcSelectYes, _funcSelectNo)
{
this.jqDomElement = _element;
this.funcSelectYes = _funcSelectYes;
this.funcSelectNo = _funcSelectNo;

this.val = function ()
{
return $(this.jqDomElement).find('input').is(':checked');
}

this.toggle = function (funcSelectYes, funcSelectNo)
{
if (!this.jqDomElement.find('input').is(':checked')) {
this.jqDomElement.animate({ "background-position": "0px 1px" }, "slow");
this.jqDomElement.find('input').attr('checked', true);

if (this.funcSelectYes) {
this.funcSelectYes();
}
}
else {

this.jqDomElement.animate({ "background-position": "-41px 1px" }, "slow");
this.jqDomElement.find('input').attr('checked', false);

if (this.funcSelectNo) {
this.funcSelectNo();
}
}
}
}

var ToggleButtonFactory = {};
ToggleButtonFactory.makeButton =
function (element, initialState, funcSelectYes, funcSelectNo)
{
if ($(element).is('div')) {

var elemId = $(element).attr('id');
var newDivId = '__toggle_button_div_' + Math.ceil((Math.random() * 100000));
$(element).replaceWith(
'<div id="' + newDivId + '" style="margin:0; padding:0; background:url(base.png) -41px 1px no-repeat transparent; width:46px; height:20px;"><a class="toggle-button" href="#" style="margin:0; padding:0; display:block; background:url(frame.png) 0 0 no-repeat transparent; width:48px; height:20px;"><input id="' + elemId + '" name="' + elemId + '" style="display:none;" type="checkbox" /></a></div>');

var newElem = $('#' + newDivId);
var tb = new ToggleButton(newElem, funcSelectYes, funcSelectNo);

newElem.find(
'a').click(function ()
{
tb.toggle();
return false;
});

if (initialState) {
tb.toggle();
}

return tb;
}
}

Note there’s some additional code here to deal with setting the button to an initial state and callbacks for when the button is set to the “Yes” or “No” state.

Now, to transform the my_toggle_button div shown above into a toggle button, the following is done:

var btn = ToggleButtonFactory.makeButton('#my_toggle_button', false, function () { }, function () { });

(the call can be shorter, this shows calling with all arguments and capturing the return value [the ToggleButton object])

For another take on this, see the jQuery LightSwitch plugin.

Paper Half-Life 2

Incredibly impressive,

poly2path

I’m working on a little SVG project using Raphaël. Unfortunately, Illustrator exports polygons in its SVG output which is not supported by Raphaël (only paths are supported). So I wrote an app to convert the SVG polygon string to an SVG path string.

Please upgrade your Flash Player This is the content that would be shown if the user does not have Flash Player 9.0.115 or higher installed.

the conversion…

poly2path conversion

Note that you only input the points data from the polygon (from the points attribute), not the entire polygon element. The result is the path string for the d attribute of the path element.

The conversion is very simple and based upon the fact that a polygon is a path starting with an absolute moveto, linetos to each of the points, and a closepath (this bug report [yes, a bug report!] was pretty helpful).

I actually wanted to render the output, but I was disappointed to discover that Adobe Air doesn’t currently support SVG.

The main reason for not including it was runtime size concerns (adding it would have increased the runtime size by 15 to 20 percent). Initially, the main pain-points regarding AIR were the size of the runtime, integration with the operating system and native APIs, support for the <canvas> tag and new CSS properties, and JavaScript performance. These priorities, coupled with a trend toward reduced interest in SVG graphics, led to SVG support not being included in the current version of Adobe AIR.

Italy from space

A nighttime picture of Italy taken from the Cupola observation deck of the International Space Station:

italy from space

(I modified brightness + contrast, and sharpened a bit)

NYC Data Mine, restaurant inspection data

I’ve just finished importing the current restaurant inspection data from the NYC Data Mine into a PostgreSQL database. It wasn’t the most difficult migration, but more difficult than it should be as the raw data from the data mine is messy and not well-formed; a typical problem with many of the data sets present in NYC Data Mine. I came across a great post by Steven Romalewski (director of the CUNY Mapping Service) about the poor data quality and poor metadata based on his experiences.

From looking at the restaurant inspection data and skimming a few other sets, I get the sense that structured and relational data simply isn’t understood or handled well. To be fair, there’s a very real lack of tools in the market, at least at the consumer/data-entry level, for handling such data, so it’s not surprising that everything gets jerryrigged into an Excel worksheet. This is very clear when looking at the restaurant inspection data, you notice right away that restaurant ids and names are repeated across multiple rows.

In any case, the restaurant inspection data is better than most of the sets, but there’s a few issues to take note of:

  • In multiple cases the same row, with the exact same data, is repeated.
  • There are 2 columns for the inspection date: INSPDATE and GRADEDATE; GRADEDATE = INSPDATE if there’s a letter grade for the restaurant, otherwise it’s blank/null.
  • Most glaring, there are invalid timestamps in the GRADEDATE column for 2 restaurants (but, of course, it’s across multiple rows as the restaurants has multiple entries), CAPRI RESTAURANT and MAMA LUCIA:

    timestamp problem

For my purposes, I only wanted the most recent inspection result (i.e. the row the latest INSPDATE timestamp). To do this, I added an additional column for a serial/auto_increment id number. Then, once imported, I deleted the unneeded rows with the following query:

/* table is restaurant
id = CAMIS
inspection_score_date = INSPDATE
internal_id = serial/auto_increment id number
*/

DELETE FROM restaurant WHERE internal_id NOT IN
(SELECT MAX(restaurant.internal_id) AS max_iid FROM restaurant,
(SELECT id, dba, MAX(inspection_score_date) AS last_inspt FROM restaurant GROUP BY id, dba) AS sub
WHERE restaurant.id=sub.id AND restaurant.inspection_score_date=sub.last_inspt GROUP BY restaurant.id)

The innermost subquery pulls the rows with the most recent inspection date, the outer takes care of duplicate rows with the same inspection date by simple taking the row with the max internal id number. What results is a column of internal id numbers – each representing a row with a unique restaurant inspection for the most-recent inspection.

I’m not sure if this is the best or most efficient way to do this, but it works and took about 14s to delete the unneeded rows for 398,878 rows on a low-end VPS.

PostgreSQL + PHP installation on Windows 2003 x64

Well the PostgreSQL installation itself is easy enough, getting it to work with PHP is the challenging part. Here’s what I did:

webOS file upload

This is something pretty basic, but the example in the API documentation leaves a lot to be desired and, if I recall correctly, doesn’t even work.

So here’s how to do a file upload on webOS, sending the file along with additional data in the POST call.

One critically important point to take note of is that session information is not passed from your app to the com.palm.downloadmanager service so, if necessary, you must verify and authenticate the user again. As I was using PHP for my server-side stuff, I looked into getting and passing the PHPSESSID cookie, but I couldn’t figure out how the get the value, webOS does not seem to store it with other cookies.

// additional POST data
// spot_id, userName, password declared elsewhere
var post_params = [
    {
'key': 'id', 'data': spot_id, 'contentType': 'text/plain' },
    {
'key': 'username', 'data': userName, 'contentType': 'text/plain' },
    {
'key': 'password', 'data': password, 'contentType': 'text/plain' }
];    



ctrlr.serviceRequest(
'palm://com.palm.downloadmanager/', {
method:
'upload',

// myfile = fileName of file to upload
// url_to_web_service = URL to server-side script that will handle file upload
// Note, contentType is mime type of file (according to docs), but 'img' used for images?!
parameters: {
'fileName': myfile,
'fileLabel': 'my_cool_file',
'url': url_to_web_service,
'contentType': 'img',
'subscribe': true,
'postParameters': post_params
},

onSuccess:
function (resp)
{
if (resp.responseString) {
// file has been uploaded             
}
else {
// file partially uploaded, resp contains progress info
}
},

onFailure:
function (e)
{
// something bad happened!
}
});


On the server-side, the file is identified by the value of ‘fileLabel’ (in this case ‘my_cool_file’), and the additional POST data is identified by the value of the key field.

Page fading

Page transitions on a web site are not at all smooth nor elegant in any way; you click on a link or type in an address, the browser sends the request, and once the response comes back it dumps whatever it can onto the screen. I wanted to do a little experiment to see if it was possible to fade in and out between pages, and to do it as unobtrusively as possible (i.e. avoid hacks to the DOM to support the effect).

You can see the result here.

page fading

It works quite well. This uses jQuery’s fadeIn and fadeOut functions, doing a fadeIn on the body when the page is loaded, and a fadeOut when any link is clicked. Once the fadeOut completes, the browser goes to the next page by updating window.location.

function fadeOutPage(target)
{
$(
'body').fadeOut('slow', function () { window.location = target; });
}

function fadeInPage()
{
$(
'body').fadeIn('slow');
}

window.onload() is as follows:

window.onload = function ()
{
$(
'a').click(function ()
{
fadeOutPage($(
this).attr('href'));
return false;
});

fadeInPage();
}

For an actual site, you’d likely need to deal with links handled by the JS click() event differently, so that they don’t fade out the page.

So far, so good. Unfortunately, there’s one obtrusive bit of CSS needed to get the effect to work correctly:

<body style="display:none;">

This is necessary to hide everything in the body when the page initially loads; calling $(‘body’).hide() within window.onload() won’t work as you’ll see the page flicker onto the screen at full opacity, then disappear, then fade in.