• Tag Archives towpath
  • Things that have to do with the Lehigh or Delaware Towpaths, biking towpaths, or biking the D&L.

  • Towpath Life

    I have a bell on each of my offroad bikes, but I normally don’t use them — I prefer verbal communication when I overtake someone, since it’s more effective and (in my opinion) way more polite. Yesterday, on the way back from the Museum, I came across a little old lady walking her German shepherd, and called out my usual “hi, passing on your left!” She moved over to the left (oops!), but by that time I’d stopped, and we were doing that neighborly, chuckling “yes, hello, passing you on the towpath” sort of greetings and negotiation, when her dog just absolutely went bonkers — barking, snarling, leaping at me at the end of its leash. It looked for all the world like an attack dog in some kind of video.

    It dragged the woman over as it came at me, completely out of her control — luckily it was not as hardcore as it wanted me to believe, because when it attacked it went for my ankle. Luckily again, I was wearing street clothes, including hiking boots, and the dog couldn’t really get a purchase on them. (I’m guessing it happened too fast for me to react, since I felt strangely calm throughout the entire situation.) All the while the woman was yanking at the dog, and when she finally got it under control I got a little snarky and said “good thing your dog was on a leash.”

    I don’t think she got it, because her reply was “well, it is the law.” I just rode off…

    Her parting shot, once I was down the trail? “If you had a bell you could have warned me!” I was tempted, but I didn’t ring my bell.


  • I Care About Nutrition, Part Infinity

    I was supposed to go for a lunch-hour ride today with Greg H, but his office was shorthanded and he had to bail. I went out anyway, just for an easy towpath ride, but by about five miles I was sluggish and exhausted and couldn’t go on. I stopped at Farmersville Road, ate some shot blocks and two GU packs, sat for a while for them to take effect, and then moseyed my way home. I think I guessed right: whatever ailed me was nutritional, and I felt much better on the way back.

    I also think I lucked out, because with Greg I’m sure  we would have done either Lehigh or Sals, and I would have been dying. I still have no idea what could have made me so drained — I haven’t overdone the physical activity lately, and we’ve been eating much better, since our return, than we have in about a month.

     


  • Befuddle Oneself Methodically

    I’ve been thinking a lot about the economic impact of the towpath lately, and have been looking to the Great Allegheny Passage as a model, where many food and lodging places have popped up to cater to the cycle touring crowd.  I know that the D&L Corridor people are also looking at various businesses and how the towpaths might impact them, but I believe that they are looking at it from a county-wide perspective, when they should probably really be looking at impact within a few blocks of the path, and or at least within about a mile of the river — would you decide to take a fully loaded touring bike miles out of your way, and probably up some hill to get away from the river, just for say, lunch, if you didn’t absolutely have to? So, that got me thinking about the question: what restaurants, hotels, bike shops, and other amenities are actually within a mile of the relevant sections of the  Delaware and Lehigh Rivers?

    (This was a good first approximation, but it’s surely a naive way of looking at the problem, since there are many places within a mile of the river as the crow flies, that are not actually within a mile, or maybe even many miles, of anyplace accessible from the towpath — and places that will see towpath business will need to be located within a matter of blocks, not miles, from towpath access points. But I realized all that later as I thought more about the overall situation, and my first analysis of towpath business prospects was what I worked on first.)

    The way I looked at it, my original problem broke down into two parts. First, what is the region within one mile (or whatever distance) from the river, and second, what are the amenities within that region? The first part was fairly straightforward, but the second, which looked like it would involve some kind of Google Maps search — and eventually it did — turned out to be more complicated than I thought…

    Partial map of Lehigh County and River
    Nifty QGIS: Five minutes of work to get the buffer zone.

    I used QGIS to deal with the first part. I took as my reference some Pennsylvania aerial photos, plus a property map of Lehigh County, and created a new line vector (in a projection that uses feet as a unit of measure), following what looked like the middle of the Lehigh River from about Laurys Station to just past Bethlehem, and then I used the “Create Buffer” geoprocessing tool to create a vector polygon buffer region around that section of river, whose distance from my line vector was 5280 feet, in other words, one mile. That part worked great, but what to do with my buffer region?

    My first thought was to take the buffer vector and export it to a KML file, import that KML file into a custom Google Map (using Google’s “My Maps” personal map creation/editing feature), and then “do something” with it. That all worked great as well, up until the “do something” part — the KML file, and the personal map, were not much use when it came to customizing a map search.

    I did find online, however, that there were some things you could do with polygonal regions, and testing locations (such as the ones returned from search results) to see if they fell within those regions, using the Google Maps API. This added two new steps: first I had to re-export my buffer region, this time as a GeoJSON file because that was what the API would accept, and I also had to sign up for an API key from Google Maps. Both of these were also straightforward and easy to do.

    The final step was to put it all together: make a web page, and (with some javascript), load and draw the GeoJSON file, run a search (for restaurants, in my experimental code), and then find and display results that fell within my region. Code code code, give it a try… nothing. I was able to load the file and see my region, but no place results would be displayed.

    Turns out, there is more than one Polygon type in the API, and the one created by loading a GeoJSON file is different than the one you can test locations against; I would have to convert my polygon from one form to another. (This seemed to me a bit much, especially since I thought I should have been able to load the original KML file and it would “just work.” After all, isn’t KML a Google thing, and kind of a standard?) No matter, the conversion process from one polygon to the other looked as straightforward as every other step so far, so I just added it to the end of the task chain. Code code code, give it a try… nothing, and here is where it started to get really frustrating.

    I couldn’t for the life of me figure out what was going wrong, it looked like I did things exactly the way I was supposed to but my new, converted polygon could not be made, and it looked like the original polygon actually was empty, even though it was drawn on screen. I eventually used a callback routine from the GeoJSON loading function to get the polygon coordinates, and for some reason that worked.

    That gave me my clue: the “some reason” was that the callback was not executed until after the file was done loading, so the conversion routine had something — a non-empty original polygon — to work with, while in my original code the rest of the script wouldn’t wait for the file to finish loading before continuing, so there really was nothing to work with yet when I tried to do the conversion. That took three paragraphs to write, but more than a day to work out…

    I didn’t really like my solution: if you’re forced to use callbacks like that, you end up going down the rabbit hole, callback after callback after callback, just to get some semblance of sequential execution. (Meantime, I found that some methods did not suffer from these kinds of problems, they seemed to wait for the data to load before trying to work on it. Strangely enough, all the simple API examples I found at Google used these methods instead of the one I needed.) Eventually I set up a wrapper function to hide the messy details and just get me my goddamned polygon from the goddamned GeoJSON file.

    Anyway, here is my demo map:

    UPDATE (7/26/2018): This map will stop working after July 30th, because the “radar search” function (see script below) has been deprecated by Google Maps. I may take some time to update the script — which I’ll mark with another update — but then again I may not, because this is a low-usefulness, low-visibility, low-priority thing for me, and also because fuck Google.

    And here’s my script. Most of this is based on Google Maps API examples, but the function getBuffer() loads the data, and createBufferPolygon() is the wrapper that creates the polygon object:

      var myNewMap;        // the google map
      var myPlaceService;  // object for google places api
      var myBufferPoly;    // the polygon that holds the buffer region
      var myInfoWindow;    // info window for the selected place
          
      // the callback function from loading the API, where everything actually happens
      function initMap() {
        myNewMap = new google.maps.Map(document.getElementById('map'), {
          center: {lat: 40.672628, lng: -75.422778 },
          mapTypeId: google.maps.MapTypeId.TERRAIN,
          zoom: 11
        });
            
        var bikeLayer = new google.maps.BicyclingLayer();
        bikeLayer.setMap(myNewMap);
        myBufferPoly = createBufferPolygon(
          'lbuf2.geojson',
          'lehigh',
          myNewMap,
          true,
          'green'
        );
        myPlaceService = new google.maps.places.PlacesService(myNewMap);
        myInfoWindow = new google.maps.InfoWindow();
    
        getSearch();
      }
          
      // this is the wrapper function, which calls the function that loads the GeoJSON file
      // after creating the polygon to hold the buffer region 
      function createBufferPolygon(url, featureName, map, isVisible, polyFillColor) {
        var bufPoly = new google.maps.Polygon({
          map: map,
          clickable: false,
          visible: isVisible,
          fillColor: polyFillColor
        });
        getBuffer(url, featureName, bufPoly);
        return bufPoly;
      }
          
      // this function loads a GeoJSON file containing a named polygon
      // then adds it to the given polygon object
      function getBuffer(url, featureName, poly) {
        var bufGeom;
        var bufferData = new google.maps.Data();
        bufferData.loadGeoJson(
          url, 
          {idPropertyName: 'name'},
          function(featarr) {
            bufGeom = bufferData.getFeatureById(featureName).getGeometry();
            poly.setPaths(bufGeom.getAt(0).getArray());
          });
      }
          
      // finds all restaurants within 15km of a certain location 
      function getSearch() {
        var request = {
          location: {lat: 40.703117, lng: -75.416561 },
          radius: 15000,
          keyword: 'restaurant'
        };
        myPlaceService.radarSearch(request, displayResults);
       }
          
        // displays search results that fall within the buffer region
        function displayResults(searchResults, searchStatus) {
          if (searchStatus !== google.maps.places.PlacesServiceStatus.OK) {
            console.error("getSearch error: " + searchStatus);
            return;
          }
          for (var i=0, result; result=searchResults[i]; ++i) {
            if (google.maps.geometry.poly.containsLocation(
              result.geometry.location, myBufferPoly)) {
                addMarker(result);
              }
            }
          }
          
      // adds marker for selected places
      function addMarker(place) {
        var marker = new google.maps.Marker({
          map: myNewMap,
          position: place.geometry.location,
          icon: {
            url: 'http://maps.gstatic.com/mapfiles/circle.png',
            anchor: new google.maps.Point(10, 10),
            scaledSize: new google.maps.Size(10, 17)
          }
        });    
        google.maps.event.addListener(marker, 'click', function() {
        myPlaceService.getDetails(place, function(result, status) {
          if (status !== google.maps.places.PlacesServiceStatus.OK) {
            console.error(status);
            return;
          }
          myInfoWindow.setContent(result.name);
          myInfoWindow.open(map, marker);
          });
        });   
      }
    

    That’s a lot of work for something that solves the wrong problem! My next look at this will likely just involve finding the access points, and doing Google searches near each one — soooo much less elegant…