Showing posts with label osm. Show all posts
Showing posts with label osm. Show all posts

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

Tuesday, January 17, 2012

Mapping New York - point data

We're just about done with the basemap. While we'll eventually augment it - we'll come back later to adding things like hiking tracks in the state forest lands - I'll skip them for now because they're irrelevant to my map of the Niskayuna nature preserves.

About all that remains is to add data about points of interest (and other single-point features).
All of the data that I'm adding at this step is already in the database from the ‘new_york.osm’ file. It's all in the 'point' table.

The OSM point data is, you should forgive the expression, all over the map in terms of what it describes. In order to get it to layer correctly with other things, I've broken it out into multiple layers in the QGIS project:

  • Highway points - those for which

    upper(geometrytype("way")) IN ('POINT','MULTIPOINT')
    AND "highway" IS NOT NULL
    AND "highway" NOT IN ('bus_stop', 'crossing', 'stop_sign', 'street_lamp,
    'traffic_signals', 'traffic_signals;motorway_junction',
    'unclassified', 'yield')

    These are things like roundabouts, turning circles, and freeway interchanges. They layer right next to the roads, and have a simpleminded style sheet. (The only thing of interest is that its symbols are sized in map units rather than millimetres - I represent a traffic circle by a circle of diameter 15 m at map scale.) Eventually, I want to do better labeling for numbered interchanges, but that can wait for a version of QGIS that has highway shields.

  • A mixed bag of features that includes schools, houses of worship, hospitals, public toilets, miscellaneous buildings, helipads, gates, and boat launches. (I expect this list to expand over time, depending on what is relevant to a given map. There are dozens of things that might be included.)

    upper(geometrytype("way")) IN ('POINT','MULTIPOINT')
    AND place IS NULL
    AND highway IS NULL
    AND (amenity IS NULL OR amenity NOT IN ('fire_hydrant'))

    This layer goes above the building footprints.

  • Place names (and names of dams, which are shown the same way). This layer is topmost in the stack. Its style displays nothing. It merely exists as a place for the labeling engine to add the names of objects.

    "place" IN ('city', 'hamlet', 'island', 'islet', 'locality', 'suburb',
    'town', 'unincorporated_area', 'village')
    OR "waterway" IN ('dam')



With all of this in place, the map appears quite informative - except that it's missing one of the preserves that I want to show! (The Mohawk River State Park, formerly the Schenectady Museum Nature Preserve.) In the next installment, I'll try to get that on the map.

In the meantime, here are the style sheets for the layers I discussed in this lesson:



Read more...

Monday, January 16, 2012

Mapping New York - more polygons

