Skip to content

Commit f7991ab

Browse files
authored
Merge pull request #2417 from lonvia/bdd-add-multigeometries
Add support for checking for Multi* geometries in BDD tests
2 parents 96a1d75 + b152dc8 commit f7991ab

File tree

6 files changed

+106
-40
lines changed

6 files changed

+106
-40
lines changed

.github/workflows/ci.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,7 @@ jobs:
384384
runs-on: windows-2025
385385

386386
env:
387-
OSMURL: https://download.geofabrik.de/europe/monaco-latest.osm.bz2
387+
OSMURL: https://download.geofabrik.de/europe/monaco-latest.osm.pbf
388388

389389
steps:
390390
- uses: actions/checkout@v4
@@ -398,8 +398,9 @@ jobs:
398398
& $env:PGBIN\psql -d osm -c "CREATE EXTENSION hstore; CREATE EXTENSION postgis;"
399399
shell: pwsh
400400
- name: Get test data
401-
run: (new-object net.webclient).DownloadFile($env:OSMURL, "testfile.osm.bz2")
401+
run: Invoke-WebRequest -Uri $env:OSMURL -OutFile "testfile.osm.pbf"
402+
shell: pwsh
402403
- name: Execute osm2pgsql
403-
run: ./osm2pgsql-bin/osm2pgsql --slim -d osm testfile.osm.bz2
404+
run: ./osm2pgsql-bin/osm2pgsql --slim -d osm testfile.osm.pbf
404405
shell: bash
405406

tests/bdd/flex/geometry-collection.feature

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,11 @@ Feature: Create geometry collections from relations
4545
| 33 | node | ST_GeometryCollection | 1 | ST_Point |
4646

4747
And table osm2pgsql_test_collection contains exactly
48-
| osm_id | ST_AsText(ST_GeometryN(geom, 1)) | ST_AsText(ST_GeometryN(geom, 2)) |
49-
| 30 | 10, 11, 12, 13, 10 | NULL |
50-
| 31 | 10, 11, 12, 13, 10 | 14, 15, 16 |
51-
| 32 | 17 | 14, 15, 16 |
52-
| 33 | 17 | NULL |
48+
| osm_id | ST_AsText(geom) |
49+
| 30 | { 10, 11, 12, 13, 10 } |
50+
| 31 | { 10, 11, 12, 13, 10; 14, 15, 16 } |
51+
| 32 | { 17; 14, 15, 16 } |
52+
| 33 | { 17 } |
5353

5454
Examples:
5555
| param |
@@ -105,6 +105,6 @@ Feature: Create geometry collections from relations
105105
When running osm2pgsql flex
106106

107107
Then table osm2pgsql_test_collection contains exactly
108-
| osm_id | name | ST_NumGeometries(geom) | ST_AsText(ST_GeometryN(geom, 1)) | ST_AsText(ST_GeometryN(geom, 2)) |
109-
| 30 | three | 2 | 10, 11, 12, 13, 10 | 10, 11, 13 |
108+
| osm_id | name | ST_AsText(geom) |
109+
| 30 | three | { 10, 11, 12, 13, 10; 10, 11, 13 } |
110110

tests/bdd/flex/geometry-linestring.feature

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@ Feature: Creating linestring features from way
3232
When running osm2pgsql flex
3333

3434
Then table osm2pgsql_test_lines contains exactly
35-
| way_id | ST_AsText(sgeom) | ST_AsText(ST_GeometryN(mgeom, 1)) | ST_AsText(ST_GeometryN(xgeom, 1)) |
36-
| 20 | 1, 2, 3 | 1, 2, 3 | 1, 2, 3 |
37-
| 21 | 4, 5 | 4, 5 | 4, 5 |
35+
| way_id | ST_AsText(sgeom) | ST_AsText(mgeom) | ST_AsText(xgeom) |
36+
| 20 | 1, 2, 3 | [ 1, 2, 3 ] | [ 1, 2, 3 ] |
37+
| 21 | 4, 5 | [ 4, 5 ] | [ 4, 5 ] |
3838

3939
Scenario:
4040
Given the grid

