Tuesday, January 05, 2010

Township Jitney Schedule: Software Development History

This past weekend I wrote an app for my local town: it displays a route, and schedule information, for the town's jitney. (In local terms, a jitney is a township-sponsored shuttle service for resident commuters to and from the train station.) You can see it at http://myjitney.appspot.com.

I'd like to discuss the short development history of this web application.

The Original Plan


The original plan was to write a public, custom Google Map to be shared with the other residents using My Maps, a tool that allows users to create customized maps.

The problem with My Maps is that each marker had be placed individually, and that's tedious: at least to me. What I needed was a programmatic way to feed a set of addresses, determine each address' coordinates (you know, latitude, longitude) and place markers in a custom map.

For that I wanted the Google Maps API in Javascript, and if I had to use the Javascript API for determining coordinates, I might as well use it to build the map from scratch.

The Prototype

The prototype was written in pure Javascript, using the Google Maps API. It was really basic; routes were stored in JSON (natch), and the whole app lived inside a Google Maps widget.

To get started with the API I found an excellent Google Maps API Tutorial written by Mike Williams. The relevant entries were the entries on Markers and info windows,  Polylines from XML and Geocoding Multiple Addresses.

In fact, I hacked a version of the geocoding example by adding all the jitney stop addresses, and from that I got the coordinates of most of the map markers. For those addresses that the API couldn't parse, I used a tedious trial and error process.

The prototype took five hours to write: one hour to parse and codify the data, one hour for figuring out how to get reasonable coordinates for each address, and three hours to get my head around Javascript. For me, writing Javascript is like this: trial, error, trial, error, google, trial, trial, error, trial, error, google, but by the end, I got a map that showed all the jitney stops and their paths.

Two issues with writing the app in Javascript were my basic lack of comfort with Javascript, and also, the app didn't render on my Android phone. I dreaded debugging a web app on an Android phone.

The Rewrite


So I needed to rewrite the app, and by need, I mean want. 24 hours earlier I could have just manually built a damn custom map with My Maps, but now I was committed to code and more code. For the rewrite, I chose GWT. The Google Web Toolkit is a terrific piece of technology; you can write web applications using Java in Eclipse, my preferred IDE, and with a debugger. Since Google provides a GWT implementation of the Google Maps API, it was reasonable to port the existing prototype to GWT. The Google Plugin for Eclipse, a fabulous tool that combined GWT, AppEngine, and Eclipse, made it dead simple to deploy the app to to appspot.com which meant a permanent home to the app, along with a back-end infrastructure in case there was ever a need for servlets or a back-end data store.

(This is a great time to point out that I think that GWT is magic, and the GWT team are a bunch of magicians.)

It took four hours to write a feature-equivalent version of the application using GWT. Most of that time was spent familiarizing myself with the various APIs and getting reacquainted with GWT.


I didn't want to go through the effort of learning how to work with the AppEngine database, so the rewrite still shipped the route data as java source turned into javascript. One of the great benefits of this turned out to be that the map loaded super-quick. So I moved the CSS to the HTML <style> tag, removing yet another server request.

Thanks to GWT the app ran perfectly fine on Android. But more important, thanks to GWT I could write code in a more familiar style, and easily manipulate the DOM outside the web page's map object.

So I did.

Spit and Polish


I spent two more days adding features and polish: a pretty display of the schedule. A list of the routes so each could be viewed independent of the others. A visual indicator of when a jitney will next stop at a certain route.

One of the features was to replace the straight lines from stop to stop with paths along the street. The township had a map that laid out the supposed bus paths, so the trick was finding the coordinates where the paths turn. That turned out to be surprisingly easy with this small piece of Java:

map.addMapClickHandler(new MapClickHandler()
  @Override
  public void onClick(MapClickEvent event) {
    System.out.println(event.getLatLng());
  }
});


Then it was just a matter of running the web application and clicking each spot along the path, feeding the console output back to the web application as intermediate points.






Before

After

Besides adding features, I spent a ton of time dealing with things like positioning and formatting. I spent 30 minutes building a general purpose route building API. I spent 15 minutes on a general purpose algorithm for calculating the center of a group of points. 30 minutes went into making a widget that looks like, but isn't quite, an anchor tag. I spent endless time playing with different types of GWT panels, setting widths, heights and spans. I played with CSS. I failed at CSS, and then I played with it some more. It seems I have the same development cycle for CSS that I do with Javascript: trial, error, trial, error, google.

