Archive for the ‘internet’ Category

Google bike directions

I’ve been using Google Maps to choose bike routes for years. The route I took today from my office to downtown Naperville, for example, uses a set of residential streets and sidewalks along busy roads that I found by studying the aerial photos. Now Google has a specific option for getting bicycling directions.

I tried it out by entering my home and work addresses. Most of the route it came up with matches my regular path, but one spot was horribly wrong.

First, Google wants me to ride along Ogden Avenue, which is suicidal, although this instruction may not be as bad as it seems, since no one would be stupid enough to ride in a busy street when a there’s an asphalt path (that’s the bright green line) that’s not only close by, but actually cuts a corner.

No, the real problem is crossing Ogden at Meadow Lakes Boulevard. During rush hour, cars can barely cross that intersection. There’s no traffic light, and Ogden—the main east-west street in the area—has two through lanes and one turn lane in each direction. And there’s no median, so there’s no way for you to cross halfway when one direction is clear and wait in the middle until the other direction clears. Google’s Street View for the intersection shows cars in every lane, and I can guarantee you the Street View photos weren’t taken at rush hour. Just riding on the bike path parallel to Odgen is a pain at this intersection, because drivers on Meadow Lakes are always pulling forward to block the path.

Weirdly, there are traffic lights a block east and a block west of this intersection. Either would be better than trying to cross here.

Google recognizes that its biking directions may be wrong. Here’s the warning/disclaimer that comes with them:

I filled out the little web form that pops up when you click that link, and I have little doubt that Google’s bicycling directions will improve quickly. Still, I think it’s odd to use the term beta for this product. Losing your data is a risk beta testers are used to. Here, you stand to lose a bit more.


Exploring the Simplenote API

I’ve been meaning to work with the Simplenote API ever since it was announced, thinking it would be a good way to keep my plain-text todo lists1 synced between my computers and my iPhone. I haven’t done any syncing yet, but I have written a utility script that will get me started.

There are, as you may know, already several ways to sync your Simplenotes. Most of them, unfortunately, are tied to special note-taking apps that I don’t want install. One, Fletcher Penney’s SimplenoteSync, is more general, but works by syncing all the notes to a set of files in a single directory on your local computer. I’m looking for something a little more fine-grained than that—a way of syncing individual notes to individual files that can be anywhere on my computer.

As a first step, I wrote the following script, simplenote-index, which gathers and prints out information for all the notes on the Simplenote server.

 1:  #!/usr/bin/python
 2:  
 3:  from urllib import urlopen        # standard Python library
 4:  from base64 import b64encode      # standard Python library
 5:  import simplejson                 # http://code.google.com/p/simplejson/
 6:  
 7:  # Login credentials.
 8:  email = 'someone@example.com'
 9:  password = 'seekret'
10:  
11:  # Get my authorization token for later calls.
12:  loginURL = 'https://simple-note.appspot.com/api/login'
13:  creds = b64encode('email=%s&password=%s' % (email, password))
14:  login = urlopen(loginURL, creds)
15:  token = login.readline().rstrip()
16:  login.close()
17:  
18:  # Get the note index.
19:  indexURL = 'https://simple-note.appspot.com/api/index?auth=%s&email=%s' % (token, email)
20:  index = urlopen(indexURL)
21:  noteList = simplejson.load(index)
22:  
23:  # Print the first line of each note along with its key.
24:  baseURL = 'https://simple-note.appspot.com/api/note?key=%s&auth=%s&email=%s'
25:  for i in noteList:
26:    noteURL = baseURL % (i['key'], token, email)
27:    title = urlopen(noteURL).readline().decode('utf-8').rstrip()[:40]
28:    print '''Title:    %s
29:  Key:      %s
30:  Modified: %s
31:  Deleted:  %s
32:  ''' % (title, i['key'], i['modify'], i['deleted'])

I think the comments explain it pretty well; there’s not much to it. It logs in, gets an authorization token, then collects the information on each of your notes on the server. Here’s the sort of output to expect:

Title:    Hardware store
Key:      agtzaW1wbGUtbm90ZXIMCxIETm90ZRiF5BsM
Modified: 2009-09-08 00:24:13
Deleted:  False

