diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index eb8a37cab..6afed2d12 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -62,7 +62,7 @@ jobs: run: sudo -i -u postgres createuser -s $USER && createdb -E utf8 gis && psql -Xq -d gis -c "CREATE EXTENSION postgis; CREATE EXTENSION hstore;" - name: Import empty file run: | - osm2pgsql -G --hstore --style openstreetmap-carto.style --tag-transform-script openstreetmap-carto.lua -d gis -r xml <(echo '') + osm2pgsql -O flex -S openstreetmap-carto-flex.lua -d gis -r xml <(echo '') - name: Create indexes run: psql -1Xq -v ON_ERROR_STOP=1 -d gis -f indexes.sql - name: Load empty shapefiles diff --git a/Dockerfile.import b/Dockerfile.import index feb74aa5a..685471657 100644 --- a/Dockerfile.import +++ b/Dockerfile.import @@ -14,7 +14,7 @@ RUN apt-get update && apt-get install --no-install-recommends -y \ osm2pgsql gdal-bin python3-psycopg2 python3-yaml unzip \ python3-requests postgresql-client && rm -rf /var/lib/apt/lists/* -ADD openstreetmap-carto.style / +ADD openstreetmap-carto-flex.lua / RUN mkdir -p /openstreetmap-carto WORKDIR /openstreetmap-carto diff --git a/INSTALL.md b/INSTALL.md index 74029fb1a..c68145734 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -21,7 +21,7 @@ psql -d gis -c 'CREATE EXTENSION postgis; CREATE EXTENSION hstore;' Then, grab some OSM data; It's probably easiest to grab an PBF of OSM data from [Geofabrik](https://download.geofabrik.de/). Once you've done that, import with osm2pgsql: ```sh -osm2pgsql -G --hstore --style openstreetmap-carto.style --tag-transform-script openstreetmap-carto.lua -d gis ~/path/to/data.osm.pbf +osm2pgsql -O flex -S openstreetmap-carto-flex.lua -d gis ~/path/to/data.osm.pbf ``` You can find a more detailed guide to setting up a database and loading data with osm2pgsql at [switch2osm.org](https://switch2osm.org/serving-tiles/manually-building-a-tile-server-16-04-2-lts/). diff --git a/openstreetmap-carto.lua b/openstreetmap-carto.lua deleted file mode 100644 index 8fc2c8521..000000000 --- a/openstreetmap-carto.lua +++ /dev/null @@ -1,436 +0,0 @@ --- For documentation of Lua tag transformations, see: --- https://github.com/openstreetmap/osm2pgsql/blob/master/docs/lua.md - --- Objects with any of the following keys will be treated as polygon -local polygon_keys = { - 'abandoned:aeroway', - 'abandoned:amenity', - 'abandoned:building', - 'abandoned:landuse', - 'abandoned:power', - 'aeroway', - 'allotments', - 'amenity', - 'area:highway', - 'craft', - 'building', - 'building:part', - 'club', - 'golf', - 'emergency', - 'harbour', - 'healthcare', - 'historic', - 'landuse', - 'leisure', - 'man_made', - 'military', - 'natural', - 'office', - 'place', - 'power', - 'public_transport', - 'shop', - 'tourism', - 'water', - 'waterway', - 'wetland' -} - --- Objects with any of the following key/value combinations will be treated as linestring -local linestring_values = { - golf = {cartpath = true, hole = true, path = true}, - emergency = {designated = true, destination = true, no = true, official = true, yes = true}, - historic = {citywalls = true}, - leisure = {track = true, slipway = true}, - man_made = {breakwater = true, cutline = true, embankment = true, groyne = true, pipeline = true}, - natural = {cliff = true, earth_bank = true, tree_row = true, ridge = true, arete = true}, - power = {cable = true, line = true, minor_line = true}, - tourism = {yes = true}, - waterway = {canal = true, derelict_canal = true, ditch = true, drain = true, river = true, stream = true, tidal_channel = true, wadi = true, weir = true} -} - --- Objects with any of the following key/value combinations will be treated as polygon -local polygon_values = { - aerialway = {station = true}, - boundary = {aboriginal_lands = true, national_park = true, protected_area= true}, - highway = {services = true, rest_area = true}, - junction = {yes = true}, - railway = {station = true} -} - --- The following keys will be deleted -local delete_tags = { - 'note', - 'source', - 'source_ref', - 'attribution', - 'comment', - 'fixme', - -- Tags generally dropped by editors, not otherwise covered - 'created_by', - 'odbl', - -- Lots of import tags - -- EUROSHA (Various countries) - 'project:eurosha_2012', - - -- UrbIS (Brussels, BE) - 'ref:UrbIS', - - -- NHN (CA) - 'accuracy:meters', - 'waterway:type', - -- StatsCan (CA) - 'statscan:rbuid', - - -- RUIAN (CZ) - 'ref:ruian:addr', - 'ref:ruian', - 'building:ruian:type', - -- DIBAVOD (CZ) - 'dibavod:id', - -- UIR-ADR (CZ) - 'uir_adr:ADRESA_KOD', - - -- GST (DK) - 'gst:feat_id', - -- osak (DK) - 'osak:identifier', - - -- Maa-amet (EE) - 'maaamet:ETAK', - -- FANTOIR (FR) - 'ref:FR:FANTOIR', - - -- OPPDATERIN (NO) - 'OPPDATERIN', - -- Various imports (PL) - 'addr:city:simc', - 'addr:street:sym_ul', - 'building:usage:pl', - 'building:use:pl', - -- TERYT (PL) - 'teryt:simc', - - -- RABA (SK) - 'raba:id', - - -- LINZ (NZ) - 'linz2osm:objectid', - -- DCGIS (Washington DC, US) - 'dcgis:gis_id', - -- Building Identification Number (New York, US) - 'nycdoitt:bin', - -- Chicago Building Import (US) - 'chicago:building_id', - -- Louisville, Kentucky/Building Outlines Import (US) - 'lojic:bgnum', - -- MassGIS (Massachusetts, US) - 'massgis:way_id', - - -- misc - 'import', - 'import_uuid', - 'OBJTYPE', - 'SK53_bulk:load' -} -delete_prefixes = { - 'note:', - 'source:', - -- Corine (CLC) (Europe) - 'CLC:', - - -- Geobase (CA) - 'geobase:', - -- CanVec (CA) - 'canvec:', - -- Geobase (CA) - 'geobase:', - - -- kms (DK) - 'kms:', - - -- ngbe (ES) - -- See also note:es and source:file above - 'ngbe:', - - -- Friuli Venezia Giulia (IT) - 'it:fvg:', - - -- KSJ2 (JA) - -- See also note:ja and source_ref above - 'KSJ2:', - -- Yahoo/ALPS (JA) - 'yh:', - - -- LINZ (NZ) - 'LINZ2OSM:', - 'LINZ:', - - -- WroclawGIS (PL) - 'WroclawGIS:', - -- Naptan (UK) - 'naptan:', - - -- TIGER (US) - 'tiger:', - -- GNIS (US) - 'gnis:', - -- National Hydrography Dataset (US) - 'NHD:', - 'nhd:', - -- mvdgis (Montevideo, UY) - 'mvdgis:' -} - --- Big table for z_order and roads status for certain tags. z=0 is turned into --- nil by the z_order function -local roads_info = { - highway = { - motorway = {z = 380, roads = true}, - trunk = {z = 370, roads = true}, - primary = {z = 360, roads = true}, - secondary = {z = 350, roads = true}, - tertiary = {z = 340, roads = false}, - residential = {z = 330, roads = false}, - unclassified = {z = 330, roads = false}, - road = {z = 330, roads = false}, - living_street = {z = 320, roads = false}, - pedestrian = {z = 310, roads = false}, - raceway = {z = 300, roads = false}, - motorway_link = {z = 240, roads = true}, - trunk_link = {z = 230, roads = true}, - primary_link = {z = 220, roads = true}, - secondary_link = {z = 210, roads = true}, - tertiary_link = {z = 200, roads = false}, - service = {z = 150, roads = false}, - track = {z = 110, roads = false}, - path = {z = 100, roads = false}, - footway = {z = 100, roads = false}, - bridleway = {z = 100, roads = false}, - cycleway = {z = 100, roads = false}, - steps = {z = 90, roads = false}, - platform = {z = 90, roads = false} - }, - railway = { - rail = {z = 440, roads = true}, - subway = {z = 420, roads = true}, - narrow_gauge = {z = 420, roads = true}, - light_rail = {z = 420, roads = true}, - funicular = {z = 420, roads = true}, - preserved = {z = 420, roads = false}, - monorail = {z = 420, roads = false}, - miniature = {z = 420, roads = false}, - turntable = {z = 420, roads = false}, - tram = {z = 410, roads = false}, - disused = {z = 400, roads = false}, - construction = {z = 400, roads = false}, - platform = {z = 90, roads = false}, - }, - aeroway = { - runway = {z = 60, roads = false}, - taxiway = {z = 50, roads = false}, - }, - boundary = { - administrative = {z = 0, roads = true} - }, -} - -local excluded_railway_service = { - spur = true, - siding = true, - yard = true -} ---- Gets the z_order for a set of tags --- @param tags OSM tags --- @return z_order if an object with z_order, otherwise nil -function z_order(tags) - local z = 0 - for k, v in pairs(tags) do - if roads_info[k] and roads_info[k][v] then - z = math.max(z, roads_info[k][v].z) - end - end - - if tags["highway"] == "construction" then - if tags["construction"] and roads_info["highway"][tags["construction"]] then - z = math.max(z, roads_info["highway"][tags["construction"]].z/10) - else - z = math.max(z, 33) - end - end - - return z ~= 0 and z or nil -end - ---- Gets the roads table status for a set of tags --- @param tags OSM tags --- @return 1 if it belongs in the roads table, 0 otherwise -function roads(tags) - for k, v in pairs(tags) do - if roads_info[k] and roads_info[k][v] and roads_info[k][v].roads then - if not (k ~= 'railway' or tags.service) then - return 1 - elseif not excluded_railway_service[tags.service] then - return 1 - end - end - end - return 0 -end - ---- Generic filtering of OSM tags --- @param tags Raw OSM tags --- @return Filtered OSM tags -function filter_tags_generic(tags) - -- Short-circuit for untagged objects - if next(tags) == nil then - return 1, {} - end - - -- Delete tags listed in delete_tags - for _, d in ipairs(delete_tags) do - tags[d] = nil - end - - -- By using a second loop for wildcards we avoid checking already deleted tags - for tag, _ in pairs (tags) do - for _, d in ipairs(delete_prefixes) do - if string.sub(tag, 1, string.len(d)) == d then - tags[tag] = nil - break - end - end - end - - -- Filter out objects that have no tags after deleting - if next(tags) == nil then - return 1, {} - end - - -- Convert layer to an integer - tags['layer'] = layer(tags['layer']) - return 0, tags -end - --- Filtering on nodes -function filter_tags_node (keyvalues, numberofkeys) - return filter_tags_generic(keyvalues) -end - --- Filtering on relations -function filter_basic_tags_rel (keyvalues, numberofkeys) - -- Filter out objects that are filtered out by filter_tags_generic - local filter, keyvalues = filter_tags_generic(keyvalues) - if filter == 1 then - return 1, keyvalues - end - - -- Filter out all relations except route, multipolygon and boundary relations - if ((keyvalues["type"] ~= "route") and (keyvalues["type"] ~= "multipolygon") and (keyvalues["type"] ~= "boundary")) then - return 1, keyvalues - end - - return 0, keyvalues -end - --- Filtering on ways -function filter_tags_way (keyvalues, numberofkeys) - local filter = 0 -- Will object be filtered out? - local polygon = 0 -- Will object be treated as polygon? - - -- Filter out objects that are filtered out by filter_tags_generic - filter, keyvalues = filter_tags_generic(keyvalues) - if filter == 1 then - return filter, keyvalues, polygon, roads - end - - polygon = isarea(keyvalues) - - -- Add z_order column - keyvalues["z_order"] = z_order(keyvalues) - - return filter, keyvalues, polygon, roads(keyvalues) -end - ---- Handling for relation members and multipolygon generation --- @param keyvalues OSM tags, after processing by relation transform --- @param keyvaluemembers OSM tags of relation members, after processing by way transform --- @param roles OSM roles of relation members --- @param membercount number of members --- @return filter, cols, member_superseded, boundary, polygon, roads -function filter_tags_relation_member (keyvalues, keyvaluemembers, roles, membercount) - local members_superseded = {} - - -- Start by assuming that this not an old-style MP - for i = 1, membercount do - members_superseded[i] = 0 - end - - local type = keyvalues["type"] - - -- Remove type key - keyvalues["type"] = nil - - -- Filter out relations with just a type tag or no tags - if next(keyvalues) == nil then - return 1, keyvalues, members_superseded, 0, 0, 0 - end - - if type == "boundary" or (type == "multipolygon" and keyvalues["boundary"]) then - keyvalues.z_order = z_order(keyvalues) - return 0, keyvalues, members_superseded, 1, 0, roads(keyvalues) - -- For multipolygons... - elseif (type == "multipolygon") then - -- Multipolygons by definition are polygons, so we know roads = linestring = 0, polygon = 1 - keyvalues.z_order = z_order(keyvalues) - return 0, keyvalues, members_superseded, 0, 1, 0 - elseif type == "route" then - keyvalues.z_order = z_order(keyvalues) - return 0, keyvalues, members_superseded, 1, 0, roads(keyvalues) - end - - -- Unknown type of relation or no type tag - return 1, keyvalues, members_superseded, 0, 0, 0 -end - ---- Check if an object with given tags should be treated as polygon --- @param tags OSM tags --- @return 1 if area, 0 if linear -function isarea (tags) - -- Treat objects tagged as area=yes polygon, other area as no - if tags["area"] then - return tags["area"] == "yes" and 1 or 0 - end - - -- Search through object's tags - for k, v in pairs(tags) do - -- Check if it has a polygon key and not a linestring override, or a polygon k=v - for _, ptag in ipairs(polygon_keys) do - if k == ptag and v ~= "no" and not (linestring_values[k] and linestring_values[k][v]) then - return 1 - end - end - - if (polygon_values[k] and polygon_values[k][v]) then - return 1 - end - end - return 0 -end - -function is_in (needle, haystack) - for index, value in ipairs (haystack) do - if value == needle then - return true - end - end - return false -end - ---- Normalizes layer tags --- @param v The layer tag value --- @return An integer for the layer tag -function layer (v) - return v and string.find(v, "^-?%d+$") and tonumber(v) < 100 and tonumber(v) > -100 and v or nil -end diff --git a/openstreetmap-carto.style b/openstreetmap-carto.style deleted file mode 100644 index 0a30add22..000000000 --- a/openstreetmap-carto.style +++ /dev/null @@ -1,55 +0,0 @@ -# This is the osm2pgsql .style file for openstreetmap-carto. -# It is intended to be used with openstreetmap-carto.lua and osm2pgsql Lua -# transforms. Full usage details are in INSTALL.md -# Among things, this means that the linear vs polygon distinction in this file -# doesn't matter, because that is set in the Lua and this file is only used for -# column names and types. - -# OsmType Tag DataType Flags -node,way access text linear -node,way addr:housename text linear -node,way addr:housenumber text linear -way addr:interpolation text linear -node,way admin_level text linear -node,way aerialway text linear -node,way aeroway text polygon -node,way amenity text polygon -node,way barrier text linear -way bicycle text linear -way bridge text linear -node,way boundary text linear -node,way building text polygon -way construction text linear -way covered text linear -way foot text linear -node,way highway text linear -node,way historic text polygon -way horse text linear -node,way junction text linear -node,way landuse text polygon -node,way layer int4 linear -node,way leisure text polygon -node,way lock text linear -node,way man_made text polygon -node,way military text polygon -node,way name text linear -node,way natural text polygon -node,way oneway text linear -node,way place text polygon -node,way power text polygon -node,way railway text linear -node,way ref text linear -node,way religion text linear -way route text linear -way service text linear -node,way shop text polygon -way surface text linear -node,way tourism text polygon -way tracktype text linear -way tunnel text linear -node,way water text polygon -node,way waterway text polygon -way way_area real linear # This is calculated during import - -# Columns defined in openstreetmap-carto.lua file -way z_order int4 linear diff --git a/scripts/docker-startup.sh b/scripts/docker-startup.sh index c3177ee9d..a2263cfec 100644 --- a/scripts/docker-startup.sh +++ b/scripts/docker-startup.sh @@ -43,13 +43,11 @@ EOF osm2pgsql \ --cache $OSM2PGSQL_CACHE \ --number-processes $OSM2PGSQL_NUMPROC \ - --hstore \ - --multi-geometry \ --database gis \ --slim \ --drop \ - --style openstreetmap-carto.style \ - --tag-transform-script openstreetmap-carto.lua \ + --output flex \ + --style openstreetmap-carto-flex.style \ $OSM2PGSQL_DATAFILE # Downloading and importing needed shapefiles