Showing posts with label openstreetmap. Show all posts
Showing posts with label openstreetmap. Show all posts

Tuesday, November 17, 2020

2020-11-16 Shield tables bug found, at last

I finally found the source of the duplicated data in the OSM shield tables. It turns out that since the 'shieldway' table is a per-way table, it's cleaned out only if a way is deleted or replaced, and not if the way is just updated.

I wrote it up in detail in an issue at the osm2pgsql project. so I'm not going to repeat a lot of text here. With various work-arounds, I'm to the point where I can handle:

  • inserts, updates, and deletes of ways
  • inserts and updates of route relations

Deleting route relations causes a mess. Everything is deleted cleanly by referential integrity constraints ON DELETE CASCADE, but then the Lua script goes and reinserts the relation memberships back into the shieldway table, violating its foreign key constraint since the relation is no longer there.

I can't see a workaround for that bit, so I opened the issue above as a cry for help.

Fortunately, it's vanishingly rare to delete a route relation, so I think I can run for a while this way, and move on to trying for minutely updates. I think the next step is just that - work out how to switch osmosis to pulling from the main OSM database and trigger it minutely. I see there's a Wiki page on the subject, and I have the polygon files for the extracts I'm working with. How hard can it be? (Yes, I know, that phrase is right up there with “hold my beer,” but I'm a crazy programmer.

Next up after that, I think, will be to see if I can switch my personal map server over to auto-generated and cached tiles. That'll probably involve retooling the server to run off Apache or Nginx rather than althttpd, which fills me with dread, because I love the “zero administration” aspects of the latter - Richard Hipp is a master of the “It Just Works” school of software design. But I really want to see if going to tiles generated on the fly would allow it to scale to a whole continent, rather than about a fourth of the Lower 48 of the US.


Read more...

Monday, November 16, 2020

2020-11-15 Trying to update shield tables

Now that I have managed to speed up the part of updating the shield tables so that it could possibly run once a minute, I'm trying to run tests first with daily updates from geofabrik.de. I'm not having all that much success yet.

The update is running without complaint, but I'm finding that if a route relation is modified in the update, I get two or three copies of the member ways in the database. That surely won't work!

It's not obvious to me what's going on. There are no errors from the Lua script that's loading the database, It's just generating redundant entries. The initial load doesn't do that, and it's the same code.

I've also removed the primary key from the 'shieldways' table for now, because with that left in place, the duplicate rows did indeed cause a crash.

Gotta look into this more tomorrow.


Read more...

2020-11-15 Delhi trails mapped

With uncharacteristic promptness, I was able to edit the GPS tracks that Cathy logged on her smartwatch yesterday and get the Delaware Academy trails into OpenStreetMap.

I was only moderately surprised that nobody had mapped them previously. I was able to see some of the trails, which mostly follow long-abandoned carriage roads, on aerial images, and traced them where I could. Otherwise, I copied-n-pasted segments of GPS tracks.

The results don't look too different from the official map, so I'm fairly satisfied.


Read more...

Thursday, November 12, 2020

2020-11-11 Fixing the rivers

[OOPS - I made a typo in the date when I first posted this!]

I noticed that in my topo map, I was losing rivers. For instance, the entire east branch of the Neversink had disappeared, although several of the minor tributaries were still shown. I blew a lot of today tracking down the issue.

It turned out to be that I'd downloaded updates to NHD when I was rebuilding the database after upgrading to PostgreSQL release 12. It appears that USGS changed the file layout so that the NHDWaterbody and NHDFlowline tables now are in multiple shapefiles, all of which have to be loaded to have the full data set.

Layout of a ZIP file from NHD
Layout of a zip file from NHD

Since the first shapefile still has the same name that the old one did, the program that does the loading had no trouble, but loaded only a third of the data for New York (and even less for some other states!), so I never got an indication when loading the files that there was any sort of trouble.

Downloading and importing all the ZIPs another time took most of the day. I could have worked on other things, but the database was pretty unusable while it was thrashing over the rivers and streams.

Anyway, now it's fixed.