Title:    Metra Eastbound Mon-Fri
Key:      agtzaW1wbGUtbm90ZXIMCxIETm90ZRij5h4M
Modified: 2010-01-01 17:51:59
Deleted:  False

Title:    Metra Eastbound Saturday
Key:      agtzaW1wbGUtbm90ZXIMCxIETm90ZRjC3h4M
Modified: 2010-01-01 17:52:33
Deleted:  False

Title:    Metra Eastbound Sunday
Key:      agtzaW1wbGUtbm90ZXIMCxIETm90ZRi35h4M
Modified: 2010-01-01 17:52:46
Deleted:  False

Title:    Metra Westbound Mon-Fri
Key:      agtzaW1wbGUtbm90ZXIMCxIETm90ZRik5h4M
Modified: 2010-01-01 17:53:05
Deleted:  False

Title:    Metra Westbound Saturday
Key:      agtzaW1wbGUtbm90ZXIMCxIETm90ZRjL3h4M
Modified: 2010-01-01 17:53:20
Deleted:  False

Title:    Metra Westbound Sunday
Key:      agtzaW1wbGUtbm90ZXIMCxIETm90ZRjT3h4M
Modified: 2010-01-01 17:53:37
Deleted:  False

Title:    Swimming City Times 2009
Key:      agtzaW1wbGUtbm90ZXIMCxIETm90ZRiG5BsM
Modified: 2010-01-01 17:54:33
Deleted:  False

Title:    Swimming Meet Event Order
Key:      agtzaW1wbGUtbm90ZXIMCxIETm90ZRiH5BsM
Modified: 2010-01-01 17:54:59
Deleted:  False

Title:    Weight
Key:      agtzaW1wbGUtbm90ZXIMCxIETm90ZRiTii8M
Modified: 2010-01-20 12:35:52.611425
Deleted:  False

Like Simplenote itself, the script treats the first line of each note as its title (the script prints only the first 40 characters of the title so the formatting doesn’t get screwed up). The important thing for the work I intend to do later are the keys. These are character strings that uniquely identify each note and are essential for scripts that read and write notes. The goal of simplenote-index is to give me those keys so I can hard-wire them into my syncing scripts.

With lots of notes, simplenote-index will give lots of output. Grep is a great way to filter the output down to a reasonable level. For example,

simplenote-index | grep -i -A 3 metra

will give me just the notes with the local train schedule

Title:    Metra Eastbound Mon-Fri
Key:      agtzaW1wbGUtbm90ZXIMCxIETm90ZRij5h4M
Modified: 2010-01-01 17:51:59
Deleted:  False
--
Title:    Metra Eastbound Saturday
Key:      agtzaW1wbGUtbm90ZXIMCxIETm90ZRjC3h4M
Modified: 2010-01-01 17:52:33
Deleted:  False
--
Title:    Metra Eastbound Sunday
Key:      agtzaW1wbGUtbm90ZXIMCxIETm90ZRi35h4M
Modified: 2010-01-01 17:52:46
Deleted:  False
--
Title:    Metra Westbound Mon-Fri
Key:      agtzaW1wbGUtbm90ZXIMCxIETm90ZRik5h4M
Modified: 2010-01-01 17:53:05
Deleted:  False
--
Title:    Metra Westbound Saturday
Key:      agtzaW1wbGUtbm90ZXIMCxIETm90ZRjL3h4M
Modified: 2010-01-01 17:53:20
Deleted:  False
--
Title:    Metra Westbound Sunday
Key:      agtzaW1wbGUtbm90ZXIMCxIETm90ZRjT3h4M
Modified: 2010-01-01 17:53:37
Deleted:  False

The -i option to grep makes the search case-insensitive, and the -A 3 option prints not only the line containing the search string, but the three lines after it as well.

With this easy one under my belt, I can start working on the more complicated syncing scripts.


  1. No, I don’t use OmniFocus anymore. It was just too much work for the good I got out of it. I’m back to my TextMate-based LGTD system, which I’ve updated a bit and really should do a short post on. 


Simple thanks

Like many people, I first learned of Simplenote through John Gruber’s Daring Fireball post from last summer. I bought the app and liked it from the beginning, even though it had some user interface problems. Using Simplenote paid off yesterday, when I became one of the WhySimplenote contest winners. (Twitter links to these two posts were my entries.)

