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