Done.

By Sunday night the app was done, and so was I. But it's still not done. Even with such a small one-off project, there so many features that could be added. For instance, while the app runs on Android (and reportedly the iPhone) it's not really built for small phones. The individual links are too small to be useful.

But also, I'd like to use Street View to show each stop. Unfortunately, while a Street View API exists in the Javascript API, there's no equivalent in the GWT API. I probably spent about two hours before I recognized that it would involve another painful, endless cycle of trial, error, trial, error, google. Too bad.

Done?

Damn. While writing this post, someone provided feedback, requesting a feature that made too much sense not to implement. So instead of cleaning up this post I'm reading about geocoding again. I love writing software.

Sunday, January 03, 2010

The New Year's Jackpot

This is a true story about New Year's Eve.

My niece and her new husband are visiting from California. They wanted to spend NYE in New York City, to see the ball drop on Times Square and what not. Of course, we didn't stop them, but warned them that it's not all it's cracked up to be -- mostly a mass of bodies freezing in place. That evening they head to the action, but the streets are already blocked off, from 50th street all the way to 42nd. There's no way to get in.


My niece sees a restaurant just on the other side of the barricade, and gets an idea. She found the phone number for the restaurant, called it, and asked if they could seat two more people.


"We can try to squeeze you in," they said.


With restaurant reservation in hand, and an escort by someone from the restaurant, they were able to pass through metal detectors, and brought in to the restaurant. The meal, I'm told, was amazing.
After dinner the manager stopped at their table to make polite conversation. He asked if they were from the area.


"Oh, no. We're visiting from California."


The manager asked why they were visiting. And here is where my niece shows how brilliant she is. She says "We just got married, and this is sort of our second honeymoon."


The manager says "It is? Oh, I'm gonna set you up."


The manager, who is, or knew, a retired police officer, had my niece and her husband escorted by four officers through more metal detectors and were dropped off right underneath the ball. This was about as close as you could get, closer than all the regular people who were behind a barricaide, whom, I'm told, were distinctly unhappy with this.


That's what hitting the jackpot looks like. I wish that you, too, hit the jackpot this year.

Sunday, December 27, 2009

New office space

We bought our house in April. When we moved in, my office had about fifty boxes in it. Every few weekends I'd manage to unpack a couple of boxes, many of which just had old, recyclable mail and printouts of source code that needed shredding.

Yesterday I finally made it down to six boxes, a real achievement, and so I celebrated with a quick trip to Ikea to dedicate a corner of my room to a quiet space.




I like the freestanding shelf. I've never put one up before. I can see adding a second shelf underneath it.

The rug not only helps define the space, but now there's someplace in my office where Maggie can always sit (as demonstrated in the photo.)

Now I'm no design star, which is why everything is black, but I'm very happy. A clean, empty space.


Next week I'm getting a second chair, a second shelf, and a plant. What else?

Saturday, December 12, 2009

What's your small usability feature request?

