Thursday, June 18, 2009

Kitsap Loop

Today was the first successful completion of my "Kitsap Loop" journey, which is essentially going south through Tacoma, west over the Narrows, north through Kitsap County, and back east via the Washington State Ferries. The "weakest link" of this trip is Kitsap Transit's Purdy Connection, which runs only five times each day over the course of the day. This connection is also why I haven't been able to complete this trip on my previous attempt, since I arrived too early and didn't want to wait for two hours.

Like the Whidbey Island journey, I will only mention highlights. This trip isn't called a "grand tour" because it didn't cover much of Kitsap County, unlike the Whidbey trip.

  1. CT 414 Seattle
  2. ST 594 Tacoma
  3. PT 2 TCC (originally intended to take a later PT 1 TCC)
  4. PT 100 Purdy
  5. KT Purdy Connection
  6. KT Port Orchard to Bremerton
  7. WSF Bremerton to Seattle
  8. MT 99 International District
  9. CT 414 Mountlake Terrace

Unlike previous journeys, which always started at either my house, Lynnwood, or Bothell, I decided to take advantage of CT's new parking garage at Mountlake Terrace Transit Center. It was mostly empty, but the TC was very nice, new, and much cleaner than many others. They even had a cable-stay walkway! Expecting a 40-foot Invero to pick me up, I was even more surprised when a 278XX bus picked me up.

I had hoped to make a quick-and-painless transfer to my next bus on Stewart in Seattle, but construction got in the way, and I had to run along Lenora to catch my next bus. I really wasn't expecting it to be that annoying.

Another painful transfer was to my next bus, which arrived just as I was getting my bearings. That bus smelled just like a locker room, which was disgusting the entire way.

I had originally intended to have twenty-minute headways, making the above dashing around completely unnecessary, and ended up accumulating time until I was an hour early at TCC. The driver (the same one I rode with on my previous attempt) was a bit mystified when I said, "I'll just take the next one." Instead of just waiting for an hour, I went to an IHOP a couple blocks away for lunch.

At Purdy, I had to wait another hour (as I had planned initially). After watching some teenagers go into the woods next to the transit center and come out, I decided to investigate the path for myself. The only notable thing back there was a fairly fast-flowing creek, as well as a lot of trash that had been left before.

The bus for the Purdy Connection was a dial-a-ride-transit/access bus, which made sense since it acted like Metro's DART service (somewhat fixed-route with some deviations). Those on board were friendly, and chatted as though they knew each other. Fortunately, there were no deviation requests, and it was a straight-shot along SR-16 to Port Orchard, and my hair got a beating by the wind.

The foot ferry was a fun ride, and was even smaller than King County's Water Taxi. It also was a very short trip, since I could see what I knew to be Bremerton from Port Orchard.

Having successfully completed a link I had never done before, I ended up in Bremerton. While I had hoped to meet a friend of mine there after missing the first ferry transfer, she couldn't make it in time before the next ferry left. Instead, I had some ice cream, and even saw two Orion I buses!

I barely made the free route 99 after landing in Seattle, and was lucky to even see it at all. It was over ten minutes late, or ten minutes early. Transferring to the 414 wasn't so chancy; it came only twenty minutes after I had arrived, with the Invero I had expected earlier. Is it just me, or do all of CT's Inveros have broken reclining mechanisms?

This was truly an epic trek, but I likely won't do it again alone due to that darn Purdy Connection. I don't think I'll ever forget this one.

Tuesday, June 16, 2009

Whidbey Grand Tour #3

That's right, today was my third all-day bus tour around Whidbey Island. Whidbey Island is nice to visit because of the wide, open fields, dense forests, and small-town feel. There are also several funny street names, such as "Power Road," "Spyglass Road," several variations on "Bluff Road," and "Useless Bay Road." Views of the water are common along the bus routes, such as Deception Pass, and in some places the roads are mere feet from water's edge. Last, but not least, all Island Transit routes are fare-free, as they are all tax-funded. Their buses are primarily cutaway ElDorado models, with some Gillig Phantoms.

While I won't go into in-depth detail of every leg of the trip, I will mention everything of note I can think of. There won't be a very smooth flow between paragraphs. That said, here's a list of the routes I took in order:

  1. ST 532 Everett
  2. IT 412C Camano Island
  3. IT 411C Mt. Vernon
  4. IT 411W Oak Harbor
  5. IT 1 Clinton
  6. WSF Clinton to Mukilteo
  7. CT 113 Lynnwood
  8. CT 121 UW-Bothell

While in Everett, two things of note happened. First, I saw that Everett Transit has purchased multiple diesel-electric hybrid buses. Secondly, I purchased something at Everett Station's cafe called a "Frozen Xplosion," which is pretty much fruit juice blended with ice. Last time I had bought this, they were out of the peach juice, and made something closer to an Italian soda instead, but I preferred this time with the actual peach juice. A Coast Starlight train stopped by during my wait for the next leg of the route, which hadn't happened before.

The widening of a certain section of SR-20, which had been underway during my last visit, was completed this time around. A section later along the route, however, was currently under heavy construction. One reason I like the cutaway buses is that they tend to give a rough (but fun) ride, and this was an extreme case. The driver even said "Prepare to grit your teeth" just before the rough section.

Apparently wood burning is legal on Whidbey Island, as someone had a large pile of wood burning a few blocks away (but in clear sight since it was an open area) just before the driver changeover. The smell was very strong where it passed us, but didn't last so long.

Riding Washington State Ferries is generally fun, and unlike the larger ferries in Seattle the Clinton-Mukilteo run doesn't have a separate pedestrian ramp. I never really figured out the order involved until today. Upon docking, passengers then vehicles are unloaded, then priority vehicles, regular vehicles, and pedestrians are loaded.

As per the "tradition" established after every trip, I stopped by the Mukilteo ferry port's Ivar's eatery. It was the only real meal I ate in around nine hours, and was welcomed. I barely made catching the 113 after this, but was treated to a ride in one of CT's new (281XX) buses.

That's pretty much everything of note on my trip. My last thoughts here are that buses generally smell bad, primarily due to spray deodorant and cigarettes. I can't wait until my trip through the Kitsap Peninsula!

Monday, June 15, 2009

hwsched

As I've mentioned earlier, I've been working on a homework scheduler. Work isn't anywhere near done, but it's functional enough (but not tested enough). Here's what I have so far:hwsched-20090615.zip

To use this script, the following files must be symlinks or hard links to hwsched.py:

  • classes
  • exams
  • assignments
  • instructors
  • quarters
  • sections

dumpaudio

I was having all sorts of trouble trying to figure out how to extract audio from any video file into a WAV (signed 16-bit, little-endian) format. I was fortunate enough to find something that worked universally, so here it is:

dumpaudio

#!/bin/sh
if [ $# -ge 1 ]; then
IN=$1
OUT=$1.wav
if [ $# -ge 2 ]; then
OUT=$2
fi
ffmpeg -i "$IN" -f wav -acodec pcm_s16le "$OUT"
else
echo "Usage: $0 in [ out ]" >&2
fi

Wednesday, June 10, 2009

pdfsearch.py

For the longest time, I've wanted to search PDF files from the command line. Now, I can with pdfsearch.py! This program uses pyPdf to look for pages containing strings that match the provided regex, but due to the messy output of pyPdf, it won't print the matching lines.

pdfsearch.py

#!/usr/bin/python
from optparse import OptionParser
from sys import argv
from pyPdf import PdfFileReader
from os import walk
from re import compile as re_compile
from re import IGNORECASE
from os.path import join

parser = OptionParser(description = 'Search for text in PDF files.', usage = '%s [ options ] term [ file1..fileN ]' % argv[0])
parser.add_option('-i', '--insensitive', action = 'store_true', dest = 'insensitive', help = 'Search case-insensitively.', default = False)

def pdfgrep(expr, file):
pdf = None
with open(file, 'rb') as f:
pdf = PdfFileReader(f)
for i in xrange(pdf.getNumPages()):
content = pdf.getPage(i).extractText().strip()
if expr.search(content):
yield i



argv = [unicode(i, 'utf8') for i in argv]
options, args = parser.parse_args(argv[1:])

optionmap = {
'insensitive' : IGNORECASE
}


if len(args) >= 1:
term_flags = 0
for key, value in optionmap.iteritems():
if getattr(options, key):
term_flags |= value
term = re_compile(args[0], term_flags)
paths = ['.']
if len(args) >= 2:
paths = args[1:]
for path in paths:
for dirpath, dirnames, filenames in walk(path):
for filename in filenames:
if filename[-4:] == '.pdf':
fullfilename = join(dirpath, filename)
pages = list(pdfgrep(term, fullfilename))
if len(pages) > 0:
print u'%s:%s' % (fullfilename, u', '.join([str(i + 1) for i in pages]))
else:
parser.error('No search term provided.')
% python pdfsearch.py -i undo ../Notes/CSE444
../Notes/CSE444/2009-05-07:QuizSection_Midterm.pdf:4
../Notes/CSE444/PDF/lecture14.pdf:3, 6, 10, 11
../Notes/CSE444/PDF/lecture13.pdf:17, 18, 22
../Notes/CSE444/PDF/lecture09-10.pdf:2, 16, 31, 32, 36, 37, 39, 40, 41, 42, 43, 45, 48, 49, 58, 59, 60, 62, 63, 65
../Notes/CSE444/PDF/lecture11.pdf:30

Wednesday, June 3, 2009

svg_standalone.py

I've finally realized the merits of standalone SVG files using data URIs, so I wrote this simple Python script to convert an SVG with >image xlink:href="filename" ... /< to one with data URIs instead.

svg_standalone.py

#!/usr/bin/python
from sys import stderr, argv, exit
from base64 import b64encode
from os.path import isfile
from mimetypes import init, guess_type
from xml.dom.ext.reader import Sax2
from xml.dom.ext import Print

mp = 'http://www.w3.org/1999/xlink', 'href'
init()

if len(argv) == 2:
fname = argv[1]
# Input
with open(fname) as f:
doc = Sax2.Reader().fromStream(f)
# Replace
root = doc.documentElement
imgs = root.getElementsByTagName('image')
for img in imgs:
href = img.getAttributeNS(*mp)
if isfile(href):
mtype = guess_type(href)[0]
b64data = None
with open(href) as f:
b64data = b64encode(f.read())
if not b64data is None:
img.setAttributeNS(*mp, value = ''.join(['data:', mtype, ';base64,', b64data]))
else:
print >>stderr, 'Unable to generate Base64 data:', href
else:
print >>stderr, 'Unable to find image:', href
# Output
if fname[-4:] == '.svg':
fname = fname[:-4] + '.standalone' + fname[-4:]
else:
fname = fname + '.standalone.svg'

with open(fname, 'w') as f:
Print(doc, stream = f)
else:
print >>stderr, 'Usage: %s file.svg' % argv[0]
exit(1);