Smathermather's Weblog

Remote Sensing, GIS, Ecology, and Oddball Techniques

Archive for the ‘Recreation’ Category

Parks Data Cake, part deux

Posted by smathermather on April 2, 2014

Following up on my previous post, I have started to detail the how of our parks map rendering works, including a GitHub repository with all the code and data to build your own in TileStache. One of these days, we’ll port this to TileMill, but in the mean time, it works and works wonderfully.

Screenshot of trailsforthepeople-styles github readme.md

 

— Addendum —

Truth in advertising– the contours data were not included in the repository as they are 1.3 GB all by themselves

— End Addendum —

 

Posted in Cartography, Recreation, TileStache, Trail Curation, Trails | Tagged: , | Leave a Comment »

Parks Data Cake

Posted by smathermather on March 24, 2014

Stamen has a great blog entry on mapping for parks on their blog. It’s a teaser for a deeper dive in mapping parks, and I’m staying tuned, as their write-ups tend to be detailed, thoughtful, and complete.

I thought I’d offer my own teaser– a bit of work done collaboratively with GreenInfo Network.  It started with their basemap– something they spent a few person-years refining from OSM data.

Example of GIN basemap

Example of GIN custom basemap

Custom basemap

We took it a little further by creating custom placed labels where the labels gave us some extra bang for the buck.

Custom text placement example

Custom text placement

And then we started to really play with the zoomed in versions, using nicely detailed streams, contours, and canopy boundaries (derived from LiDAR) to enrich the map, labeling natural features and major facilities with custom labels as well:

Image showing labeling, contours, vegetation, etc.

Detailed view of map

A couple of tricks we used here– first, the custom labels have shadows (convex hull) masks which match the background, ala this write up:

Example of masked text

Also, compositing is used heavily– the hillshade background is flattened significantly, and then overlayed with multiplied composites of park green, canopy green on top, contours, with all other layers being simple overlays. This results in a brightness and clarity to the information that allows for complexity without either muddling the map, or overwhelming the map reader.

Finally, since many of the parks are urban, major buildings are highlighted for context. In future revisions, we’ll add more (but still subdued) detail to the areas outside the parks, but this was a good start:

Example of major buildings showing on map near a park

In case you are wondering about technology, this is all done in TileStache (edit: and GeoServer).  In the future, this may well be replicated in Tilemill. More to come… .

Posted in Cartography, Recreation, TileStache, Trail Curation, Trails | Tagged: , | 4 Comments »

Compositing in TileStache

Posted by smathermather on November 9, 2012

TileStache (and some adaptation of great pre-built cartography from others more artful than I) results in this:

20121109-233511.jpg

Posted in Cartography, Mobile, Trails | Tagged: | Leave a Comment »

Going deeper into web cartography: future=past? (and Swiss cartographic genius)

Posted by smathermather on May 12, 2012

My favorite cartography book is Eduard Imhof’s Cartographic Relief Presentation.  A few years back I picked this book up (translated to English) from ESRI press for $75 if memory serves me.  Now it can be gotten for much cheaper.

Not created in GeoServer (nor TileMill– from Imhof’s book)

Imhof spends a lot of time on feature simplification and separation, a problem which keeps me up at night.  For example, if you have a lot of overlapping and/or touching contours, what are the local distortions and simplifications that can be done to enhance map readability?

Cartographic representation choices with overlapping and tightly packed contours, also from Cartographic Relief Presentation

This problem applies to other overlapping features, such as a road following a river, where at a given scale one might not distinguish between the two, so we exaggerate their differences, in this case, while maintaining their basic topology.

I’ve played with this problem before, but now I’m considering a new approach.  I pinged Martin Davis of JTS fame for some advice.  He suggested using force directed layout to solve this problem, much as he does in JTS Test Builder for magnifying/investigating topology.