Winners got a $30 iTunes gift card, which I’ll put to use immediately, and an upgrade to Premium service, which may give me the kick in the pants I need to start work on the syncing script I’ve had in mind ever since the Simplenote API came out. Thanks, Cloud Factory!

My favorite part of the winners announcement was the descriptions of what the other winners were doing with Simplenote. I was particularly taken with Andrew’s entry:

In my college share room, there’s five of us, but it’s my job to get the groceries. Each of us have access to the online component of Simplenote.

Whenever someone remembers something we/they need from the shops (often while daydreaming in lectures), they just type it onto the web app from their laptop.

Then, when I’m at the shops, I just take out my iPhone to look at the collaborative shopping list that’s been generated over the week. I never forget to get anything since I have a list, everyone has everything they need for the week, and there’s minimal stress since everyone can do it in their own time - not when I’m going out the door shouting to ask whether anyone needs anything.

Works great.

This is something I never would have thought of, but it fits Simplenote perfectly. Makes me sure there are more clever uses that I’m missing.


That LaTeX jQuery plugin

I’ve been seeing a link to this article, “A jQuery plugin to directly embed LaTeX into your website or blog,” on delicious.com/popular today. I have a strong interest in this topic—most recently expressed here and here—so I followed the link to have a look. It was disappointing.

Basically, the plugin

You can use other online LaTeX generators in place of Code Cogs, but they all generate images.

How is this disappointing? Let me count the ways:

  1. It won’t do inline equations. Reliance on <div>s forces each equation into its own block.
  2. You have to write the LaTeX and call the plugin for each equation.
  3. It relies on the LaTeX generator site to be up and running when someone visits your page.
  4. Although the equation is just a static image, that image has to be generated every time the page is visited.1
  5. Images don’t scale with the font size of the page.
  6. Images look like shit when printed because they’re lo-res bitmaps.

Update 12/24/09
Andreas Grech, the plugin’s author corrects me in a comment below.

Point 2 is wrong. You need only call the plugin once per page. I read the source code of his example page too quickly and came to the wrong conclusion. He calls the plugin several times on that page because he’s demonstrating various ways of using it.

Point 1 is at least half wrong. Because the plugin works off classes, you can put equations in <span>s. I’m not sure there’s a way to get LaTeX’s inline style, which is different from its display style.

His comment is a good defense of his plugin. It doesn’t persuade me to use the plugin, but it’s worth reading.

The jsMath system, which has been around for quite a while, has none of these disadvantages. OK, the equations are “typeset” on the page dynamically, but the work is done locally on the browser’s computer, so it’s well distributed and not being performed by some innocent third-party server.

You could argue that images have more universal support than the CSS tricks jsMath uses. That’s true, but apart from Mobile Safari, is anyone really using a browser that isn’t on jsMath’s supported list?

Maybe I shouldn’t be so harsh; after all, I’ve written plenty of scripts that must seem foolish to others. But if you want to use images for your equations, it’s far more efficient to generate them statically. You could even write a script that does what this plugin does, but saves the generated images to your site and inserts the appropriate <img> tags into the HTML. Such a script would be run just once—or, at most, a few times as you’re editing the page.

And if you want equations that actually look good on your page, use jsMath.


  1. I can see where this would be an advantage as you’re writing and editing the page, but once it’s done you’re just using the generator site’s bandwidth for no good reason. 


MathJax: equations on the web

When I started reading the MathJax homepage, I wondered why I would ever want to switch to it from the tried and true jsMath, which I’ve used for years to get good looking equations in my web page. Then I saw that Davide Cervone, the creator of jsMath, is one of the MathJax designers, and decided this is a project worth watching.

MathJax continues the jsMath tradition of accepting TeX/LaTeX notation to generate the equations, and intends to extend that to cover MathML as well. The MathJax preview page has many examples. Here’s the MathJax code for showing the binomial distribution on a line of its own:

\[P(E)   = {n \choose k} p^k (1-p)^{ n-k} \]

The result (apart from a difference in fonts; see below) looks like this:

P(E) = {n \choose k} p^k (1-p)^{ n-k}

That code should be familiar to anyone who’s used LaTeX. It is, in addition, the same way you’d generate the equation if you were writing an HTML page using jsMath and the tex2math plugin.