Last time, we got the map to a pretty decent topographic road map. (In a future installment, I may discuss curating the OpenStreetMap data; it has recurrent errors that come from the Census Bureau TIGER files. But that's a side issue.) The next thing that I want to get into place is some polygon data: "who owns this land? What's it used for? Does the public have right of access? Where are the landmark buildings?

The relevant part of the first set of questions can mostly be answered by a database of publicly-owned lands. This database is getting into territory where the public databases aren't quite up to snuff. Within the Adirondack and Catskill Blue Line, the data are readily available from NYSGIS. The usual drill of using ‘ogr2ogr’ loads them into PostGIS:

ogr2ogr -f PostgreSQL -overwrite -t_srs EPSG:32618 \
"PG:dbname=gis" DEC_Lands.shp \
-nln nys_dec_lands -nlt MULTIPOLYGON -lco PRECISION=NO

Once again, the ‘-t_srs’ option is there to reproject the data into the projection that I intend to use for the finished map, and the ‘-lco PRECISION=NO’ works around a bug that causes a failure in inserting some of the numeric data.

Outside the Blue Line, the data come from a different place: the New York State Office of Cybersecurity. (I'd be intrigued to know why they became the custodian of the data.) In any case, they have a collection of files available on the NYSGIS web site.

Rather than putting each of these files into a separate table in the database, and hence needing a separate layer to show them, I decided to integrate them into a single table, and add a column to the data representing which data set a given row came from. For this, I decided to resort to scripting. Pulling out my handy-dandy Tcl interpreter, I ran the following loadall.tcl script:

set firsttime true
# Find all the shapefiles in the working directory
foreach file [glob *.shp] {

# Extract the base name of each file, and create an 'ogr2ogr' command to load it
set base [file rootname [file tail $file]]
set cmd [list ogr2ogr -f "PostgreSQL"]
if {$firsttime} {
set firsttime false
lappend cmd -overwrite
} else {
lappend cmd -append
}
# Add a 'data_source' column to identify which file we loaded
lappend cmd -sql "SELECT *, '$base' AS data_source FROM $base" \
-t_srs EPSG:32618 \
-skipfailures \
"PG:dbname=gis" \
$file \
-nln nys_public_land_boundaries \
-nlt MULTIPOLYGON \
-lco PRECISION=NO
# Report on the console which file we're processing, and load it
puts $cmd
exec {*}$cmd >@stdout 2>@stderr
}

With these boundaries, what I mostly care about is “recreational” (go ahead and access) versus “nonrecreational” (permission needed, or special land use such as prisons and schools). (And I also want to treat the ‘AdirondackCatskill’ file specially, because that's the Blue Line, rather than reflecting public ownership.

Both of these layers need some styling. Rather than walk through that whole process, I have QML files attached at the end of the post.

Dealing with OpenStreetMap polygon data is rather more complicated, because it's got so many different things in the same file. I therefore made several different layers, with SQL queries to extract specific features.

(1) The first layer, I just left with the name, new_york_osm_polygon. This layer really represents "here are polygons that I don't know what to do with, yet." It is stacked behind everything else, and I usually leave it unchecked unless I'm actively working on styling for polygons. Its query looks like:

admin_level IS NULL
AND ("boundary" IS NULL OR "boundary" NOT IN ('national_park'))
AND ("waterway" IS NULL OR "waterway" IN ('boatyard','dam','dock','rapids','waterfall'))
AND ("natural" IS NULL OR "natural" NOT IN ('bay', 'marsh', 'pond', 'swamp', 'water','waterway','wetland'))

This query excludes:

  • Anything with an ‘admin_level’ attribute: these are administrative regions (states, counties, cities, towns, etc.)

  • National park boundaries. I have these more accurately in the NYS Public Lands file.

  • All waterways, other than man-made assets on the water.

  • All wetlands, I've taken care of those already.


I style this letter either in bright yellow or icky purple, just to call attention to the unclassified features.

(2) The next layer is the layer where I describe land use. Its SQL query looks like:

"landuse" IS NOT NULL
OR ("leisure" IS NOT NULL AND "leisure" NOT IN ('ice_rink','pitch', 'track', 'tennis_court'))
OR "aeroway" IS NOT NULL
OR (amenity IN ('school', 'college', 'university', 'hospital') AND building IS NULL)

which translates to:

  • Land use polygons

  • Polygons marked 'leisure', except for a handful that usually appear inside parks and want to be rendered at a higher level.

  • Polygons marked 'aeroway'.

  • Polygons marked 'school', 'college', 'university', or 'hospital', except for buildings (these allow highlighting of campuses).


This set may need to be considered a work in progress; I expect these rules will need to be tweaked depending on the theme of the map.

For this layer, I created a fairly complex style with rules that fill the polygons in different colors according to land use. The QML is attached.

(3) Next up are the 'public lands' and 'DEC lands' layers, which I already discussed.

(4) Next, I have a few more types of region from OpenStreetMap. I call the layer OSM Subregion, and its query looks like:

(amenity IN ('parking')
OR leisure IN ('Dog Run', 'pitch' ,'tennis court','track'))
OR aeroway IN ('apron')
AND building IS NULL

so that it includes parking lots, dog runs, playing fields, tennis courts, racetracks, and airport aprons. What these types of object have in common is that they are usually layered atop another object (a shopping mall, industrial facility, park, airport, etc.), and so they look better rendered in an upper layer. Once again, I've attached a QML file to style them. This layer goes above the 'public lands' layers, but below the contour lines.

(5) Finally, there are polygons for a few man-made features:

building IS NOT NULL
OR leisure IN ('pool','swimmin_pool','swimming_pool','wading_pool')

that is to say, buildings and swimming pools. This layer goes very high in the stack - above the roads. It provides footprints for these structures. Once again, I've attached the QML that styles it.

Wow, that's a lot of layers from one data set. But with them all in place, our map now has quite a lot of detail.
Added polygon data to the basemap
Next time, we'll make the map prettier, by adding shaded relief.

Attachments:


Read more...

Saturday, January 14, 2012

Back to the street map

Last time we left off with a map that was getting pretty good at showing physical features. It displays waterways (rivers, streams, lakes), topography (with contour lines) and makes a first attempt at land cover (at least showing wetlands). Now, it's time to move back to the man-made features.

The first thing that nearly everyone wants to see on a map are the linear features: streets, roads, railroads, canals, tracks and so on. We've already loaded a wealth of information about these from Open Street Map into the PostGIS database. But we still need to show the stuff with appropriate styling. This styling winds up getting done with progressively more complex sets of rules, distinguishing one type of line from another: people like to see, for instance, a specific style of line for “hard surfaced secondary road” or “abandoned railroad.” Coming up with these rules is rather tedious, and I won't make you read through an entire posting of what the rules are. Rather, I'll just refer to QML files that contain the rules and link to them at the bottom of the posting.

The first thing we're going to have to do with the lines in the Open Street Map data is to get rid of the ones we don't want. Administrative boundaries, we'll skip for now. (I'm still working on getting those styled well.) We've already mostly done the waterways: we want to do some additional work to put in dams, locks, rapids and so on, but we want to exclude the lakes and streams. And 'routes' - such as numbered highways - duplicate the roads. We'll handle them separately.

So we need to select ‘new_york_osm_line’ (or whatever name we used to import the line graphics), and pull ‘Query’ from the 'Layer' menu. We enter the SQL query:

upper(geometrytype("way")) IN ('LINESTRING','MULTILINESTRING')
AND ("waterway" IS NULL
OR "waterway" IN ('breakwater', 'dam', 'dock', 'flume', 'lock_gate',
'rapids', 'waterfall', 'weir'))
AND ("boundary" IS NULL)
AND ("route" IS NULL)

(Read the query as: “We want only line graphics, that aren't waterways, boundaries, or routes, except that breakwaters, dams, ..., weirs are acceptable waterways.) Now we have a manageable set of lines to work with. After considerable fiddling, we come out with a set of rule-based styles to render them:
Styling OSM Roads
We also want to show highway numbers. While showing them on shields is promised functionality for QGIS, it isn't in the development main line yet. So for right now, we just use ordinary labels. We create a new layer against the same OpenStreenMap line layer, changing the query this time to:

"highway" IS NOT NULL AND "ref" IS NOT NULL

(which selects numbered highways). We render nothing for this layer: the easiest way to do this is to select “Simple Line” as the rendering style, and then select “No Pen” as the pen style. We use the “Labeling” dialog (from the “Layer” menu) to put in the labels, giving them a red foreground and a white buffer. When QGIS gets highway shields, we'll be able to change this one place and show proper shields instead.
Added OSM roads (and other line features)

Not too bad! The QML for the street style sheet is attached at the end of the post.

Next time, we'll start putting in some land use data, and add some building footprints.

Attachments

Read more...