I don’t know yet where or how (if) I’ll implement this, but it’s an interesting an potentially solvable problem.  Now where to work on this in the stack– in PostGIS, because I know it best, in the renderer (GeoServer) where I’ll be swimming in Java of a level I don’t have a chance, or in the browser/client, where there are already some examples written in Javascript… .

Posted in Analysis, Cartography, Database, PostGIS, PostgreSQL, SQL, Trail Curation, Trails | Tagged: , , , , , , , | Leave a Comment »

Nice post on installing pgRouting on Ubuntu

Posted by smathermather on March 9, 2012

Just a quick link to a post I found on installing pgRouting on Ubuntu:

http://obsessivecoder.com/2010/02/01/installing-postgresql-8-4-postgis-1-4-1-and-pgrouting-1-0-3-on-ubuntu-9-10-karmic-koala/

Posted in Database, pgRouting, PostGIS, PostgreSQL, Recreation, SQL, Trails | Tagged: , , , , , | Leave a Comment »

Mapfish Play cont. Musings on PostGIS driven Mapfish requests– Code only

Posted by smathermather on March 8, 2012

Code only post-- which only means it's been out here not-quite ready to post for a month.  Now I post out of shear annoyance with myself... .
SELECT '{"units":"ft","srs":"EPSG:3734","layout":"1) LETTER 8.5x11 Portrait","dpi":300,"serviceParams":{"locale":"en_US"},"resourcesUrl":"http://maps/geoserver/www/printing","layersMerging":true,"preferredIntervalFractions":[0.1,0.2,0.4],"metaTitle":"GIS Print","metaAuthor":"","metaSubject":"GIS Print","metaKeywords":"","outputFilename":"cm_gis","legends":[{"name":"","classes":[{"name":"Reservation","icons":["http://maps/geoserver/wms?&VERSION=1.1.0&REQUEST=GetLegendGraphic&LAYER=reservation_bounds&HEIGHT=10&WIDTH=10&FORMAT=image%2Fpng&TRANSPARENT=true&LEGEND_OPTIONS=forceLabels%3Afalse&EXCEPTIONS=application%2Fvnd.ogc.se_xml&RULE=unrestricted"]},{"name":"Restricted","icons":["http://maps/geoserver/wms?&VERSION=1.1.0&REQUEST=GetLegendGraphic&LAYER=reservation_bounds&HEIGHT=10&WIDTH=10&FORMAT=image%2Fpng&TRANSPARENT=true&LEGEND_OPTIONS=forceLabels%3Afalse&EXCEPTIONS=application%2Fvnd.ogc.se_xml&RULE=restricted"]}]},{"name":"Detailed Hydro","classes":[{"name":"Ditch","icons":["http://maps/geoserver/wms?&VERSION=1.1.0&REQUEST=GetLegendGraphic&LAYER=detailed_hydro_view&HEIGHT=10&WIDTH=10&FORMAT=image%2Fpng&TRANSPARENT=true&LEGEND_OPTIONS=forceLabels%3Afalse&EXCEPTIONS=application%2Fvnd.ogc.se_xml&RULE=cm_streams_ditch"]},{"name":"Non-Stream Waterway","icons":["http://maps/geoserver/wms?&VERSION=1.1.0&REQUEST=GetLegendGraphic&LAYER=detailed_hydro_view&HEIGHT=10&WIDTH=10&FORMAT=image%2Fpng&TRANSPARENT=true&LEGEND_OPTIONS=forceLabels%3Afalse&EXCEPTIONS=application%2Fvnd.ogc.se_xml&RULE=cm_streams_ditch"]},{"name":"Stream","icons":["http://maps/geoserver/wms?&VERSION=1.1.0&REQUEST=GetLegendGraphic&LAYER=detailed_hydro_view&HEIGHT=10&WIDTH=10&FORMAT=image%2Fpng&TRANSPARENT=true&LEGEND_OPTIONS=forceLabels%3Afalse&EXCEPTIONS=application%2Fvnd.ogc.se_xml&RULE=cm_streams_stream"]},{"name":"Stream or River","icons":["http://maps/geoserver/wms?&VERSION=1.1.0&REQUEST=GetLegendGraphic&LAYER=detailed_hydro_view&HEIGHT=10&WIDTH=10&FORMAT=image%2Fpng&TRANSPARENT=true&LEGEND_OPTIONS=forceLabels%3Afalse&EXCEPTIONS=application%2Fvnd.ogc.se_xml&RULE=Stream%20or%20River"]},{"name":"Pond","icons":["http://maps/geoserver/wms?&VERSION=1.1.0&REQUEST=GetLegendGraphic&LAYER=detailed_hydro_view&HEIGHT=10&WIDTH=10&FORMAT=image%2Fpng&TRANSPARENT=true&LEGEND_OPTIONS=forceLabels%3Afalse&EXCEPTIONS=application%2Fvnd.ogc.se_xml&RULE=Pond"]},{"name":"Lake Erie","icons":["http://maps/geoserver/wms?&VERSION=1.1.0&REQUEST=GetLegendGraphic&LAYER=detailed_hydro_view&HEIGHT=10&WIDTH=10&FORMAT=image%2Fpng&TRANSPARENT=true&LEGEND_OPTIONS=forceLabels%3Afalse&EXCEPTIONS=application%2Fvnd.ogc.se_xml&RULE=Lake"]},{"name":"Other Wet Areas","icons":["http://maps/geoserver/wms?&VERSION=1.1.0&REQUEST=GetLegendGraphic&LAYER=detailed_hydro_view&HEIGHT=10&WIDTH=10&FORMAT=image%2Fpng&TRANSPARENT=true&LEGEND_OPTIONS=forceLabels%3Afalse&EXCEPTIONS=application%2Fvnd.ogc.se_xml&RULE=Other%20Wet%20Areas"]}]},{"name":"Trails","classes":[{"name":"ADA, APT","icons":["http://maps/geoserver/wms?&VERSION=1.1.0&REQUEST=GetLegendGraphic&LAYER=cm_trails&HEIGHT=10&WIDTH=10&FORMAT=image%2Fpng&TRANSPARENT=true&LEGEND_OPTIONS=forceLabels%3Afalse&EXCEPTIONS=application%2Fvnd.ogc.se_xml&RULE=rule01"]},{"name":"Bridle","icons":["http://maps/geoserver/wms?&VERSION=1.1.0&REQUEST=GetLegendGraphic&LAYER=cm_trails&HEIGHT=10&WIDTH=10&FORMAT=image%2Fpng&TRANSPARENT=true&LEGEND_OPTIONS=forceLabels%3Afalse&EXCEPTIONS=application%2Fvnd.ogc.se_xml&RULE=rule02"]},{"name":"Hiking","icons":["http://maps/geoserver/wms?&VERSION=1.1.0&REQUEST=GetLegendGraphic&LAYER=cm_trails&HEIGHT=10&WIDTH=10&FORMAT=image%2Fpng&TRANSPARENT=true&LEGEND_OPTIONS=forceLabels%3Afalse&EXCEPTIONS=application%2Fvnd.ogc.se_xml&RULE=rule03"]},{"name":"Mountain Bike Trails","icons":["http://maps/geoserver/wms?&VERSION=1.1.0&REQUEST=GetLegendGraphic&LAYER=cm_trails&HEIGHT=10&WIDTH=10&FORMAT=image%2Fpng&TRANSPARENT=true&LEGEND_OPTIONS=forceLabels%3Afalse&EXCEPTIONS=application%2Fvnd.ogc.se_xml&RULE=rule04"]},{"name":"Connector Trail","icons":["http://maps/geoserver/wms?&VERSION=1.1.0&REQUEST=GetLegendGraphic&LAYER=cm_trails&HEIGHT=10&WIDTH=10&FORMAT=image%2Fpng&TRANSPARENT=true&LEGEND_OPTIONS=forceLabels%3Afalse&EXCEPTIONS=application%2Fvnd.ogc.se_xml&RULE=rule05"]}]}],"layers":[{"baseURL":"http://maps/geoserver/wms?","opacity":1,"singleTile":false,"type":"WMS","layers":["cuy_bridge_decks","planet_osm_line_outside_cuy_map","cuy_roads_poly","cuyahoga_street_centerlines","reservation_bounds_solid"],"format":"image/png","styles":[""],"customParams":{"TILED":"false","TRANSPARENT":true}},{"baseURL":"http://maps/geoserver/wms?","opacity":1,"singleTile":false,"type":"WMS","layers":["reservation_bounds"],"format":"image/png","styles":[""],"customParams":{"TRANSPARENT":true,"TILED":false}},{"baseURL":"http://maps/geoserver/wms?","opacity":1,"singleTile":false,"type":"WMS","layers":["detailed_hydro_view"],"format":"image/png","styles":[""],"customParams":{"TRANSPARENT":true,"TILED":false}},{"baseURL":"http://maps/geoserver/wms?","opacity":1,"singleTile":false,"type":"WMS","layers":["cm_bridge_view"],"format":"image/png","styles":[""],"customParams":{"TRANSPARENT":true,"TILED":false}},{"baseURL":"http://maps/geoserver/wms?","opacity":1,"singleTile":false,"type":"WMS","layers":["cm_trails"],"format":"image/png","styles":[""],"customParams":{"TRANSPARENT":true,"TILED":false}},{"baseURL":"http://maps/geoserver/wms?","opacity":1,"singleTile":false,"type":"WMS","layers":["impervious_update","cm_buildings","cm_buildings_outline"],"format":"image/png","styles":[""],"customParams":{"TILED":"false","TRANSPARENT":true}},{"baseURL":"http://maps/geoserver/wms?","opacity":1,"singleTile":false,"type":"WMS","layers":["golf_view"],"format":"image/png","styles":[""],"customParams":{"TILED":"false","TRANSPARENT":true}},{"baseURL":"http://maps/geoserver/wms?","opacity":1,"singleTile":false,"type":"WMS","layers":["nhd_lake_erie"],"format":"image/png","styles":[""],"customParams":{"TILED":"false","TRANSPARENT":true}},{"baseURL":"http://maps/geoserver/wms?","opacity":1,"singleTile":false,"type":"WMS","layers":["reservation_boundaries_public_private_cm_dissolved_mask_gradien"],"format":"image/png","styles":[""],"customParams":{"TILED":"false","TRANSPARENT":true}},{"baseURL":"http://maps/geoserver/wms?","opacity":1,"singleTile":true,"type":"WMS","layers":["supplementary_shields","odot_interstate","odot_us_routes","odot_state_routes","planet_osm_line","cuyahoga_street_centerlines_labels","planet_osm_line_outside_cuy","detailed_hydro_labels","facilities_cm","facility_areas_cm"],"format":"image/png","styles":[""],"customParams":{"TILED":"false","TRANSPARENT":true}}],"pages":[{"center":[' || ST_X(ST_Centroid(the_geom)) || ',' || ST_Y(ST_Centroid(the_geom)) || '],"scale":2400,"rotation":0,"mapTitle":""}]}'::text
FROM loops
WHERE (ST_XMax(ST_Envelope(the_geom)) - ST_XMin(ST_Envelope(the_geom))) < (ST_YMax(ST_Envelope(the_geom)) - ST_YMin(ST_Envelope(the_geom)))
AND
(ST_YMax(ST_Envelope(the_geom)) - ST_YMin(ST_Envelope(the_geom))) < 7900
SELECT 'landscape, linear follow'::text, ST_Centroid(the_geom)
	FROM loops
		WHERE (ST_XMax(ST_Envelope(the_geom)) - ST_XMin(ST_Envelope(the_geom))) > (ST_YMax(ST_Envelope(the_geom)) - ST_YMin(ST_Envelope(the_geom)))
			AND
		(ST_XMax(ST_Envelope(the_geom)) - ST_XMin(ST_Envelope(the_geom))) > 15800

