Skip to content

Commit dd35be9

Browse files
committed
Flex: Allow creating point geometry from way
This allows the as_point() function to be called for ways. The function has a single (optional) parameter specifying which member node to create the point from. The index parameter is 1-based as usual in Lua. Negative indexes count from the back, :as_point(), :as_point(nil), :as_point(0), :as_point(1) will return the first node, :as_point(2) the second node and so on. :as_point(-1) will return the last node. If the index is out of range a null geometry will be returned. If the parameter is not a number an error will be generated.
1 parent 83a1af0 commit dd35be9

File tree

6 files changed

+212
-6
lines changed

6 files changed

+212
-6
lines changed

src/geom-from-osm.cpp

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,18 @@
1919

2020
namespace geom {
2121

22+
void create_point(geometry_t *geom, osmium::Location const &location)
23+
{
24+
if (location.valid()) {
25+
auto &point = geom->set<point_t>();
26+
point.set_x(location.lon());
27+
point.set_y(location.lat());
28+
}
29+
}
30+
2231
void create_point(geometry_t *geom, osmium::Node const &node)
2332
{
24-
auto &point = geom->set<point_t>();
25-
point.set_x(node.location().lon());
26-
point.set_y(node.location().lat());
33+
create_point(geom, node.location());
2734
}
2835

2936
geometry_t create_point(osmium::Node const &node)

src/geom-from-osm.hpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,15 @@
2727

2828
namespace geom {
2929

30+
/**
31+
* Create a point geometry from a location. If the location is not valid,
32+
* the output will not be changed.
33+
*
34+
* \param geom Pointer to an existing geometry which will be used as output.
35+
* \param location The input location.
36+
*/
37+
void create_point(geometry_t *geom, osmium::Location const &location);
38+
3039
/**
3140
* Create a point geometry from a node.
3241
*

src/output-flex.cpp

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -454,11 +454,48 @@ int output_flex_t::app_get_bbox()
454454

455455
int output_flex_t::app_as_point()
456456
{
457-
check_context_and_state("as_point", "node",
458-
m_calling_context != calling_context::process_node);
457+
check_for_object(lua_state(), "as_point");
458+
459+
if (m_calling_context == calling_context::process_node) {
460+
if (lua_gettop(lua_state()) > 1) {
461+
throw fmt_error("No parameter(s) needed for as_point().");
462+
}
463+
auto *geom = create_lua_geometry_object(lua_state());
464+
geom::create_point(geom, *m_context_node);
465+
return 1;
466+
}
467+
468+
if (m_calling_context != calling_context::process_way) {
469+
throw fmt_error(
470+
"The function as_point() can only be called (directly "
471+
"or indirectly) from the process_[untagged]_node/way() functions.");
472+
}
473+
474+
if (lua_gettop(lua_state()) > 2) {
475+
throw fmt_error("Too many arguments for function as_point()");
476+
}
477+
478+
m_way_cache.add_nodes(middle());
479+
auto const &nodes = m_way_cache.get().nodes();
480+
481+
int n = 0;
482+
if (lua_gettop(lua_state()) > 1) {
483+
if (lua_type(lua_state(), 2) != LUA_TNUMBER) {
484+
throw std::runtime_error{
485+
"Argument #1 to 'as_point()' must be an integer."};
486+
}
487+
n = lua_tointeger(lua_state(), 2);
488+
if (n > 0) { // at_point() uses 1-based index numbers
489+
--n;
490+
} else if (n < 0) { // negative index values count from the back
491+
n += static_cast<int>(nodes.size());
492+
}
493+
}
459494

460495
auto *geom = create_lua_geometry_object(lua_state());
461-
geom::create_point(geom, *m_context_node);
496+
if (n >= 0 && static_cast<std::size_t>(n) < nodes.size()) {
497+
geom::create_point(geom, nodes[n].location());
498+
}
462499

463500
return 1;
464501
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
Feature: Creating point features from way
2+
3+
Scenario:
4+
Given the grid
5+
| 1 | 2 | |
6+
| 4 | | 3 |
7+
| | 5 | |
8+
And the OSM data
9+
"""
10+
w20 Thighway=motorway Nn1,n2,n3
11+
w21 Thighway=motorway Nn4,n5
12+
"""
13+
And the lua style
14+
"""
15+
local points = osm2pgsql.define_way_table('osm2pgsql_test_points', {
16+
{ column = 'n', type = 'int' },
17+
{ column = 'geom', type = 'point', projection = 4326, not_null = false },
18+
})
19+
20+
function osm2pgsql.process_way(object)
21+
if object.tags.highway == 'motorway' then
22+
points:insert({ n = nil, geom = object:as_point() })
23+
points:insert({ n = 0, geom = object:as_point(0) })
24+
points:insert({ n = 1, geom = object:as_point(1) })
25+
points:insert({ n = 2, geom = object:as_point(2) })
26+
points:insert({ n = 3, geom = object:as_point(3) })
27+
points:insert({ n = 4, geom = object:as_point(4) })
28+
points:insert({ n = -1, geom = object:as_point(-1) })
29+
points:insert({ n = -2, geom = object:as_point(-2) })
30+
points:insert({ n = -3, geom = object:as_point(-3) })
31+
end
32+
end
33+
"""
34+
When running osm2pgsql flex
35+
36+
Then table osm2pgsql_test_points contains exactly
37+
| way_id | n | ST_AsText(geom) |
38+
| 20 | NULL | 1 |
39+
| 20 | 0 | 1 |
40+
| 20 | 1 | 1 |
41+
| 20 | 2 | 2 |
42+
| 20 | 3 | 3 |
43+
| 20 | 4 | NULL |
44+
| 20 | -1 | 3 |
45+
| 20 | -2 | 2 |
46+
| 20 | -3 | 1 |
47+
| 21 | NULL | 4 |
48+
| 21 | 0 | 4 |
49+
| 21 | 1 | 4 |
50+
| 21 | 2 | 5 |
51+
| 21 | 3 | NULL |
52+
| 21 | 4 | NULL |
53+
| 21 | -1 | 5 |
54+
| 21 | -2 | 4 |
55+
| 21 | -3 | NULL |
56+
57+
Scenario:
58+
Given the grid
59+
| 1 | 2 |
60+
And the OSM data
61+
"""
62+
w20 Thighway=motorway Nn1,n2
63+
"""
64+
And the lua style
65+
"""
66+
function osm2pgsql.process_way(object)
67+
local geom = object:as_point('foo')
68+
end
69+
"""
70+
Then running osm2pgsql flex fails
71+
72+
And the error output contains
73+
"""
74+
Argument #1 to 'as_point()' must be an integer.
75+
"""
76+
77+
Scenario:
78+
Given the grid
79+
| 1 | 2 |
80+
And the OSM data
81+
"""
82+
w20 Thighway=motorway Nn1,n2
83+
"""
84+
And the lua style
85+
"""
86+
function osm2pgsql.process_way(object)
87+
local geom = object:as_point(1, 'foo')
88+
end
89+
"""
90+
Then running osm2pgsql flex fails
91+
92+
And the error output contains
93+
"""
94+
Too many arguments for function as_point()
95+
"""

tests/test-geom-linestrings.cpp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,50 @@ TEST_CASE("create_linestring from invalid OSM data", "[NoDB]")
115115
REQUIRE(geom.is_null());
116116
}
117117

118+
TEST_CASE("create_point from OSM way data", "[NoDB]")
119+
{
120+
test_buffer_t buffer;
121+
buffer.add_way("w20 Nn1x1y1,n2x2y2");
122+
123+
auto const &nodes = buffer.buffer().get<osmium::Way>(0).nodes();
124+
geom::geometry_t geom;
125+
geom::create_point(&geom, nodes[0].location());
126+
127+
REQUIRE(geom.is_point());
128+
REQUIRE(geometry_type(geom) == "POINT");
129+
REQUIRE(dimension(geom) == 0);
130+
REQUIRE(num_geometries(geom) == 1);
131+
REQUIRE(geom.get<geom::point_t>() == geom::point_t{1, 1});
132+
}
133+
134+
TEST_CASE("create_point from OSM data without locations", "[NoDB]")
135+
{
136+
test_buffer_t buffer;
137+
buffer.add_way("w20 Nn1,n2");
138+
139+
auto const &nodes = buffer.buffer().get<osmium::Way>(0).nodes();
140+
geom::geometry_t geom;
141+
geom::create_point(&geom, nodes[0].location());
142+
143+
REQUIRE(geom.is_null());
144+
}
145+
146+
TEST_CASE("create_point from way with single node", "[NoDB]")
147+
{
148+
test_buffer_t buffer;
149+
buffer.add_way("w20 Nn1x1y1");
150+
151+
auto const &nodes = buffer.buffer().get<osmium::Way>(0).nodes();
152+
geom::geometry_t geom;
153+
geom::create_point(&geom, nodes[0].location());
154+
155+
REQUIRE(geom.is_point());
156+
REQUIRE(geometry_type(geom) == "POINT");
157+
REQUIRE(dimension(geom) == 0);
158+
REQUIRE(num_geometries(geom) == 1);
159+
REQUIRE(geom.get<geom::point_t>() == geom::point_t{1, 1});
160+
}
161+
118162
TEST_CASE("geom::segmentize w/o split", "[NoDB]")
119163
{
120164
geom::linestring_t const expected{{0, 0}, {1, 2}, {2, 2}};

tests/test-geom-points.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,20 @@ TEST_CASE("geom::point_t from location", "[NoDB]")
4242
REQUIRE(p == geom::point_t{3.141, 2.718});
4343
}
4444

45+
TEST_CASE("geom::point_t from location with create_point", "[NoDB]")
46+
{
47+
osmium::Location const location{1.1, 2.2};
48+
49+
geom::geometry_t geom;
50+
geom::create_point(&geom, location);
51+
REQUIRE(geom.is_point());
52+
53+
auto const &p = geom.get<geom::point_t>();
54+
REQUIRE(p.x() == Approx(1.1));
55+
REQUIRE(p.y() == Approx(2.2));
56+
REQUIRE(p == geom::point_t{1.1, 2.2});
57+
}
58+
4559
TEST_CASE("create_point from OSM data", "[NoDB]")
4660
{
4761
test_buffer_t buffer;

0 commit comments

Comments
 (0)