Yesteday the M4 release of Eclipse 3.6 was announced, and in the New and Noteworthy is my recent contribution:
In JUnit launch configurations, you can now edit the test method:
JUnit launch configuration on 'Test' tab
When the test method is empty, all tests in the class will be run.
The particular thing about this feature is that it was already possible to have a single method under test, but it was only available in an arcane way, through a launch shortcut. Select an individual method in the Eclipse editor (I imagine it's also possibly available through an outline or other such adapted view), right-click and select Run As > JUnit Test.

My hope isn't that a small feature like this makes Eclipse more powerful, but that it makes you more powerful. Give it a try and provide feedback on the bug report.

Powerful features lose their worth if people don't know about them, and people won't find out about powerful features if they're hard to find. Word-of-mouth only goes so far.

Whether or not you believe Eclipse users suffer death from a thousand paper cuts (I do like that matrix), every tool has its rough edges.  What small usability feature do you think would make a difference?

Thursday, December 10, 2009

Eclipse users, go download IntelliJ

JetBrains just announced the release of IntelliJ 9, which also comes with its new Community Edition! Congratulations, JetBrains!

The primary reason I was interested in Eclipse years past was because it was free and better than NetBeans; I could go from one job to another and not have to ask for an IntelliJ license. But I've always wanted to play with it. The final selling point to me was Eric Burke's IDE matrix. The day to start trying out IntelliJ has come now that a free version is available.

I'm playing with it tonight. Everything is familiar yet entirely foreign. I imagine that someone recovering from a stroke, learning to use his hands for the second time, might have similar (albeit more striking) frustration while realizing fingers don't magically go where they're supposed to.

My day job is providing tools and support to the Eclipse users at Google. We have quite a few Eclipse users at Google, but we also have quite a few IntelliJ users at Google. And it's not a terribly inaccurate characterization that the IntelliJ users at Google love IntelliJ. The folks that love it are colleagues I admire; Jetbrains must be doing something right.

If you use the Eclipse IDE to write your software, I encourage you to download IntelliJ and take it for a spin. See what you like, see what you don't. And then, report back. Sharing ideas from other communities can make a big difference to an open community.

Unless you fell into that "Tragedy of the Commons" conversation, in which case we're all screwed.

Tuesday, December 08, 2009

Budget adds $14 to your rental, calls it a service

This weekend I rented a car with Budget. I declined their fuel service option since I knew I was driving a relatively short distance, and there are plenty of gas stations near the rental station. I could easily fill the tank. Since I am a member of their RapidRez program, I didn't need to go in to the office; I picked up my paperwork literally on the way out of the airport parking lot, put it aside, and drove to the city.

Sunday night I paid $8 to fill the gas tank, and returned the car. Some rental facilities give you a bill as you drop off the vehicle, but at this location I was directed to the office, where I picked up the paperwork. I looked it over quickly and noticed a $14 fee. (Okay, it was a $13.99 fee but the only two groups of people who suggest those numbers are unequal are marketers and mathematicians.) I asked the agent about the fee, and I was pointed to a small sign regarding their new policy:
EZ Fuel
If you drive fewer than 75 miles, save time and do not refill the tank. Budget will automatically add a charge to the rental for fuel. If you do refill the tank, simply present a receipt and Budget will remove the charge.
Their website has a small write-up about the EZ Fuel option.

Apparently I was given a piece of paper regarding this policy when I left the parking lot. But I wasn't asked to sign anything, nor was I aware of it when I handed the forms to my passenger and drove off the lot. I explained to the agent that I filled the tank, but could not find the receipt. The agent insisted I was still responsible for the charge. I told the agent that there was no way I was paying for this. The agent insisted I was liable without the receipt. I stood my ground. I repeatedly pointed to the slip of paper provided by the person who collected my car that indicated a) no damage and b) a full tank of gas. It was only when I asked to speak with the manager that they removed the fee.

There are so many things I dislike about this fee: this article, suggests they're banking on your unwillingness to argue for the refund. They're hoping you run for the shuttle, or not even notice. I wonder if the typical profile of an airport renter who drives less than 75 miles is the businessperson in for the day, people who are seriously counting minutes. I, too, would have not argued for a refund if I returned the car as originally planned Monday morning 9AM, instead of Sunday 11:55PM when I was the sole customer in the office.

I can imagine they want to recoup costs for their one-gallon consumers. Here's a solution: raise your rates. Of course, they won't; they want to maintain their competitive advertised rates, in the same way airlines charge for meals. Here's another idea: charge a sliding scale based on the vehicle's fuel efficiency. They won't do that either: it's easy to have a flat fee, and it will encourage customers to rent the more fuel efficient cars, for which they charge less. OK, here's another idea: stop relying on mediocre analog fuel gauges for your measurements. That also has problems. Here's another: don't make me come to your office to get a receipt.

So is it worth ensuring I drive more than 75 miles just to avoid the fee? At $3 per gallon, a car that gets 16 miles to the gallon will need $14 of fuel to travel 75 miles. What's the right strategy to execute when driving to an airport in an unfamiliar location when your odometer reads 73.5 miles?

I've only used Budget because they provide a RapidRez card to my company's employees. It's not worth this. Even though I am certain to drive 300 miles when I return to Los Angeles in January, I'm going with Enterprise.