UNION ALL

SELECT 'landscape, quad page'::text, ST_Centroid(the_geom)
	FROM loops
		WHERE (ST_XMax(ST_Envelope(the_geom)) - ST_XMin(ST_Envelope(the_geom))) > (ST_YMax(ST_Envelope(the_geom)) - ST_YMin(ST_Envelope(the_geom)))
			AND
		(ST_XMax(ST_Envelope(the_geom)) - ST_XMin(ST_Envelope(the_geom))) > 7900
			AND
		(ST_XMax(ST_Envelope(the_geom)) - ST_XMin(ST_Envelope(the_geom))) <= 15800

UNION ALL

SELECT 'landscape, single page'::text, ST_Centroid(the_geom)
	FROM loops
		WHERE (ST_XMax(ST_Envelope(the_geom)) - ST_XMin(ST_Envelope(the_geom))) > (ST_YMax(ST_Envelope(the_geom)) - ST_YMin(ST_Envelope(the_geom)))
			AND
		(ST_XMax(ST_Envelope(the_geom)) - ST_XMin(ST_Envelope(the_geom))) < 7900

UNION ALL

SELECT 'portrait, quad page'::text, ST_Centroid(the_geom)
	FROM loops
		WHERE (ST_XMax(ST_Envelope(the_geom)) - ST_XMin(ST_Envelope(the_geom))) < (ST_YMax(ST_Envelope(the_geom)) - ST_YMin(ST_Envelope(the_geom)))
			AND
		(ST_YMax(ST_Envelope(the_geom)) - ST_YMin(ST_Envelope(the_geom))) > 7900
			AND
		(ST_YMax(ST_Envelope(the_geom)) - ST_YMin(ST_Envelope(the_geom))) <= 15800