tests/bdd/flex/geometry-multilinestring.feature

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,11 @@ Feature: Creating (multi)linestring features from way and relations
4141
When running osm2pgsql flex
4242

4343
Then table osm2pgsql_test_lines contains exactly
44-
| osm_type | osm_id | ST_GeometryType(geom) | ST_AsText(ST_GeometryN(geom, 1)) | ST_AsText(ST_GeometryN(geom, 2)) |
45-
| W | 20 | ST_LineString | 1, 2, 3 | NULL |
46-
| W | 21 | ST_LineString | 4, 5, 6 | NULL |
47-
| R | 30 | ST_LineString | 1, 2, 3 | NULL |
48-
| R | 31 | ST_MultiLineString | 1, 2, 3 | 4, 5, 6 |
44+
| osm_type | osm_id | ST_AsText(geom) |
45+
| W | 20 | 1, 2, 3 |
46+
| W | 21 | 4, 5, 6 |
47+
| R | 30 | 1, 2, 3 |
48+
| R | 31 | [ 1, 2, 3; 4, 5, 6 ] |
4949

5050
Scenario:
5151
Given the grid
@@ -77,7 +77,7 @@ Feature: Creating (multi)linestring features from way and relations
7777
When running osm2pgsql flex
7878

7979
Then table osm2pgsql_test_roads contains exactly
80-
| relation_id | ST_GeometryType(geom) | ST_GeometryType(merged) | ST_AsText(ST_GeometryN(merged, 1)) | ST_AsText(ST_GeometryN(merged, 2)) |
81-
| 30 | ST_MultiLineString | ST_MultiLineString | 1, 2, 3 | NULL |
82-
| 31 | ST_MultiLineString | ST_MultiLineString | 1, 2 | 3, 4 |
80+
| relation_id | ST_GeometryType(geom) | ST_AsText(merged) |
81+
| 30 | ST_MultiLineString | [ 1, 2, 3 ] |
82+
| 31 | ST_MultiLineString | [ 1, 2; 3, 4 ] |
8383

tests/bdd/flex/geometry-multipoint.feature

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,9 @@ Feature: Creating (multi)point features from nodes and relations
4343
When running osm2pgsql flex
4444

4545
Then table osm2pgsql_test_points contains exactly
46-
| osm_type | osm_id | ST_GeometryType(geom) | ST_NumGeometries(geom) | ST_AsText(ST_GeometryN(geom, 1)) | ST_AsText(ST_GeometryN(geom, 2)) |
47-
| N | 1 | ST_Point | 1 | 1 | NULL |
48-
| N | 5 | ST_Point | 1 | 5 | NULL |
49-
| R | 30 | ST_Point | 1 | 1 | NULL |
50-
| R | 31 | ST_MultiPoint | 2 | 5 | 1 |
46+
| osm_type | osm_id | ST_AsText(geom) |
47+
| N | 1 | 1 |
48+
| N | 5 | 5 |
49+
| R | 30 | 1 |
50+
| R | 31 | [ 5; 1 ] |
5151

tests/bdd/steps/steps_db.py

Lines changed: 79 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -178,15 +178,43 @@ def __init__(self, value, props, factory):
178178
self.factory = factory
179179