Before (White's Run is missing):
Map of Womelsdorf, WV, missing White's Run
After (White's Run is back):
Map of Womelsdorf, WV, with White's Run restored


Read more...

2020-11-09 Lua module for highway shields committed

Today, I tidied up, tested and committed a Lua module for processing highway shields in osm2pgsql.

Sample highway shield

I've tried to keep the design as non-intrusive as possible to people's existing workflow, assuming they already use the flex backend. There are just four changes needed to the Lua style file.

  • In initialization, add the lines:
          local shieldtables = require("shieldtables")
          local shieldt = shieldtables.new(prefix)
          
    where prefix is the prefix (e.g., planet_osm) being used in the database for the OSM tables.
  • In the osm2pgsql.process_way function, add the line:
          shieldt:process_way(object);
          
  • In the osm2pgsql.process_relation function, add the line:
          shieldt:process_way(object);
          
  • Create a osm2pgsql.select_relation_members function if you don't already have one. There's a call:
          shieldt:select_relation_members(object)
          
    with the same API that returns the necessary selection. For all the style sheets that I was using, I was able just to use:
          function osm2pgsql.select_relation_members(object)
    	     return shieldt:select_relation_members(object)
          end
          
  • And that's pretty much it, for how to use it. The database tables prefix_shieldroute and prefix_shieldway are managed entirely by those procedures.

    I still need to test to make sure that incremental update works with the new setup. If it does, that's half the job of making minutely updates work, I think.


    Read more...

    Monday, November 9, 2020

    2020-11-07 First whack at a stylesheet for osm2pgsql

    I managed to do enough Lua coding today to make my first attempt at a stylesheet for the flex backend of osm2pgsql to create the tables that the renderer for pictorial highway shields in OpenStreetMap will need.

    The code is still pretty nasty - I'll clean it up on Monday - but it's producing the tables. I tried it on an export for Connecticut (rather than attempting the whole map), and it's turning out tiles that look just as they did before.

    A little piece of the Connecticut map

    True to form, I found a bug in osm2pgsql while I was working on this. Nothing I can't work around. (2020-11-09: The maintainers are amazing. It's fixed already.)
    Read more...

    2020-11-06 Learning Lua

    I spent pretty much all of my hacking time today munching on Lua documentation and trying out random snippets of code. (Once you know a couple of dozen programming languages, you typically learn a new one by taking a few hours to read the reference documentation and try a few short programs, and you're good to go.)

    This is mostly so that I'll know enough Lua to write a library for extending the style sheets of osm2pgsql to create the tables that the current incarnation of OSM shield rendering uses.


    Read more...

    Friday, November 6, 2020

    2020-11-05 Highway shield graphics - minor updates

    Not a lot of progress today. I sketched some more ideas for using the flex back end of osm2pgsql to process concurrent routs in my notebook, and I puttered about verifying that the picayune changes that I made to the shield graphics worked.

    The main change there is that the highway shields are back in New Brunswick, Prince Edward Island, Saskatchewan and Alberta. (The Canadian mappers had made wholesale changes to the network tag to unify the processing across Canada, and the graphics generation needed to be updated to match.)
    Map of Woodstock, New Brunswick, CA
    Map of Woodstock, New Brunswick showing rendering of highway shields. Green: primary; blue: secondary; black: tertiary; maple leaf: Trans-Canada Highway.

    There are also a lot more numbered county roads in West Virginia.
    Map of area west of Womelsdorf, West Virginia, US
    Map of the area west of Womelsdorf, West Virginia, showing that state's distinctive numbering scheme for secondary and tertiary roads.

    And suburban Suffolk County, New York has its numbered county highways now.
    Map of area south of MacArthur Airport, Bohemia, NY
    Map of the area south of MacArthur Airport, showing numbered county roads (blue pentagonal shields) rendered in a distinct style from state highways (white shields in New York's distinctive shape).


    Read more...

    Wednesday, November 4, 2020

    2020-11-04 North American highway shields

    As I mentioned earlier, I'm trying to resume work on the project of rendering North American highway banners on OpenStreetMap.

    I had left off work on the project this spring, when I was starting the sprint toward retirement, trying to make sure that my work was handed off responsibly. I had also left with a tremendous sense of frustration.

    The osm2pgsql tool, which I had been using (like most people who try to serve up map tiles) to populate a database for rendering, was not capable at the time of creating the tables I needed. I had written a detailed proposal of how what I needed might be accomplished. It was badly received; in fact, one of the core team of the project suggested that osm2pgsql was simply the wrong tool for the job.

    I then withdrew the proposal, and began to contemplate alternative tools, but was quite discouraged at the time that it would take to retool. Given the pressures at work, I put the project on hold.

    Fortunately, since then, the plan for alternative programs to populate the database has been overcome by events. Jochen Topf, unbeknownst to me, was working on a new flex back end for the program, which allows a significantly wider selection of database schemata than the original program did, and is, in fact, a superset of what I had proposed. While some post-processing will be required, it appears fit for purpose.

    So, the next tasks will be (a) retool my existing database population to use that back end (testing against a small extract, most likely Connecticut or Rhode Island), and (b) expand that back end to produce the tables that I need to describe numbered highways. With any luck, I can make some progress on that stuff today.


    Read more...

    2020-11-03 The Erie Canal in Niskayuna

    I've been curious for a while now about the old right-of-way of the Erie Canal along the Mohawk River in Niskayuna, and I've finally managed to explore some of it.

    Most of my curiosity came from the fact that when I joined the project, someone had already drawn on OpenStreetMap a trail that followed the canal right-of-way from Aqueduct Road near the traffic circle nearly to the Schenectady city line.

    Within the maintained area of Aqueduct Park, the trail pretty much follows the canal tow path, and you can see a very wet area to the southe where the canal has been filled in. There's then a rather less manicured trail that follows a natural gas pipeline (and old trolley right-of-way) up to the Mohawk-Hudson Bike/Hike Trail (Empire State Trail, Erie Canalway).

    West of there, the canal right-of-way was obviously impassable. There were, however, numerous unmarked trails that headed north and west from the gas pipeline. I'd only been in there once before, and turned back because the nettles, raspberries, and poison-ivy were just too dense for my liking. Now that we've had a hard frost, I wanted to explore. Part of the area is state land, and while the tax rolls show another part as privately owned, I saw no posters, so had few reservations about bashing off down the herd paths.

    Because the days are getting short, and walking there and back again from home is 10 km already, I kept the walks in that area fairly short. What I found was that there's a loop around the beaver pond near the bike path, and another, longer loop consisting of a path in the ditch and another on the south towpath. The flooded section is relatively short, and looks either to be intermittent or the result of beaver activity. Crossing it would still involve wading, so I scrambled the bank of the ditch and found a short beaten path through open woods to the gas line at that end.

    There is also a well-defined unmarked trail connecting the west side of the beaver pond down to the trail on the towpath and the one in the canal bed. A very short section of that is flooded, with a well-beaten workaround.

    There may be a connection with the National Grid maintenance track under the power line at the west end of the section that I explored. There's more state land to the west, so it's possible that the network goes a little farther. (There's a puzzling area on aerials - it looks as if there's a large clearing with a service building and no road access. No idea what's going on there!)

    Anyway, I uploaded a rough draft of the paths that I found and some of the soggy spots that they skirted. That was the mapping activity for this couple of days.

    One remaining puzzle: The pond by the bike path is full year-round. The stream under the bridge bu the power line runs continuously. But in a dry season, there's no water flowing across the trails, and no bridges or culverts that I could see. Where does the water go?


    Read more...

    2020-11-02 Beginning a new chapter

    Today was my first day of not reporting to work at my former employer. I'm now a full-time self-funded open-source developer (that is to say, a retiree). This post is possibly the first of a series chronicling my adventures in retirement.

    Many friends and colleagues have asked me, “so, what are your plans?”

    I can't say that I have firm plans for anything at this point. The year 2020 has been a striking example of, “do you want to make the Almighty laugh? Tell Him your plans." My immediate plan is to try to rest up and decompress a little bit.

    Frankly, I've never in my life been so without structure. I've pretty much never had a vacation longer than a week or two without an externally-imposed schedule. Even as a child, I had my parents filling, and overfilling, my “free&rdquo time with “enrichment activities”. Mary Ann and I would up doing a lot of the same with our daughter - although trying to keep her on a looser leash than the one I had - which tended to fill our time as well as hers. “Where do I go from here?” is going to taks some soul-searching. Still, I can drop some random thoughts.

    I'd really like to do more hiking. I'd been putting on weight the past few years with enforced inactivity. Having the doctors work repeatedly on my eyes and feet, with activity restrictions on the rehab, has been confining. I've been doing a lot more walking in town since COVID-19 hit - it's one of the few things I can do outside the house, and working up the distance again to where I'm regularly doing 10 km a day (without a pack, of course, or with a very light day pack.) Catherine and I have also made a few trips again into the Catskills, most of which I've yet to write about. (Slovenly of me!)

    In fact, if COVID-19 is ever not a blocker for crossing county and state lines, resupplying, or sharing a campsite, I'd like to try another big hike. My thru-hike of the Northville-Placid Trail turned into a series of sections, because of illnesses and injuries. Unlike a traditional 'thru hiker,' I couldn't just rest a few days after a problem and pick it up again - I'd need to go back to work! I've kind of an itch to do the Long Path of New York - the idea of hiking from Manhattan to the Adirondacks just intrigues me. (Yes, I know, the trail is built only to Altamont. There's a description of the route beyond there: who needs a maintained trail? I'm an experienced bush-whacko.) Given the current state of the world, I'm pretty sure that isn't going to happen in 2021. 2022, maybe, if all goes well.

    I also have several unfinished open-source projects.

    The biggest one of these, by far, is the “quadcode” system: a machine-code compiler for the Tcl programming language. Tcl is a highly dynamic language whose core paradigm is string substitution. Extracting data type information and constraining side effects to the point that safe machine code can be generated is an insanely difficult problem. Donal Fellows and I have had some limited success, but making the beast into something that's serviceable, that's deployable, and that covers much more of the language is going to be a long slog.

    The next phase here will likely involve some major refactoring, and will require some really intense brain work - something that's not compatible with my goal of getting some rest after my final sprint on the job. It'll likely be a little while before I start picking up the pieces - but watch this space!

    Another big unfinished project is to make a rendering pipeline for OpenStreetMap that deals in a graceful way with North American highway numbering systems. Identifying named and numbered routes with pictorial symbols on the map is a big part of it. Many North American jurisdictions have three or four different numbering schemata overlaid, so the shape of the marker is important to a driver trying to follow a route. Moreover, unlike the way most of the rest of the world does it, routes are often concurrent - it's not uncommon for a road over a bridge to have three or four different highway numbers. There's one pathological example near Indianapolis that has eight! This sort of overlaying has caused rendering gaffes, and a better approach than the current one is needed.

    Again, I've had some limited success making a map that handles numbered routes in more or less the way I like, but now comes the long slog to make the implementation scalable (e.g., dealing with minutely diffs on a planetary scale; managing the expiration of tiles so that the whole map need not be pre-rendered), fortify it to be robust against future change, make it fit more closely with the rendering tools used by other OpenStreetMap servers, and find a path to widespread deployment (rather than just serving to a few friends off my home machine).

    I've also been neglecting my music. I might have time in retirement to take it up again, particularly if conditions in the world improve to where people can perform again. I've been doing just a little bit - including playing recorder for a televised church service this past weekend. (The offertory , starting at 31:14, has a solo by Yours Truly.)

    What else? Well, I've got a long history of going off-script. I may find myself itching to contribute to open-source machine vision, or get back into making stuff (particularly electronics), or just try something entirely new.

    Or the perfect consulting gig might just fall in my lap. You never know. I'll just have to see what happens. Wish me luck!


    Read more...

    Thursday, July 6, 2017

    Making tiled maps, and importing into OpenStreetMap

    A user on the OSMand group asked about my process for generating my own tiled maps, and for importing parks and preserves.

    The topic, I thought, deserved a decent writeup, so I decided to work my reply into a post here.

    Workflow of getting tiles into Backcountry Navigator

    I've got a reasonably capable (quad Core i7, 32 GB memory, half a terabyte of SSD, 4 terabytes of RAID 0+1) Linux system at home, where I host a PostGIS database. By far the biggest part of that database is taken up with the OSM North America export from geofabrik.de, which I initially loaded with osm2pgsql, and synchronize with GeoFabrik's nightly diffs using osmosis. I retain the 'slim' tables because I have scripts that need them.

    Starting from Lars Ahlzen's TopOSM, I've set up a Mapnik rendering pipeline that uses OSM, the National Elevation Dataset, the USFWS national wetlands inventory, the National Hydrography Dataset, the National Landcover Dataset, and a bunch of state and local databases to assemble an American-style topo map that shows much of the information that I want to see. I've set up a previewer for that map at

    https://kbk.is-a-geek.net/catskills/test4.html

    (Feel free to pan and zoom.) What I'm set up at present to render is the US east of a line from roughly Atlanta to the Mackinac Strait, and north of Atlanta. (That's because I set it up partly for some correspondents of mine who are interested in maintaining information for approach trails to the Appalachian Trail, and the area I render is roughly the bounding box of that trail.

    The map is augmented with information that I got from a number of state and local GIS departments. For example, this view shows a number of trails in magenta. Those are trails that I got from NY State Department of Environmental Conservation's GIS department. I do NOT import those because

    1. There are license incompatibilities
    2. The data are stale, and were originally digitized from inappropriately small scale maps. In some places they're quite good indeed, but in other places they're way off.

    I find them to be a useful indication of "there is a trail somewhere near here", and a "to do" list for trail mapping with GPS.

    For the most part, those auxiliary data came in the form of shapefiles, and got imported into more tables in the PostGIS database. The Mapnik source for the map as you see it has dozens and dozens of layers.

    Since I've served the map up, I can tell BackCountry Navigator to use it as a web map, much as it would use the US topos from ArcGIS, or OpenStreetMap tiles, or Bing aerial imagery. The URL for the map is

    https://kbk.is-a-geek.net/catskills/tiles/{Z}/{X}/{Y}.jpg

    (Please don't incorporate into apps to re-share, I have limited bandwidth and even more limited time to support the thing. Also, ''please'' don't try to bulk-download all the tiles! If you need large amounts of map, email me and we'll work something out.)

    Since BackCountry Navigator supports downloading the tiles for an area in advance of a trip, I download from my home Wi-Fi before I go, and run happily without cell service in the woods.

    Importing parks and preserves

    The imports of parks and preserves have been several projects, each with its own workflow.

    New York City watershed recreation

    I did an import of New York City Watershed Recreation Lands. For that, the city made available PostScript maps of each of its facilities (note that these are located outside the city, protecting the watershed lands in the Catskill Mountains that provide New York with its water). It turns out that these PostScript files were already georeferenced, and that the names of layers in them were predictable, so I was able to set up a script that downloads them one at a time, scrapes out of the file just the boundary of the facility, and pushes the facility boundary into PostGIS.

    The script got rather complicated, because it had to check that it wasn't overwriting data that are already in OSM, repair topology of the polygons, simplify the ways, shrink the polygons back a short distance to avoid collisions, and similar tidying operations. I also developed a mapping between the descriptive attributes in the shapefile and OSM tagging.

    All the scripting was done in Tcl/Tk, for no better reason than that I'm familiar with it through having used it for about 25 years.

    I proposed the import on the OSM Wiki and went through the usual storm and fury on the 'imports' mailing list.

    The eventual import was done by taking the data, one parcel at a time, and using the JOSM remote control interface to push the polygons into JOSM. I did a final eyeball check for each, and committed them to OSM.

    I've since revisited the import once, picking up 10 new purchases, 25 boundary changes, and six modified sets of access restrictions.

    New York State Department of Environmental Conservation lands

    Emboldened by this experience, I took on reworking the seven-year-old import of the NYS Department of Environmental Conservation Lands shapefile. It was a similar workflow: pour the data into PostGIS, tidy up the geometry and topology, map tags, and so on - but on a much larger scale, and starting from a single shapefile rather than several hundred PostScript maps.

    Once again, since I had automatable data, I did this one as a formal (re)import proposal

    This was again a parcel-by-parcel effort in JOSM, but this time, there was much more manual work, since there were existing versions of the parcels that had to be conflated. It was a pretty hellish job, completed in off-and-on evening work between May and September of 2016. The hardest areas to handle were ones where complicated shorelines formed the boundaries of reserves; Saranac Lakes Wild Forest and Lake George Islands were ones that I recall as being particularly tricky.

    Conflation was also tricky if the parcels shared ways with adjacent landuse or landcover polygons. In the worst cases, I simply left the original polygons in place, but removed the tagging identifying the land as state forest, and then overlaid with the protected area.

    Once again, now that I keep after it every year or so, the modifications are more straightforward. I reimported again a couple of months ago and managed to do it in a couple of evenings.

    New York State Parks

    I then moved on to New York's State Parks. Note that the Adirondack Park and the Catskill Park are parks owned by the state, but they are not State Parks; instead they are entities unto themselves, enshrined in the state constitution.

    Each of the state parks has a georeferenced PDF trail map available from New York State Office of Parks, Recreation and Historic Preservation. Unlike the New York City PDF's, there were no vector layers for me to scrape. Moreover, the license status of the state park maps is unclear, and I live in the one Federal Circuit where government entities can claim copyright to data such as these. Instead, I treated the PDF's as a 'to do' list of parks that needed to be mapped.

    For each of these, I did the following:

    1. Converted the PDF to a GeoTIFF for efficiency, and loaded the PDF into Quantum GIS. (QGIS can read GeoPDF, but becomes unusably slow when it does.)

    2. As a separate layer, loaded up a shapefile of tax parcels owned by New York State. This shapefile has license terms compatible with ODBL - the public has the right to use the data for any lawful purpose.

    3. Selected all the tax parcels that were coterminous with the park. This could be as few as one or as many as several hundred.

    4. Conflated the parcels and repaired the topology. (This was a fair amount of manual patchwork.)

    5. Exported the tidied parcel from QGIS as a shapefile.

    6. Opened the shapefile in JOSM and downloaded the OSM data.

    7. Added tagging. For this, I wound up developing a couple of JOSM presets for 'New York State Park' and 'New York State Historic Site', and did a bunch of copy-and-paste of things like park names, web sites, and telephone numbers from parks.ny.gov.

    8. Conflated with what was already in OSM. A lot of state parks were already there, with somewhat whimsical boundaries. If the boundaries were from TIGER, I had no qualms about overwriting them.

      Please pick up after your TIGER

      If the boundaries were actually provided by a local mapper, I tried to get in touch with the mapper in question and find out how they were obtained. The mappers were very cooperative, indeed, and got back to me promptly. In virtually all cases, they had traced approximate boundaries from Bing and were happy to have the ones from the tax rolls.

      Again, there were adjacent-parcel issues, and again, I sometimes resorted to overlaying the protected area and leaving existing landcover polygons (and adjacent landuse polygons) alone.

    I didn't call this one an 'import'. I was comfortable with not doing so. There was far too much manual work involved for it to fall under the definition of 'automated edits.' Everything that went in had been touched with eyeball and mouse. Nobody complained. It is more blessed to beg forgiveness than to ask permission.

    Other land areas

    I used the same technique, with different source datasets, to fill in a number of county and municipal parks, and some private preserves. This is a work in progress, there's always more to be done. The most recent ones that I brought in were just this past weekend (2017-07-03), with a few more parcels belonging to the nonprofit Mohawk Hudson Land Conservancy.

    (I still need to get out to these and GPS the trails!)

    That's also how I sorted out the unholy mess of overlapping polygons for West Point, four state parks (Bear Mountain, Harriman, Sterling Forsest, Schunnemunk, Storm King), the Federal corridor for the Appalachian Trail, a private, open-to-the-public preserve (Black Rock Forest), the villages of Harriman, Woodbury, Fort Montgomery and Stony Point, the Woodbury golf course, and the Hudson River riverbank. What a tangle that was!

    TL;DR

    The one-line summary: "It's never easy, is it?"


    Read more...

    Sunday, January 11, 2015

    Telling inside from outside using PostGIS and Mapnik

    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.

    Dashed line shaded on lower side
    Dashed line, shaded on lower (inner) side

    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.

    Map, with natural areas showing a green inner border
    Map, with natural areas showing a green inner border

    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.

    Map, with labels on a state line
    Map, with labels on a state line

    (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.


    Read more...