UNION ALL

SELECT 'portrait, single page'::text, ST_Centroid(the_geom)
	FROM loops
		WHERE (ST_XMax(ST_Envelope(the_geom)) - ST_XMin(ST_Envelope(the_geom))) < (ST_YMax(ST_Envelope(the_geom)) - ST_YMin(ST_Envelope(the_geom)))
			AND
		(ST_YMax(ST_Envelope(the_geom)) - ST_YMin(ST_Envelope(the_geom))) < 7900

UNION ALL

SELECT 'portrait, linear follow'::text, ST_Centroid(the_geom)
	FROM loops
		WHERE (ST_XMax(ST_Envelope(the_geom)) - ST_XMin(ST_Envelope(the_geom))) < (ST_YMax(ST_Envelope(the_geom)) - ST_YMin(ST_Envelope(the_geom)))
			AND
		(ST_YMax(ST_Envelope(the_geom)) - ST_YMin(ST_Envelope(the_geom))) > 15800
;

Posted in Database, GeoServer, MapFish, Other, PostGIS, PostgreSQL, Recreation, SQL, Trail Curation, Trails | Tagged: , , , , , , | 1 Comment »

Playing with new tools and old standards: GeoJSON, Leaflet, CartoDB across platforms

Posted by smathermather on February 16, 2012

