Skip to content

Commit 5e2efa7

Browse files
committed
Work-in-progress: Add geometry functions in Lua / new insert syntax
This is the future of geometry processing in Lua. Only the most important geometry functions are implemented so far. There is a new syntax for inserting rows into a table. Instead of "add_row" the "insert" function is used. "add_row" is still available and it works as before. But the new geometry functions only work with the "insert" command. The "insert" function also allows multiple geometry columns which wasn't possible with "add_row". Geometry creation functions: * as_point() (for nodes) * as_linestring() (for ways) * as_polygon() (for ways) * as_multilinestring() (for relations) * as_multipolygon() (for relations) * as_geometrycollection() (for relations) Geometry functions: * geometrytype() * srid() * transform(target_srid) * area() (for polygons) * centroid() (for polygons) * simplify(tolerance) (for linestrings) * split_multi() (for multi geometries) * #geom/__len() returns the number of geometries (0 for null geom, 1 for point/linestring/polygon, n for multi* and geometrycollection geoms) See the file flex-config/new-insert-syntax.lua for an example that shows how to use these.
1 parent f14e7ab commit 5e2efa7

13 files changed

+1154
-11
lines changed

flex-config/new-insert-syntax.lua

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
-- This config example file is released into the Public Domain.
2+
3+
-- This is Lua config showing the handling of custom geometries.
4+
--
5+
-- This is "work in progress", changes are possible.
6+
--
7+
-- Note that the insert() function is used instead of the old add_row()!
8+
9+
-- The following magic can be used to wrap the internal "insert" function
10+
local orig_insert = osm2pgsql.Table.insert
11+
local function my_insert(table, data)
12+
local json = require 'json'
13+
inserted, message, column, object = orig_insert(table, data)
14+
if not inserted then
15+
for key, value in pairs(data) do
16+
if type(value) == 'userdata' then
17+
data[key] = tostring(value)
18+
end
19+
end
20+
print("insert() failed: " .. message .. " column='" .. column .. "' object='" .. json.encode(object) .. "' data='" .. json.encode(data) .. "'")
21+
end
22+
end
23+
osm2pgsql.Table.insert = my_insert
24+
25+
26+
local tables = {}
27+
28+
-- This table will get all nodes and areas with an "addr:street" tag, for
29+
-- areas we'll calculate the centroid.
30+
tables.addr = osm2pgsql.define_table({
31+
name = 'addr',
32+
ids = { type = 'any', id_column = 'osm_id', type_column = 'osm_type' },
33+
columns = {
34+
{ column = 'tags', type = 'jsonb' },
35+
{ column = 'geom', type = 'point', projection = 4326 },
36+
}
37+
})
38+
39+
tables.ways = osm2pgsql.define_way_table('ways', {
40+
{ column = 'tags', type = 'jsonb' },
41+
{ column = 'geom', type = 'linestring', projection = 4326 },
42+
{ column = 'poly', type = 'polygon', projection = 4326 },
43+
{ column = 'geom3857', type = 'linestring', projection = 3857 },
44+
{ column = 'geomautoproj', type = 'linestring', projection = 3857 },
45+
})
46+
47+
tables.major_roads = osm2pgsql.define_way_table('major_roads', {
48+
{ column = 'tags', type = 'jsonb' },
49+
{ column = 'geom', type = 'linestring' },
50+
{ column = 'sgeom', type = 'linestring' }
51+
})
52+
53+
tables.polygons = osm2pgsql.define_area_table('polygons', {
54+
{ column = 'tags', type = 'jsonb' },
55+
{ column = 'geom', type = 'geometry', not_null = true },
56+
{ column = 'area4326', type = 'real' },
57+
{ column = 'area3857', type = 'real' }
58+
})
59+
60+
tables.routes = osm2pgsql.define_relation_table('routes', {
61+
{ column = 'tags', type = 'jsonb' },
62+
{ column = 'geom', type = 'multilinestring', projection = 4326 },
63+
})
64+
65+
tables.route_parts = osm2pgsql.define_relation_table('route_parts', {
66+
{ column = 'tags', type = 'jsonb' },
67+
{ column = 'geom', type = 'linestring', projection = 4326 },
68+
})
69+
70+
-- Helper function to remove some of the tags we usually are not interested in.
71+
-- Returns true if there are no tags left.
72+
function clean_tags(tags)
73+
tags.odbl = nil
74+
tags.created_by = nil
75+
tags.source = nil
76+
tags['source:ref'] = nil
77+
78+
return next(tags) == nil
79+
end
80+
81+
-- Helper function that looks at the tags and decides if this is possibly
82+
-- an area.
83+
function has_area_tags(tags)
84+
if tags.area == 'yes' then
85+
return true
86+
end
87+
if tags.area == 'no' then
88+
return false
89+
end
90+
91+
return tags.aeroway
92+
or tags.amenity
93+
or tags.building
94+
or tags.harbour
95+
or tags.historic
96+
or tags.landuse
97+
or tags.leisure
98+
or tags.man_made
99+
or tags.military
100+
or tags.natural
101+
or tags.office
102+
or tags.place
103+
or tags.power
104+
or tags.public_transport
105+
or tags.shop
106+
or tags.sport
107+
or tags.tourism
108+
or tags.water
109+
or tags.waterway
110+
or tags.wetland
111+
or tags['abandoned:aeroway']
112+
or tags['abandoned:amenity']
113+
or tags['abandoned:building']
114+
or tags['abandoned:landuse']
115+
or tags['abandoned:power']
116+
or tags['area:highway']
117+
end
118+
119+
function osm2pgsql.process_node(object)
120+
if clean_tags(object.tags) then
121+
return
122+
end
123+
124+
if object.tags['addr:street'] then
125+
tables.addr:insert({
126+
tags = object.tags,
127+
geom = object:as_point()
128+
})
129+
end
130+
end
131+
132+
function osm2pgsql.process_way(object)
133+
if clean_tags(object.tags) then
134+
return
135+
end
136+
137+
if object.is_closed and object.tags['addr:street'] then
138+
tables.addr:insert({
139+
tags = object.tags,
140+
geom = object:as_polygon():centroid()
141+
})
142+
end
143+
144+
if object.tags.highway == 'motorway' or
145+
object.tags.highway == 'trunk' or
146+
object.tags.highway == 'primary' then
147+
tables.major_roads:insert({
148+
tags = object.tags,
149+
geom = object:as_linestring(),
150+
sgeom = object:as_linestring():simplify(100),
151+
})
152+
end
153+
154+
-- A closed way that also has the right tags for an area is a polygon.
155+
if object.is_closed and has_area_tags(object.tags) then
156+
local g = object:as_polygon()
157+
local a = g:area()
158+
if a < 0.0000001 then
159+
tables.polygons:insert({
160+
tags = object.tags,
161+
geom = g,
162+
area4326 = a,
163+
area3857 = g:transform(3857):area()
164+
})
165+
end
166+
else
167+
tables.ways:insert({
168+
tags = object.tags,
169+
geom = object:as_linestring(),
170+
poly = object:as_polygon(),
171+
geom3857 = object:as_linestring():transform(3857), -- project geometry into target srid 3857
172+
geomautoproj = object:as_linestring() -- automatically projected into projection of target column
173+
})
174+
end
175+
end
176+
177+
function osm2pgsql.process_relation(object)
178+
if clean_tags(object.tags) then
179+
return
180+
end
181+
182+
local relation_type = object:grab_tag('type')
183+
184+
if relation_type == 'multipolygon' then
185+
local g = object:as_multipolygon()
186+
local a = g:area()
187+
if a < 0.0000001 then
188+
tables.polygons:insert({
189+
tags = object.tags,
190+
geom = g,
191+
area4326 = a,
192+
area3857 = g:transform(3857):area()
193+
})
194+
end
195+
return
196+
end
197+
198+
if relation_type == 'route' then
199+
local route_geom = object:as_multilinestring()
200+
if #route_geom > 0 then -- check that this is not a null geometry
201+
-- print(object.id, tostring(route_geom), route_geom:srid(), #route_geom)
202+
tables.routes:insert({
203+
tags = object.tags,
204+
geom = route_geom
205+
})
206+
for n, line in pairs(route_geom:split_multi()) do
207+
tables.route_parts:insert({
208+
tags = object.tags,
209+
geom = line
210+
})
211+
end
212+
end
213+
end
214+
end
215+

src/db-copy-mgr.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,13 @@ class db_copy_mgr_t
4343

4444
m_current = std::make_unique<db_cmd_copy_delete_t<DELETER>>(table);
4545
}
46+
m_committed = m_current->buffer.size();
47+
}
48+
49+
void rollback_line()
50+
{
51+
assert(m_current);
52+
m_current->buffer.resize(m_committed);
4653
}
4754