It’s also how I generated the equation for this post. I don’t use the tex2math plugin, but I modified Michel Fortin’s PHP Markdown Extra to take input like that and create the equivalent in “classic” jsMath:

<div class="math">P(E)   = {n \choose k} p^k (1-p)^{ n-k}</div>

MathJax will have the ability to use fonts likely to already be on the user’s computer and to download fonts as needed via webfonts. This will be a big improvement over jsMath, which really prefers that the user have the TeX font set already installed.

Update 12/17/09
Looking at the MathJax preview page again, I see that it is using the TeX fonts (i.e, Computer Modern), at least on systems which, like mine, have them installed. I wonder if it uses @font-face to bring those fonts to systems that don’t. The fonts are free, so I can’t imagine there being a licensing problem with them.

Since there isn’t a MathJax download link, it appears that the authors don’t consider it ready for prime time just yet. But with talented programmers and some significant sponsors behind it, MathJax has a good chance to become the standard way people write equations for the web.


David Mertz on mechanize and Beautiful Soup

Just a quick note than David Mertz, author of the excellent Text Processing in Python and a regular contributor to IBM’s developerWorks site, has a new Charming Python article that introduces the Python mechanize and Beautiful Soup libraries. These are libraries that I’ve used in some of my projects:

The development of those scripts would have gone smoother if I’d had Mertz’s article in hand before I started writing them.

Tags:


Some Simplenote stuff

Last week I had a business trip to Hartford, Connecticut and decided to try a new way of keeping the flight/hotel/etc. details. I’ve written on the past about how I keep my travel information handy. For a few years, I had all the info printed on a 3×5 card, which I kept in my Hipster PDA. After getting an iPhone, I switched to creating a PDF that looked good on the iPhone’s screen and could be moved to it via email or one of the many wifi file transfer utilities. This trip, I put the information in Simplenote. It worked out so well I moved some other information from PDF to Simplenote.

Let me start by saying that for pure information retrieval speed, nothing beats a 3×5 card in your back pocket. But when I got the iPhone I wanted to consolidate as much as I could into it, and small inefficiencies seemed like a small price to pay for that consolidation. But Files, the file transfer/viewing app I use, does take a while to open, probably because it tries to establish a WebDAV server on whatever local wifi network it can connect to. Simplenote has a much faster startup time and would be preferable if I could get the formatting right.

Here’s the OmniOutliner template I was using to create the trip card PDF.

Obviously, Simplenote, which works with plain text only, isn’t going to give me the font size and style changes that I can get in a PDF, but I’d be satisfied if I could get the flight numbers and times to line up reasonably well. This isn’t a given. Simplenote uses Helvetica, a proportional font, and doesn’t allow tabs; therefore characters will not automatically line up in columns. Luckily, two features of Helvetica allowed me to get columns that were close enough to alignment to make the trip info well structured and readable:

  1. All the numerals are of the same width. As far as I know, this is a feature of every proportional font.
  2. The space character is almost exactly half the width of a numeral.

By putting two spaces in place of every “missing” numeral, I could get times with one-digit hours to align with times having two-digit hours. And by putting the flight numbers at the ends of the lines, their unavoidable misalignment wouldn’t matter.

As you can see, the alignment isn’t perfect, but it’s good enough to quickly scan through the list down any of the columns.

In addition to launch speed, the other advantage of using Simplenote instead of a PDF is editablity. If my plans change, I can edit the note directly on my phone, something I can’t do with a PDF.

I was so pleased with the ability to align columns of numbers that I converted my Metra commuter train schedule from a set of HTML files to a set of Simplenote notes. Here’s what it looks like in the web app

and here’s what it looks like on my phone

Not as nice as the HTML version

but, like the trip info, easily readable and much faster to access.

Now that I’m using Simplenote more, and collecting more notes, I’m glad to see that its developers are adding new options for organizing notes and making its launch time even shorter. It would be nice if Simplenote had a note-by-note option for displaying the text in a monospace font; this would allow perfect column alignment for all kinds of data. I can understand why the Simplenote people haven’t done this; the only monospace font on the phone is the ugly and excessively-serifed Courier. Maybe if Apple moves Menlo to the iPhone, we’ll get a monospace option in Simplenote.