Leaflet, CartoDB, GeoJSON, and cross platform web map deployment. First some introductions:

Leaflet is a modern, lightweight open-source JavaScript library for interactive maps for desktop and mobile web browsers, developed by CloudMade to form the core of its next generation JavaScript API. Weighting just about 21kb of gzipped JS code, it still has all the features you will ever need for you web mapping needs while providing a fast, smooth, pleasant user experience.”

Not a bad description. There’s more maturity and flexibility to OpenLayers, but still much fun to be had with Leaflet. And as both are Open Source and competing in similar markets, they inform each other, and it’s fun to watch the friendly rivalry between the communities developing the libraries.

In an earlier post, I demonstrated the use of Leaflet in conjunction with CartoDB. What’s cartodb? From their web site:

“CartoDB allows you to map data & develop location aware applications quickly and easily. With plans starting from free, take CartoDB for a test drive today!”

CartoDB is a really slick hosted service (cloud) implementation of PostGIS, similar in some respects to fusion tables functionality. Don’t like the cloud it’s hosted on, or the associated prices? Well, it’s open source, so you can put it on your private cloud too, or set up physical linux machine to host it.

Mix these two things with 3 hours of insomnia the other night, and you get this:

Nice cross platform implementation!  These are pure vector deployed maps– served through Leaflet from CartoDB as GeoJSON.  Here shown deployed to a Blackberry, iPhone, Android, and Kindle Fire (also Android), which a buddy and I pulled up as we sat in a bar tonight.  We also took a picture of this with a windows laptop also running the page for demonstrable non-webkit deployment, but this was the better (albeit fuzzy) picture.  Ah, the wonders of Open Standards.  Next time, we’ll see if we can get a Windows Mobile phone in there, along with Mac, Windows, and Linux desktops.

