Saturday, January 17, 2015
The trip started well, with me walking in with "Just Bill", an 'imaginary friend' that I was meeting for the first time in person. Fellow hikers "1azarus" and "Malto" hiked in to meet us. Malto slept in the shelter with me, while Just Bill and 1azarus hammocked nearby. We settled in companionably, spinning tales by the fire as hikers do.
This morning, I bailed out on the rest of the trip.
It wasn't because of the cold, although up on the ridge it fell to near 0°F (-18°C) if not below. I was bundled up, warm and dry. I was close to the limit of what my gear can handle comfortably, but still within it.
It wasn't the fact that I awoke when Malto decamped in the night. I got right back to sleep after he left. He was back in the morning.
It wasn't the raccoon dragging my (empty) pack out of the shelter, although it's a bit starting to wake up in the wee hours to find a raccoon six inches from your nose dragging your pack away. He also got quite a head start on me, since I needed to work my way sleepily out of a completely-battened-down sleeping bag (How do these draft collar, hood, and zipper things work, anyway?) don spectacles and headlamp, and put on frozen boots before I could give chase. I found that he'd also stolen Just Bill's pot. In which he never does anything but boil water. Well, they do like shiny things.
It wasn't the subsequent awakening by some other campers coming by the shelter wondering what the hollering was about.
It wasn't the subsequent return of the raccoon looking for something else to steal. Everything was out of his reach.
It wasn't the commotion of the other campers as the 'coon decided he'd find easier pickings in their tent.
It wasn't even that all of these things combined to a night of much-interrupted sleep.
No, it was the early rumblings of a stomach bug that finally convinced me to call it quits. Combined with the fact that the little marauder had stolen my toilet paper. And the fact that my various medicines, along with a few other very important small items (spork!) had not made it into my pack.
I'm glad I came home to rest. Today would have been uncomfortable in the woods.
If by some chance I'm feeling much improved in the morning, I'll try to meet the remaining stalwarts in Doodletown.
Just Bill proposed a new trail name for me: Sleeps with Raccoons.
Every trip an adventure...
Sunday, January 11, 2015
Nice labeling of administrative boundaries appears to have been a challenge for Mapnik users, and I've certainly not seen a good summary on the Web of how to render administrative boundaries attractively and legibly. In some recent experiments, I found what appears to be a scheme that others can leverage. Read on for the details.
I recently did an update to my work-in-progress of a hikers' map of the US Northeast, and decided to revisit how I handled the shading of the map. Another mapper had shown me a project of his, where the background of the map was rendered according to the National Land Cover Database - and it clearly provided useful information for a hiker, particularly those of us who occasionally venture off the marked trails.
Using landcover (overlaid with hill shading) as the base shading of the map left me with a problem: my previous map had used fill colours as a way to distinguish land ownership and regulatory status. In addition to answering the question of, "will hiking up this ridge have me pushing through the spruce?" I wanted to answer questions like, "is this area designated as Wilderness?" (Different camping regulations.) "Do I need a New York City Watershed permit to hike here?" and so on.
One way that I've seen printed maps handle the desire to overlay multiple types of area features is for them to outline an area and then use some special treatment (hachure, stipple, shading) along the inner side of the outline to indicate the information. Trying to use this sort of treatment with Mapnik raises the question: which side is the inner side? That's where I got to the last time that I thought about using this sort of treatment, and got no satisfactory answer. OSM's polygons do not appear to be wound in a consistent direction.
But this time, I stumbled upon a PostGIS function that I'd previously missed: ST_ForceRHR. This is a call that accepts a geometry (polygon or multipolygon), and imposes on it the Right Hand Rule. It returns the same geometry, with the borders listed so that along the direction of a line, the interior of the area is always on the right-hand side. (That is, it walks around polygons in a clockwise direction.)
The right-hand rule was exactly the missing piece that I needed. All that I needed for my wilderness areas, state parks, protected watersheds, and what not was to make a little semitransparent PNG with shading on one side, like this one.
We make a style that uses a LinePatternSymbolizer to render the line that's shaded on one side:
<!--Miscellaneous area features from OSM --> <Style name="osm-misc-area"> <Rule> <MaxScaleDenominator>750000</MaxScaleDenominator> <Filter> [leisure] = 'playground' or [leisure] = 'golf_course' or [landuse] = 'recreation_ground' or [leisure] = 'recreation_ground' or [landuse] = 'village_green' </Filter> <LinePatternSymbolizer file="graphics/7e5-border.png"/> </Rule> <!-- many more rules for other types of landuse --> </Style>
And we feed it with an area query that uses ST_ForceRHR. As with most queries with subqueries, we need to use ST_Intersects to make sure that the geometry index gets used.
<Layer name="recreation-lands-osm" srs="+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +units=m +k=1.0 +no_defs"> <StyleName>recreation-land-osm</StyleName> <Datasource> <Parameter name="type">postgis</Parameter> <Parameter name="dbname">gis</Parameter> <Parameter name="estimate_extent"> false </Parameter> <Parameter name="extent"> -8905831.039562456, 4865981.220634319, -7458419.471954359, 6274868.52598669 </Parameter> <Parameter name="geometry_field">rhr</Parameter> <Parameter name="table"> (SELECT ST_ForceRHR(way) AS rhr, name, way_area as shape_area FROM planet_osm_polygon WHERE ST_Intersects(ST_SetSRID(!bbox!, 3857), way) AND (leisure IN ('park', 'nature_reserve', 'common', 'playground', 'garden', 'golf_course', 'recreation_ground') OR landuse IN ('forest', 'vineyard', 'conservation', 'recreation_ground', 'village_green', 'allotments') OR "natural" IN ('wood') -- many more types of areas ) ) AS areas </Parameter> </Datasource> </Layer>
And the resulting rendering is just as I hoped: a thin dashed line with a green inner highlight on the natural areas.
Then it occurred to me: If we combine the right-hand rule with the list placement type on a TextSymbolizer, we can finally do proper labeling of administrative boundaries. Given the right-hand rule, we know that if a line is going left-to-right, the interior is below the line, and conversely, if it is going right-to-left, the interior is above the line. We can adjust dy accordingly to place a label on the correct side of the line.
<!-- Attempt at edge labels on admin boundaries --> <Style name="admin-edge-label"> <Rule> &minz8; <TextSymbolizer avoid-edges="true" clip="false" face-name="MartinGotURWTMed Italic" size="12" halo-radius="2" fill="black" halo-fill="transparent" dy="-8" placement-type="list" placement="line" spacing="500" max-char-angle-delta="30" upright="right_only"> [name] <Placement upright="left_only" dy = "9"> [name] </Placement> </TextSymbolizer> </Rule> </Style>
The layer specification is similar to the one for land use. The SQL query looks like:
(SELECT ST_ForceRHR(way) AS rhr, name FROM &db_osm_polygon_table; WHERE ST_Intersects(ST_SetSRID(!bbox!, 3857), way) AND "boundary"='administrative' AND admin_level IN ('2', '4', '6')) AS outlines
And again, it performs perfectly. Country, state and county names come out facing each other across the boundary lines.
(I am oversimplifying here, but only slightly. I'm actually rendering these labels twice, according to the recommendations at http://mapnik.org/news/2012/04/20/smart-halos/. Rather than using the dst-over compositing operator, however, I'm rendering the image with fill color and the image with line art separately, and compositing them in Python.