4855
/**
@@ -356,6 +363,7 @@ class db_copy_mgr_t
356363

357364
std::shared_ptr<db_copy_thread_t> m_processor;
358365
std::unique_ptr<db_cmd_copy_delete_t<DELETER>> m_current;
366+
std::size_t m_committed = 0;
359367
};
360368

361369
#endif // OSM2PGSQL_DB_COPY_MGR_HPP

src/flex-table.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ flex_table_column_t &flex_table_t::add_column(std::string const &name,
7777
auto &column = m_columns.back();
7878

7979
if (column.is_geometry_column()) {
80+
if (m_geom_column != std::numeric_limits<std::size_t>::max()) {
81+
m_has_multiple_geom_columns = true;
82+
}
8083
m_geom_column = m_columns.size() - 1;
8184
column.set_not_null();
8285
}

src/flex-table.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,11 @@ class flex_table_t
191191
std::string full_name() const;
192192
std::string full_tmp_name() const;
193193

194+
bool has_multiple_geom_columns() const noexcept
195+
{
196+
return m_has_multiple_geom_columns;
197+
}
198+
194199
private:
195200
/// The name of the table
196201
std::string m_name;
@@ -225,6 +230,9 @@ class flex_table_t
225230
/// Cluster the table by geometry.
226231
bool m_cluster_by_geom = true;
227232

233+
/// Does this table have more than one geometry column?
234+
bool m_has_multiple_geom_columns = false;
235+
228236
}; // class flex_table_t
229237

230238
class table_connection_t

src/middle-ram.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,13 @@ middle_ram_t::rel_members_get(osmium::Relation const &rel,
264264
buffer->commit();
265265
++count;
266266
}
267+
} else {
268+
{
269+
osmium::builder::NodeBuilder builder{*buffer};
270+
builder.set_id(member.ref());
271+
}
272+
buffer->commit();
273+
++count;
267274
}
268275
break;
269276
case osmium::item_type::way:

0 commit comments

Comments
 (0)