180180
def set_coordinates(self, value):
181-
m = re.fullmatch(r'(POINT|LINESTRING|POLYGON)\((.*)\)', value)
181+
if value.startswith('GEOMETRYCOLLECTION('):
182+
geoms = []
183+
remain = value[19:-1]
184+
while remain:
185+
_, value, remain = self._parse_simple_wkt(remain)
186+
remain = remain[1:] # delete comma
187+
geoms.append(value)
188+
self.geom_type = 'GEOMETRYCOLLECTION'
189+
self.value = geoms
190+
else:
191+
self.geom_type, self.value, remain = self._parse_simple_wkt(value)
192+
if remain:
193+
raise RuntimeError('trailing content for geometry: ' + value)
194+
195+
def _parse_simple_wkt(self, value):
196+
m = re.fullmatch(r'(MULTI)?(POINT|LINESTRING|POLYGON)\(([^A-Z]*)\)(.*)', value)
182197
if not m:
183198
raise RuntimeError(f'Unparsable WKT: {value}')
184-
if m[1] == 'POINT':
185-
self.value = self._parse_wkt_coord(m[2])
186-
elif m[1] == 'LINESTRING':
187-
self.value = self._parse_wkt_line(m[2])
188-
elif m[1] == 'POLYGON':
189-
self.value = [self._parse_wkt_line(ln) for ln in m[2][1:-1].split('),(')]
199+
geom_type = (m[1] or '') + m[2]
200+
if m[1] == 'MULTI':
201+
splitup = m[3][1:-1].split('),(')
202+
if m[2] == 'POINT':
203+
value = [self._parse_wkt_coord(c) for c in splitup]
204+
elif m[2] == 'LINESTRING':
205+
value = [self._parse_wkt_line(c) for c in splitup]
206+
elif m[2] == 'POLYGON':
207+
value = [[self._parse_wkt_line(ln) for ln in poly[1:-1].split('),(')]
208+
for poly in splitup]
209+
else:
210+
if m[2] == 'POINT':
211+
value = self._parse_wkt_coord(m[3])
212+
elif m[2] == 'LINESTRING':
213+
value = self._parse_wkt_line(m[3])
214+
elif m[2] == 'POLYGON':
215+
value = [self._parse_wkt_line(ln) for ln in m[3][1:-1].split('),(')]
216+
217+
return geom_type, value, m[4]
190218

191219
def _parse_wkt_coord(self, coord):
192220
return tuple(DBValueFloat(float(f.strip()), self.precision) for f in coord.split())
@@ -195,14 +223,51 @@ def _parse_wkt_line(self, coords):
195223
return [self._parse_wkt_coord(pt) for pt in coords.split(',')]
196224

197225
def __eq__(self, other):
198-
if other.find(',') < 0:
199-
geom = self._parse_input_coord(other)
200-
elif other.find('(') < 0:
201-
geom = self._parse_input_line(other)
226+
if other.startswith('[') and other.endswith(']'):
227+
gtype = 'MULTI'
228+
toparse = other[1:-1].split(';')
229+
elif other.startswith('{') and other.endswith('}'):
230+
gtype = 'GEOMETRYCOLLECTION'
231+
toparse = other[1:-1].split(';')
202232
else:
203-
geom = [self._parse_input_line(ln) for ln in other.strip()[1:-1].split('),(')]
204-
205-
return self.value == geom
233+
gtype = None
234+
toparse = [other]
235+
236+
geoms = []
237+
for sub in toparse:
238+
sub = sub.strip()
239+
if sub.find(',') < 0:
240+
geoms.append(self._parse_input_coord(sub))
241+
if gtype is None:
242+
gtype = 'POINT'
243+
elif gtype.startswith('MULTI'):
244+
if gtype == 'MULTI':
245+
gtype = 'MULTIPOINT'
246+
elif gtype != 'MULTIPOINT':
247+
raise RuntimeError('MULTI* geometry with different geometry types is not supported.')
248+
elif sub.find('(') < 0:
249+
geoms.append(self._parse_input_line(sub))
250+
if gtype is None:
251+
gtype = 'LINESTRING'
252+
elif gtype.startswith('MULTI'):
253+
if gtype == 'MULTI':
254+
gtype = 'MULTILINESTRING'
255+
elif gtype != 'MULTILINESTRING':
256+
raise RuntimeError('MULTI* geometry with different geometry types is not supported.')
257+
else:
258+
geoms.append([self._parse_input_line(ln) for ln in sub.strip()[1:-1].split('),(')])
259+
if gtype is None:
260+
gtype = 'POLYGON'
261+
elif gtype.startswith('MULTI'):
262+
if gtype == 'MULTI':
263+
gtype = 'MULTIPOLYGON'
264+
elif gtype != 'MULTIPOLYGON':
265+
raise RuntimeError('MULTI* geometry with different geometry types is not supported.')
266+
267+
if not gtype.startswith('MULTI') and gtype != 'GEOMETRYCOLLECTION':
268+
geoms = geoms[0]
269+
270+
return gtype == self.geom_type and self.value == geoms
206271

207272
def _parse_input_coord(self, other):
208273
coords = other.split(' ')

0 commit comments

Comments
 (0)