Still, for now GeoServer and OpenLayers are my babies for deploying GeoSpatial data, but there are a few different ways to deploy through Open Standards using Open Source software, and it’s fun to draw a little from the other geospatial tribes.

Posted in CartoDB, Database, Leaflet, PostGIS, PostgreSQL, Recreation, SQL, Trail Curation, Trails | Tagged: , , , , , , | 2 Comments »

PostGIS Cartographic Effects– Cartoonify Nearly Coincident Lines

Posted by smathermather on January 10, 2012

In my previous post, a long 24-hours ago, I proposed some automatic modification of line for cartographic reasons. I had some flaws in my code. The points were over-rotated by 45 degrees. Can you spot why? Tip: it’s a basic trigonometric mistake. Here’s the corrected code (though there may be a better way):

CREATE TABLE movedRoad AS
SELECT
    ST_Translate(b.the_geom,
        0.5 * ST_Distance(b.the_geom, ST_Union(a.the_geom)) * (cos(ST_Azimuth(b.the_geom, ST_ClosestPoint(ST_Union(a.the_geom), b.the_geom))) - 3.14159/4),
        0.5 * ST_Distance(b.the_geom, ST_Union(a.the_geom)) * (sin(ST_Azimuth(b.the_geom, ST_ClosestPoint(ST_Union(a.the_geom), b.the_geom))) - 3.14159/4) )
            AS the_geom
    FROM road2_points b, road1 a
        GROUP BY a.id, b.the_geom
    ;

An alternate approach is to only move those points that are too close, ala:

CREATE TABLE movedRoadAgain AS
	SELECT
        ST_Translate(b.the_geom,
	        (100 - ST_Distance(b.the_geom, ST_Union(a.the_geom))) * (cos(ST_Azimuth(b.the_geom, ST_ClosestPoint(ST_Union(a.the_geom), b.the_geom))) - 3.14159/4) ,
	        (100 - ST_Distance(b.the_geom, ST_Union(a.the_geom)))* (sin(ST_Azimuth(b.the_geom, ST_ClosestPoint(ST_Union(a.the_geom), b.the_geom))) - 3.14159/4) ) AS the_geom
	    FROM road2_points b, road1 a
	        GROUP BY a.id, b.the_geom
		HAVING ST_Distance(b.the_geom, ST_Union(a.the_geom)) < 100

	UNION ALL

	SELECT
		b.the_geom AS the_geom
	    FROM bridle_points b, apt a
	        GROUP BY a.id, b.the_geom
		HAVING ST_Distance(b.the_geom, ST_Union(a.the_geom)) >= 100
	    ;

