Skip to content

Commit 95508e2

Browse files
authored
Merge pull request #2333 from joto/locator
New feature: Check location of OSM objects against list of regions
2 parents f4a93e2 + 24ce38f commit 95508e2

25 files changed

+1247
-6
lines changed

.github/workflows/luacheck.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,5 @@ jobs:
1515
sudo apt-get install -yq --no-install-suggests --no-install-recommends lua-check
1616
1717
- name: Run luacheck
18-
run: luacheck flex-config/*.lua flex-config/gen/*.lua tests/data/*.lua tests/lua/tests.lua
18+
run: luacheck flex-config/*.lua flex-config/*/*.lua tests/data/*.lua tests/lua/tests.lua
1919

flex-config/locator/README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Flex Output Configuration for Locator
2+
3+
These are config file examples for use with the *locator* functionality.
4+
5+
First use the `import-countries.lua` to import country boundaries, then
6+
use these with the other config files.
7+
8+
You should be able to run this on the planet file or on any extracts you want.
9+

flex-config/locator/buildings.lua

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
-- This config example file is released into the Public Domain.
2+
3+
-- This file shows how to use a locator to tag all buildings with the country
4+
-- they are in.
5+
6+
-- Define the "countries" locator and get all country geometries from the
7+
-- database. Use the import-countries.lua file to import them first, before
8+
-- you run this.
9+
local countries = osm2pgsql.define_locator({ name = 'countries' })
10+
11+
-- The SELECT query must return the regions the locator will use. The first
12+
-- column must contain the name of the region, the second the geometry in
13+
-- WGS84 lon/lat coordinates.
14+
-- To improve the efficiency of the country lookup, we'll subdivide the
15+
-- country polygons into smaller pieces. (If you run this often, for instance
16+
-- when doing updates, do the subdivide first in the database and store the
17+
-- result in its own table.)
18+
countries:add_from_db('SELECT code, ST_Subdivide(geom, 200) FROM countries')
19+
20+
-- You have to decide whether you are interested in getting all regions
21+
-- intersecting with any of the objects or only one of them.
22+
--
23+
-- * Getting all regions makes sure that you get everything, even if regions
24+
-- overlap or objects straddle the border between regions. Use the function
25+
-- all_intersecting() for that.
26+
-- * Getting only one region is faster because osm2pgsql can stop looking
27+
-- for matches after the first one. Use first_intersecting() for that.
28+
-- This makes sense if you only have a single region anyway or if your
29+
-- regions don't overlap or you are not so concerned with what happens at
30+
-- the borders.
31+
--
32+
-- Just for demonstration, we do both in this example, in the "country" and
33+
-- "countries" columns, respectively.
34+
local buildings = osm2pgsql.define_area_table('buildings', {
35+
36+
-- This will contain the country code of the first matching country
37+
-- (which can be any of the countries because there is no order here).
38+
{ column = 'country', type = 'text' },
39+
40+
-- This array will contain the country codes of all matching countries.
41+
{ column = 'countries', sql_type = 'text[]' },
42+
{ column = 'tags', type = 'jsonb' },
43+
{ column = 'geom', type = 'polygon', not_null = true },
44+
})
45+
46+
local function add(geom, tags)
47+
buildings:insert({
48+
country = countries:first_intersecting(geom), -- or use geom:centroid()
49+
50+
-- We have to create the format that PostgreSQL expects for text
51+
-- arrays. We assume that the region names do not contain any special
52+
-- characters, otherwise we would need to do some escaping here.
53+
countries = '{' .. table.concat(countries:all_intersecting(geom), ',') .. '}',
54+
55+
tags = tags,
56+
geom = geom,
57+
})
58+
end
59+
60+
function osm2pgsql.process_way(object)
61+
if object.tags.building then
62+
add(object:as_polygon(), object.tags)
63+
end
64+
end
65+
66+
function osm2pgsql.process_relation(object)
67+
if object.tags.building then
68+
local geom = object:as_multipolygon()
69+
for p in geom:geometries() do
70+
add(p, object.tags)
71+
end
72+
end
73+
end
74+

flex-config/locator/iceland.lua

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
-- This config example file is released into the Public Domain.
2+
3+
-- This file shows how to use a locator with a bounding box to import only
4+
-- the data for a region. In this case only highways in Iceland are imported
5+
-- even if you run this on the full planet file.
6+
7+
local iceland = osm2pgsql.define_locator({ name = 'iceland' })
8+
9+
iceland:add_bbox('IS', -25.0, 62.0, -12.0, 68.0)
10+
11+
local highways = osm2pgsql.define_way_table('highways', {
12+
{ column = 'hwtype', type = 'text', not_null = true },
13+
{ column = 'name', type = 'text' },
14+
{ column = 'ref', type = 'text' },
15+
{ column = 'geom', type = 'linestring', not_null = true },
16+
})
17+
18+
function osm2pgsql.process_way(object)
19+
local t = object.tags
20+
if t.highway then
21+
local geom = object:as_linestring()
22+
local region = iceland:first_intersecting(geom)
23+
if region then
24+
highways:insert({
25+
hwtype = t.highway,
26+
name = t.name,
27+
ref = t.ref,
28+
geom = geom,
29+
})
30+
end
31+
end
32+
end
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
-- This config example file is released into the Public Domain.
2+
3+
-- This file is part of the examples for using the locator function of
4+
-- osm2pgsql. It is used to import all country boundaries into a database.
5+
6+
local countries = osm2pgsql.define_relation_table('countries', {
7+
-- For the ISO3166-1 Alpha-2 country code
8+
-- https://en.wikipedia.org/wiki/ISO_3166-1
9+
{ column = 'code', type = 'text', not_null = true },
10+
-- Because we want to use the geometries for the locator feature they
11+
-- must be in 4326! We use a polygon type here and will later split
12+
-- multipolygons into their parts.
13+
{ column = 'geom', type = 'polygon', not_null = true, projection = 4326 },
14+
})
15+
16+
function osm2pgsql.process_relation(object)
17+
local t = object.tags
18+
19+
if t.boundary == 'administrative' and t.admin_level == '2' then
20+
local code = t['ISO3166-1']
21+
22+
-- Ignore entries with syntactically invalid ISO code
23+
if not code or not string.match(code, '^%u%u$') then
24+
return
25+
end
26+
27+
for geom in object:as_multipolygon():geometries() do
28+
countries:insert({
29+
code = code,
30+
geom = geom,
31+
})
32+
end
33+
end
34+
end
35+
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
-- This config example file is released into the Public Domain.
2+
3+
-- This file shows how to use a locator to find the country-specific colour
4+
5+
-- Define the "countries" locator and get all country geometries from the
6+
-- database. Use the import-countries.lua file to import them first, before
7+
-- you run this.
8+
local countries = osm2pgsql.define_locator({ name = 'countries' })
9+
countries:add_from_db('SELECT code, ST_Subdivide(geom, 200) FROM countries')
10+
11+
local highways = osm2pgsql.define_way_table('highways', {
12+
{ column = 'hwtype', type = 'text' },
13+
{ column = 'country', type = 'text' },
14+
{ column = 'colour', type = 'text' },
15+
{ column = 'geom', type = 'linestring', not_null = true },
16+
})
17+
18+
-- Each country uses their own colour for motorways. Here is the beginning
19+
-- of a list of some countries in Europe. Source:
20+
-- https://en.wikipedia.org/wiki/Comparison_of_European_road_signs
21+
local cc2colour = {
22+
BE = '#2d00e5',
23+
CH = '#128044',
24+
DE = '#174688',
25+
FR = '#333b97',
26+
NL = '#064269',
27+
}
28+
29+
function osm2pgsql.process_way(object)
30+
if object.tags.highway then
31+
local geom = object:as_linestring()
32+
local cc = countries:first_intersecting(geom)
33+
highways:insert({
34+
hwtype = object.tags.highway,
35+
country = cc,
36+
colour = cc2colour[cc],
37+
geom = geom,
38+
})
39+
end
40+
end

src/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ target_sources(osm2pgsql_lib PRIVATE
2121
flex-lua-expire-output.cpp
2222
flex-lua-geom.cpp
2323
flex-lua-index.cpp
24+
flex-lua-locator.cpp
2425
flex-lua-table.cpp
2526
flex-table-column.cpp
2627
flex-table.cpp
@@ -33,6 +34,7 @@ target_sources(osm2pgsql_lib PRIVATE
3334
geom.cpp
3435
idlist.cpp
3536
input.cpp
37+
locator.cpp
3638
logging.cpp
3739
lua-setup.cpp
3840
lua-utils.cpp

0 commit comments

Comments
 (0)