One last thing. I started writing this post in Simplenote in the Hartford airport. As I was juggling the phone and some things I shouldn’t have been eating the phone’s orientation kept shifting between portrait and landscape (something the upcoming options will be able to prevent). The transitions didn’t always go smoothly, and I ended up with a screen that looked like this:

Three keyboards for the price of one!

Tags:


Yahoo! Pipes filters for TidBITS and FSJ

I subscribe to RSS feeds from both TidBITs and Fake Steve Jobs, but they both run two kinds of links-only articles:

After months of skipping these articles when they popped up, I decided to eliminate them entirely by passing the feeds through Yahoo! Pipes filters.

The filtered feeds are:

Yahoo! Pipes was introduced with much fanfare a couple of years ago, but I don’t see much talk about it anymore. It still seems like a really useful service that I don’t take advantage of the way I should.

Update 11/8/09
The filtered feeds now link to the Yahoo! Pipe pages rather than to the RSS feeds themselves. Also, I’ve changed the Fake Steve filter to also eliminate posts with “Weekend Update” in the title.

Tags:


Adapting BBC Radio recording scripts

Reader Chris Nelms sent me a nice email over the weekend. He’s a fan of several BBC Radio 4 programs and had been recording them with Audio Hijack Pro for listening to in his car—similar to my time-shifting of Radio 2 shows, except that Chris’s recordings probably have more legitimacy, as he’s a UK resident and has presumably paid the BBC license fee. When the BBC changed its program URL scheme, it broke his AHP setup. He found my scripts for recording Radio 2 shows (first discussed in this post, then updated and put in a GitHub repository) and adapted them for Radio 4.

The most important changes were to my Python radio2.py module. That module starts like this

 1:  import datetime
 2:  import urllib
 3:  import BeautifulSoup
 4:  import re
 5:  
 6:  # The particulars for the shows we're interested in.
 7:  showinfo = {'jukebox': (5, 'Mark Lamarr'),
 8:              '70s': (6, re.compile(r'Sounds of the 70s')),
 9:              '60s': (5, re.compile(r'Sounds of the 60s')),
10:              'soul': (2, 'Trevor Nelson')}
11:  
12:  
13:  def recentScheduleURL(showday, day=datetime.date.today()):
14:    'Return the schedule URL for the most recent showday (0=Mon, 6=Sun) on or before day.'
15:  
16:    backup = datetime.timedelta((day.weekday() - showday) % 7)
17:    programDay = day - backup
18:    return 'http://www.bbc.co.uk/radio2/programmes/schedules/%d/%02d/%02d' % (programDay.year, programDay.month, programDay.day)
19:  
20:  
21:  def programCode(show):
22:    'Return the code of the program page for showname on the most recent showday.'
23:    try:
24:      schedHTML = urllib.urlopen(recentScheduleURL(showinfo[show][0])).read()
25:      schedSoup = BeautifulSoup.BeautifulSoup(schedHTML)
26:      return schedSoup.find(name='span', text=showinfo[show][1]).parent.parent['href'].split('/')[-1]
27:    except KeyError:
28:      return None

To adapt it to Radio 4, Chris changed the schedule URL string in Line 18 to Radio 4’s URL and he changed the showinfo dictionary in Lines 7-10 to reflect the shows he listens to and the days of the week on which they air. Because the BBC’s site overhaul made its URLs more uniform in structure, those changes were all that were needed.

I didn’t ask Chris which shows he records, but I did notice that, unlike the Radio 2 programs I listen to, many Radio 4 shows are daily rather than weekly. This makes crafting a workable showinfo dictionary a bit more challenging. For example, the Today program runs in the morning, Monday through Saturday. If you wanted to record each episode in the afternoon or evening of the day it aired, there are a couple of ways to go about it.