Posted in Analysis, Cartography, Database, PostGIS, PostgreSQL, SQL, Trail Curation, Trails | Tagged: , , , , , , | Leave a Comment »

PostGIS Cartographic Effects– Cartoonify Nearly Coincident Lines

Posted by smathermather on January 9, 2012

I’m still working on this query, but I thought I’d post what I’ve done so far. My intent is to produce scale-dependent exaggeration of the distances between quasi-parallel lines. The reason for this is so that lines such as street lines which are nearly coincident at a particular viewing scale can be spread from each other, much in the same way great cartography lies a little to display a relatively correct map. The nice thing about digital cartography is that as we zoom in, we can make the lie a smaller and smaller, until it is just barely a fib :).

My thought was to start with the simplest case– move one line away from another (stationary) line. First step is to find the closest point on the stationary line, determine the distance and angle to that point, and then translate the point outward by some scale factor along that angle. Don’t take my code as gospel, though, I think there’s some deeper logical error, but see what you think:

ST_ShortestLine gives us the shortest line from a given point to the road from which we are trying to offset. This is just to help visualize the problem:

CREATE TABLE shortLine AS

SELECT
	ST_ShortestLine(b.the_geom, ST_Union(a.the_geom)) AS the_geom
    FROM road2_points b, road1 a
    GROUP BY a.id, b.the_geom
    ;

Now, we try to translate the point outward at the same angle, by half again the distance to the closest point:

CREATE TABLE movedRoad AS
SELECT
	ST_Translate(b.the_geom,
		0.5 * ST_Distance(b.the_geom, ST_Union(a.the_geom)) * cos(ST_Azimuth(b.the_geom, ST_ClosestPoint(ST_Union(a.the_geom), b.the_geom))),
		0.5 * ST_Distance(b.the_geom, ST_Union(a.the_geom)) * sin(ST_Azimuth(b.the_geom, ST_ClosestPoint(ST_Union(a.the_geom), b.the_geom))) )
			AS the_geom
    FROM road2_points b, road1 a
        GROUP BY a.id, b.the_geom
    ;

Resulting in something looking like this (red dashed line is the road we are moving away from, the dotted blue are the points making up the line we are moving, and the dotted brown are the newly transformed points, with ST_ShortestLine also visualized in thin brown):

/>

Posted in Analysis, Cartography, Database, PostGIS, PostgreSQL, SQL, Trail Curation, Trails | Tagged: , , , , , , | 1 Comment »

Looping Trails– alternate routing for recreational use (cont.)

Posted by smathermather on September 29, 2011

I’ve been thinking more about the recreational looping problem, given a start location and a given range of travel (e.g. 0-3 miles). A way to approach this is to build up loops from a set of nodes to which has been applied a traveling salesman algorithm. To test this on a piece of paper, before digging under the hood of e.g. pgRouting or OpenTripPlanner, I used a pretend park with trails (in green), with a pretend gridded neighborhood (in pale purple) to select 3 random nodes (numbers) to route through from a given starting point just to see what the outcomes of such an approach might look like.

If you notice, I got confused with the red route in that should have passed through point 9. Here’s my random numbers, 3 random numbers for each route, for a total of 4 required nodes:

  • 11,4,6 — Solid Black Line
  • 7,5,22 — Dashed Black Line
  • 9,2,19 — Solid Red Line

Conceptually, if implemented on the client side this could build loops on any API capable of traveling salesman, given an API wrapper around pgRouting or the addition of traveling salesman routing in OpenTripPlanner, or even using routing from the Google Maps API. Alternatively, the loops could be pre-built on the database side (not randomly, but completely), and then manual or automatic loop prioritization could be implemented to give the series of “best” choices, whether best is determined by miles, terrain, loopyness, trail surface type, mode of travel, etc..

Posted in Recreation, Trail Curation, Trails | Leave a Comment »