• Springtime! Happy Easter!

    Posted on by Don

    I’m sitting at the Bethlehem Library right now — I was planning to maybe ride today but last night’s wet spring snow put the kibosh on that. I felt a bit cooped up and wanted to get out of the house for a bit, so I thought I’d check the new coffee shop (the Church Street Market) across the way but it’s closed on Mondays. I’ll be meeting Anne and maybe Deb at Wise Bean in a bit, but I had a hankering to do the laptop-and-café thing… Oh well, the library works.

    We had a fairly hectic weekend: Friday night was the Adult Easter Egg Hunt at Anne’s niece’s house, Saturday was a Seder at Toby & Erika’s, and yesterday we did an Easter brunch. I got in a ride on Saturday morning, and I’m glad I did since conditions were really good, and it looks like they won’t be good again for a little while. Spring’s coming, it’s just taking its time.

    Fun with Computer Maintenance: I got fed up with Adblock and installed uBlock Origin instead. Better, faster, less intrusive.

    PostGIS Fun: I merged all the bus routes into one GeoJSON file, then loaded them into the database. I then took that table and broke it into two others: one containing the bus stops, with their names, reference numbers,  OSM attributes, and geometries, and a bridging table (sans geometry) containing fields for the bus stop reference, route and stop order for all routes. Some new ideas: “select distinct on” and “window functions.” Works like a charm!

    Off to the Wise Bean…


  • Digging-Out Diary

    I kind of fell into the same trap as before — leaning too heavily on categorization for my posts — and am now stuck, playing catch-up with things I wanted to talk about that really didn’t warrant their own posts. So…

    Cali Weekend: I finally managed to go skiing this year. Julie G had an extra ski pass and a Friday off, so we went up for a morning on the slopes at Blue Mountain. She’s a bit of  a neophyte (or maybe more like “haven’t skied since college”), and was more comfortable on the easier slopes, and I was too, to tell you the truth. We did two each on the Burma Road and Paradise trails, then I did two on the intermediate Lazy Mile, and we called it a day. It was surprisingly fun, especially since I had been worried beforehand, about getting hurt or whatever.

    The next day I did a towpath ride, going the whole length to the waterfall in Easton, and on Sunday Anne and I rode the towpath to Easton for brunch at Two Rivers.

    That was two weekends ago. The weather has been pretty crappy since then, but we did manage to get out, in one heavy spring snow, to attempt some cross-country skiing: too warm, and the snow was too wet. Oh well, at least we tried…

    Reading: I read Ann Leckie’s new novel Provenance , set in the same story universe as her Imperial Raadch trilogy (but not within the Raadch itself). Fast paced and fun — especially for a book about the authenticity of venerated historical objects — I’d recommend this to anyone who liked her trilogy.

    I also finished Hilary Mantel’s Bring Up The Bodies, her second novel about Thomas Cromwell, advisor to Henry VII. This one picks up where the first left off (at the execution of Thomas More), and continues through to the execution of Henry’s second wife, Ann Boleyn. It was just as absorbing, but seemed to move a bit slower than the first.

    Next up was The Discovery of Middle Earth: Mapping the Lost World of the Celts, by Graham Robb. It had an interesting premise: the Celts were more sophisticated than we realize, and had a pretty advanced navigation system, based on astronomy and a system of survey points, which they used to build roads and locate their cities. That was the theory; in practice the book was a rambling mess, full of very enjoyable digressions and oddly useless maps and diagrams, that attempted to tell the story of the Celts. (The book reminded me, more and more as I plowed through it, of Robert Graves’s The White Goddess, and not in a good way.) There were parts I really liked, but I struggled to finish it.

    And finally, I’m in the middle of The Selected Works of T.S. Spivet, a novel by Reif Larsen. This is the story of Tecumseh Sparrow Spivet, a 12-year-old prodigy (mapmaker, scientific illustrator) living on a ranch in Montana, who wins an award and a job offer from The Smithsonian Institution — who don’t realize he’s a child — and takes off for Washington DC via freight train. That’s as far as I got, but so far the story is compelling and multi-layered and, despite appearances, not a Young Adult novel.

     


  • Data Cleanup Automation Fun

    I’m still playing with LANTA’s bus routes data, girding myself for — eventually — adding the bus routes into OpenStreetMap, but I recently decided to rebuild my data, basically starting over from scratch with the PDF’s I got from the LANTA website. My current workflow is:

    PDF  —> CSV —> cleaned-up CSV —> GeoJSON —> cleaned-up GeoJSON —> (eventually) a PostGIS database

    The conversion from PDF to CSV was automatic, using a Java program I found online, and the CSV cleanup — fixing things like transposed latitude/longitude, missing minus signs etc — was done manually (using a text editor and LibreOffice Calc), which was relatively uneventful but  laborious — there are 68 individual bus routes, each with its own file.

    Things got even more laborious with the next two tasks. My first go-around with the conversion to GeoJSON was done manually within QGIS: load each of the CSV files, individually filling out the required parameters for each one. I wasn’t looking forward to converting the files individually, so I wrote a Python script to save all my route layers in GeoJSON format. (Just as an aside, I have to say I really like GeoJSON as a vector file format: I find it much easier to work with than the dated, unwieldy Shapefile standard; it’s also easier to open and work with in the JOSM editor. All my new data, if it’s not going into the database, is getting stored as GeoJSON files.)

    The “GeoJSON cleanup” is where I massage the data into the forms I want: some of the table columns are unnecessary, some need to be renamed, there are a few extra columns to add (and populate), and finally I wanted to convert the LANTA bus stop names format (in  Robbie-the-Robot-style ALL CAPS) to something a little easier o the eyes. Doing this manually would have been beyond laborious, so I wrote another Python script to massage the route files. This turned out to be more of a learning experience — as in, multiple versions of the program failed spectacularly until I got it right — and probably took longer than the brute force, manual changes approach would have, but at least it wasn’t laborious…

    I’m still not really sure why this version worked when others did not, but here is my code:

    # script to run through all visible layers,
    # adding/deleting/renaming fields as required
    # to convert from LANTA bus data to something
    # more like OpenStreetMap bus stop attributes
    # it also properly capitalizes feature names

    from PyQt4.QtCore import QVariant
    import re

    # some functions and regular expressions for string manipulation
    def match_lower( matchobj ):
    return matchobj.group().lower()
    def match_upper ( matchobj ):
    return matchobj.group().upper()
    reg = re.compile( 'Lati|Longi|Time|At Street|On Street|Direct|Placem' )
    reg_ns = re.compile( r'\(ns|fs|mid|off\)|\bncc\b|\bhs\b', re.IGNORECASE )
    reg_nth = re.compile( r'[1-9]?[0-9][a-z]{,2}', re.IGNORECASE )
    reg_leftParen = re.compile( r'([^\s])(\(\w*\))' )
    reg_rightParen = re.compile( r'(\))([^\s])' )
    reg_space = re.compile( r'\s+' )

    for layer in iface.mapCanvas().layers():

    # reset these variables for each new layer processed

    myLayerName = layer.name()
    highwayExists = False
    networkExists = False
    operatorExists = False
    publicExists = False
    busExists = False
    routeExists = False
    delList=[]

    layer.startEditing()
    pr = layer.dataProvider()

    # change the names of some fields
    fields = pr.fields()
    count = 0
    for field in fields:
    fieldName = field.name()
    print "test for changing field name " + fieldName + " count ", count
    if ( fieldName == 'Public Information Name' ):
    print 'changing to name'
    layer.renameAttribute( count, 'name' )
    if ( fieldName == 'Stop Number' ):
    print 'changing to ref'
    layer.renameAttribute( count, 'ref' )
    if ( fieldName == 'Stop Order' ):
    print 'changing to stop_order'
    layer.renameAttribute( count, 'stop_order' )
    count += 1
    layer.updateFields()

    layer.commitChanges()
    layer.reload()
    layer.startEditing()
    pr = layer.dataProvider()

    # delete some fields
    fields = pr.fields()
    count = 0
    for field in fields:
    fieldName = field.name()
    print "test for deleting fields " + fieldName + " count ", count
    m = reg.match( fieldName )
    if m:
    print fieldName
    delList.append( count )
    print delList
    count += 1
    pr.deleteAttributes(delList)
    layer.updateFields()

    layer.commitChanges()
    layer.reload()
    layer.startEditing()
    pr = layer.dataProvider()

    # add some fields, checking if they don't already exist
    count = 0
    fields = pr.fields()
    for field in fields:
    fieldName = field.name()
    print "test for adding fields " + fieldName + " count ", count
    if( fieldName == 'highway' ):
    highwayExists = True
    print 'highway', count
    if( fieldName == 'network' ):
    networkExists = True
    print 'network', count
    if(fieldName == 'public_transport' ):
    publicExists = True
    if(fieldName == 'bus' ):
    busExists = True
    if ( fieldName == 'operator' ):
    operatorExists = True
    if( field.name() == 'route' ):
    routeExists = True
    count += 1
    if( not highwayExists ):
    print "adding highway"
    pr.addAttributes( [ QgsField("highway", QVariant.String) ] )
    layer.updateFields()
    if( not networkExists ):
    print "adding network"
    pr.addAttributes( [ QgsField("network", QVariant.String) ] )
    if( not operatorExists ):
    print "adding operator"
    pr.addAttributes( [ QgsField("operator", QVariant.String) ] )
    if( not publicExists ):
    print "adding public_transportation"
    pr.addAttributes( [ QgsField("public_transport", QVariant.String) ] )
    if( not busExists ):
    print "adding bus"
    pr.addAttributes( [ QgsField("bus", QVariant.String) ] )
    if not routeExists:
    print "adding route"
    pr.addAttributes( [ QgsField('route', QVariant.String) ] )
    layer.updateFields()

    # add attributes to new fields for all features
    for feature in layer.getFeatures():
    feature['highway'] = "bus_stop"
    feature['network'] = 'LANTA'
    feature['operator'] = 'Lehigh and Northampton Transportation Authority'
    feature['public_transport'] = 'platform'
    feature['bus'] = 'yes'
    feature['route'] = myLayerName
    layer.updateFeature(feature)
    layer.updateFields()

    # clean up text in name field
    fieldName = 'name'
    for feature in layer.getFeatures():
    myString = feature[fieldName]
    myString = myString.title()
    myString = reg_ns.sub( match_upper, myString )
    myString = reg_nth.sub( match_lower, myString )
    myString = reg_space.sub( ' ', myString )
    myString = reg_leftParen.sub( r'\1 \2 ', myString )
    mystring = reg_rightParen.sub( r'\1 \2', myString )
    print myString
    feature[fieldName] = myString
    layer.updateFeature(feature)

    layer.commitChanges()
    layer.reload()

    Once I got this up and running, I realized that I wanted t do some more preliminary cleanup on my spreadsheets, so I was back to square one. I couldn’t really find out how to do a bulk load of my CSV files into QGIS, and I realized that QGIS was just using ogr2ogr under the hood, so I decided to do the bulk converting, CSV to GeoJSON, with a shell script that calls ogr2ogr. Yet another learning curve later, and it works great. More code:

    for i in *.csv
    do
    echo $i
    tail -n+2 $i | ogr2ogr -nln ${i%.csv} -f "GeoJSON" ${i%csv}geojson \
    CSV:/vsistdin/ -oo X_POSSIBLE_NAMES=Lon* -oo Y_POSSIBLE_NAMES=Lat* -oo KEEP_GEOM_COLUMNS=NO
    ogrinfo ${i%csv}geojson
    done

    It struck me then, that all my data was really text, and so working with it in a more unix-ey fashion, with shell scripting and text manipulation programs (sed, awk) to do the conversions directly from the CSV files, was probably my better strategy. Oh well, I did the first part of it with bash at least, and the Python script works well enough.

    UPDATE:

    I did it anyway, using awk to add/subtract/rename/Capitalize the CSV data before running it through ogr2ogr. The code (below) is about 35 lines (as opposed to 150 for the Python script, which only does part of the job anyway) and runs really fast:

    #! /bin/bash
    for i in *.csv
    do
    echo Converting $i to GeoJSON
    cat $i | awk -F "," -v rtname=${i%.csv} 'BEGIN {
    }
    $2 != "" && /Stop/ {
    print "ref,Latitude,Longitude,name,stop_order,highway,public_transport,bus,network,operator,route"
    }
    $2 != "" && /^[0-9]{4,4}/ {
    string = tolower($8)
    n=split(string,a," ")
    string=toupper(substr(a[1],1,1)) substr(a[1],2)
    for(i=2;i<=n;i++) {
    string = string " " toupper(substr(a[i],1,1)) substr(a[i],2)
    }
    t_str = "\(ns\)|\(fs\)|\(mid\)|\b[nN]cc\b|\b[Hh]s\b"
    if ( match( string, t_str, match_array ) ) {
    the_match = toupper(match_array[0])
    gsub(t_str, the_match, string)
    }
    gsub(/ *\(/, " (", string)
    $8 = string
    print $1 "," $2 "," $3 "," $8 "," $9 ",bus_stop,platform,yes,LANTA,Lehigh and Northampton Transportation Authority," rtname
    }' > test1.csv
    cat test1.csv
    ogr2ogr -nln ${i%.csv} -f "GeoJSON" ${i%csv}geojson test1.csv -oo KEEP_GEOM_COLUMNS=NO
    ogrinfo ${i%csv}geojson
    rm test1.csv
    done


  • A Wrinkle In Time

    Anne and I saw this last night. Meh — I was not impressed. I’ll grant that it was pretty close to (a simplified version of) the book, and that I am not the target audience for the movie (or the book for that matter) by about four decades, and that I did enjoy large stretches of the movie, but there were other large stretches that just left me annoyed. On the whole, the movie left me with the same, sanctimonious  “oh the wonder of it all!” vibe of a 90’s era computer advertisement, one targeting parents for their kids’ education, full of photogenic kids staring rapt at math-ey and science-ey graphics. I was seriously rolling my eyes by about 20 minutes into the movie…

    The casting was also kind of spotty, and in a weird way: the more likely I knew an actor, the worse they were here. That is, the kids were all pretty good, but Oprah was awful — more of that sanctimony, maybe.

    Anne kinda-sorta agreed with my “meh, kid stuff” assessment, but she didn’t think it was as bad as all that. She read the book as an adult though, reading it to/with Ben and Emmi and seeing it through their eyes, so she may have been more sympathetic.

    Anyway, it was a night out, and I actually would recommend it for tweens, especially fans of the book.


  • Watching The Snow Fall

    Posted on by Don

    The last storm (on Wednesday) came with a lot of hype, and I was disappointed to wake up with no snow on the ground, just rainy surfaces rather than the advertised snowpocalypse, but then snow started falling midmorning and it came down all day. It was a heavy, wet snow, but there was nowhere as much as they were calling for: we got maybe 4″ on the ground rather than the expected 12″ — the storm tracked east of us, apparently, and we missed the brunt of it, though areas west and north of us got some accumulation. Go figure.

    I got out Tuesday for a nice, pre-snowpocalypse ride on the towpath, which was in great shape — conditions had been improving, and I expected this would be the last chance this week for a decent ride. I rode down to the waterfall in Easton and back, really pleasant. After that I got my act together then went over to Southside — a little early, so I could get a few photos, for some OpenSteetMap mapping at the new parking garage — to meet Anne for dinner at La Lupita before going to see Colson Whitehead talk at Zoellner Arts Center.

    That was really good: he started with “I was born a poor black child” à la Steve Martin, did several readings from The Underground Railroad, talked about his career, his writing process, and writing as a part of his life in general. The Underground Railroad was the subject of a number of workshops at Lehigh; the event was well-attended by students as well as the usual bookish crowd, and the Q&A afterward was pretty decent.

    Wednesday was spent waiting for snow, watching snow from inside while I did computer stuff and Anne did her spinning, and then watching the storm peter out… I got in another bike ride yesterday after some trivial shoveling, just going over to the CAT office to help Scott move some shelves, the first step in his office reconfiguration project. It was a bit chilly, but the air felt fresh and very spring-like.

    Today I woke up and looked outside — snow flurries.


  • Annihilation

    Posted on by Don

    I saw this one on Sunday, with John R and Brian & Katie. Verdict: pretty good, with an interesting story and some good visuals, but in the end it was a disappointment, especially after seeing Black Panther.

    The story here is that, after a meteor crashes, in the middle of a state park in the southern US, the crash site becomes the center of a slowly-expanding area, with ominous, anomalous behavior and an odd rainbow-like light. The secret researchers call it “The Shimmer,” but no one and nothing sent into the zone has returned.

    Except one guy, a soldier who reappears at his home, a year after his mission, with no memory of how he got there. He immediately becomes sick, he and his wife (a professor and cancer researcher) are taken to the secret base, and she becomes one of five women who are the next research team to enter The Shimmer.

    From there, the story is a bit like a cross between Alien and Deliverance, with a bit of Solaris thrown in. The team is attacked by mutant creatures, find evidence that the earlier teams either mutated, or went crazy and killed each other, or both, and then they start losing it in the same ways. None make it out, except the professor.

    As I think about it now, I actually liked the movie, and am definitely going to read the award-winning book on which it’s based.


  • O Brave New World!

    Posted on by Don

    I finally bit the bullet: I put this site on SSL.

    I’ve been wanting to do this for a while, though my motives have been a bit unclear unless I count Internet street cred. The idea is to be able to use HTTPS rather than the HTTP protocol, where the “S” at the end stands for “secure:” the connection between my server and your browser is encrypted (using SSL, or TLS for the pedantic), in a way that keeps the data transfered between them secret, while also verifying that the data source is actually me. This comes in handy if we don’t want third parties snooping on our connection, or modifying the data by adding malware or advertisements. I’m not sure how much those are real problems for my little blog, but all the cool kids are moving to HTTPS so I guess I’d better do it as well.

    The process was a bit time consuming, but it turned out to be easier and more straightforward than I thought it would be. The verification is done via a cryptographically-signed “certificate” from an already-trusted source (a certificate authority), and getting a certificate — for free — from a trusted source was the hardest part of the process — but even counting my learning curve (but not my fretting/waffling), getting and installing the certificate took all of 10 minutes. There were a few more hoops to jump through with my host (another 10 minutes, plus more waffling), but cPanel did most of the work, and now it’s done: my site communicates via the secure HTTPS protocol, redirecting from HTTP if necessary. You can see the green lock up in the address bar, which indicates the secure connection.

    Now you can enjoy my site, knowing that it truly is me, talking to you, in secret…

     


  • Black Panther

    Posted on by Don

    Anne and I saw this on Saturday — wow. I am not a comic book, “Marvel Universe,” or big-budget movie fan by any stretch of the imagination, but this was one of the better movies I’ve seen in a while. It helps that the story basically stands by itself, without any need to know much else about the story world it inhabits, and it also helps that the big-budget effects were done both well and tastefully, but what really made this movie was that the story it told was actually really good.

    Sorry, spoilers ahead! Continue reading  Post ID 1127


  • Montreal Photos

    Posted on by Don

    As promised, here are my photos from Montreal:


  • Playing Serious Catch-Up

    Morning weigh-in: 189.0#, 14.0% BF

    Today I followed “weigh-in protocol” (do it pre-shower), and it looks like my body fat is back in the normal range. So, there’s that…

    Reading: I got two books on maps from the library, but they are surprisingly boring.

    My computer project du jour is to bring my photo database, and my  Flickr account, up to date. I’ve been downloading from Dropbox into Shotwell (my photo program), doing a bit of curating and then uploading the keepers into Flickr — I am now getting close to the end of 2014,  with thousands of photos to go. Sigh.