The brute force technique would be to create a showinfo entry for each day the show airs:

 6:  # The particulars for the shows we're interested in.
 7:  showinfo = {'montoday': (0, re.compile(r'^Today$')),
 8:              'tuetoday': (1, re.compile(r'^Today$')),
 9:              'wedtoday': (2, re.compile(r'^Today$')),
10:              'thutoday': (3, re.compile(r'^Today$')),
11:              'fritoday': (4, re.compile(r'^Today$')),
12:              'sattoday': (5, re.compile(r'^Today$'))}
13:  
14:  def recentScheduleURL(showday, day=datetime.date.today()):
15:    'Return the schedule URL for the most recent showday (0=Mon, 6=Sun) on or before day.'
16:  
17:    backup = datetime.timedelta((day.weekday() - showday) % 7)
18:    programDay = day - backup
19:    return 'http://www.bbc.co.uk/radio4/programmes/schedules/fm/%d/%02d/%02d' % (programDay.year, programDay.month, programDay.day)
20:  
21:  
22:  def programCode(show):
23:    'Return the code of the program page for showname on the most recent showday.'
24:    try:
25:      schedHTML = urllib.urlopen(recentScheduleURL(showinfo[show][0])).read()
26:      schedSoup = BeautifulSoup.BeautifulSoup(schedHTML)
27:      return schedSoup.find(name='span', text=showinfo[show][1]).parent.parent['href'].split('/')[-1]
28:    except KeyError:
29:      return None

The search criterion for the show name had to be a regular expression with start and end anchors (^ and $), because Radio 4 also has a daily show called Farming Today, the name of which would interfere with a search that didn’t have the anchors. Note that I’ve also made the necessary change to the schedule URL string in Line 19 (née 17).

The problem with the brute force approach is that it requires six different AHP sessions and six different audio source AppleScripts, one for each day of the week. A better approach would be this:

 6:  # The particulars for the shows we're interested in.
 7:  showinfo = {'today': ((0,1,2,3,4,5), re.compile(r'^Today$'))}
 8:  
 9:  def recentScheduleURL(showday, day=datetime.date.today()):
10:    'Return the schedule URL for the most recent showday (0=Mon, 6=Sun) on or before day.'
11:    
12:    if isinstance(showday, tuple):
13:        backups = [ (day.weekday() - d) % 7 for d in showday ]
14:        backup = datetime.timedelta(min(backups))
15:    else:
16:        backup = datetime.timedelta((day.weekday() - showday) % 7)
17:    programDay = day - backup
18:    return 'http://www.bbc.co.uk/radio4/programmes/schedules/fm/%d/%02d/%02d' % (programDay.year, programDay.month, programDay.day)
19:  
20:  
21:  def programCode(show):
22:    'Return the code of the program page for showname on the most recent showday.'
23:    try:
24:      schedHTML = urllib.urlopen(recentScheduleURL(showinfo[show][0])).read()
25:      schedSoup = BeautifulSoup.BeautifulSoup(schedHTML)
26:      return schedSoup.find(name='span', text=showinfo[show][1]).parent.parent['href'].split('/')[-1]
27:    except KeyError:
28:      return None

Here, I’ve generalized the recentScheduleURL function to accept either a tuple or an integer for the showday argument; either way, it finds the most recent episode. Taking advantage of this, I’ve used a tuple to define the days of the week on which Today runs. Now a single AHP session and a single AppleScript will suffice to record all the episodes in the week.

I like this approach so much, I’ve incorporated the tuple option into radio2.py. Although I don’t need it now, I can foresee a time when I’d want to record a daily show. My thanks to Chris Nelms for his help.

The updated version is available at the GitHub repository.

Tags:


Tribune’s head in the cloud

The top story in this morning’s Chicago Tribune business section is a fluff piece about Microsoft’s big new data center in the suburb of Northlake. I know that a fluff piece in the business section is about as common as dishonesty in Charles Krauthammer’s column, but this one is worth a look.

The gist of the piece is how cloud computing is the future of computing and, of course, how wonderful this new data center—and, by extension, Microsoft—is. The article includes a big photo, thoughtfully provided by Microsoft, sighting down a row of blue, futury-looking servers in the center. What the article does not include is any mention of the huge loss of data that hit T-Mobile’s Sidekick cell phone users this weekend. The Sidekick apparently stores all its data in the cloud, and when the servers went down, they took all that information with them. The failed servers were operated by a company called Danger, which happens to be a Microsoft subsidiary.

Now I’m sure the fluff article was researched, and probably written, a while ago. But you’d think someone at the Tribune—the writer, Greg Burns, or one of his editors—would have recognized that an article extolling the virtues of MS cloud computing would have to at least mention the Sidekick disaster. You’d be wrong.

Fortunately, the list of links on the Tribune web site does, in a small way, what the article should have done.

Tags: