Skip to content

Commit 8e3b76d

Browse files
committed
Fix OSM→OSW conversion when OSM Way contains consecutive duplicate nodes
https://dev.azure.com/TDEI-UW/TDEI/_workitems/edit/3286 **PR Title** Fix OSM→OSW conversion when OSM Way contains consecutive duplicate nodes **PR Summary** - Fix OSM→OSW conversion when OSM Way contains consecutive duplicate nodes (bug 3286). Instead of generating an invalid 0 length geometry, the segment is ignored. - Tested on file attached to dev board bug: osm.b8106236-b919-4e50-b956-99399635c0bb.xml - Added a new unit test
1 parent a29c3bf commit 8e3b76d

5 files changed

Lines changed: 44 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# Change log
22

3+
### 0.3.4
4+
- Fix OSM→OSW conversion when OSM Way contains consecutive duplicate nodes (bug 3286). Instead of generating an invalid 0 length geometry, the segment is ignored.
5+
36
### 0.3.3
47
- Fix OSM→OSW export classification so canonical OSM tags are used for semantic recognition and `ext:*` tags are preserved as extensions instead of being treated as feature-defining tags.
58
- Fix closed ext-only ways such as `ext:demolished:building=yes` to emit polygon output without falling through to point geometry construction.

src/osm_osw_reformatter/serializer/osm/osm_graph.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ def way(self, w) -> None:
3434

3535
d2 = {**d, **OSWWayNormalizer(tags).normalize()}
3636

37+
segment_n = 0
3738
for i in range(len(w.nodes) - 1):
3839
u = w.nodes[i]
3940
v = w.nodes[i + 1]
@@ -49,12 +50,19 @@ def way(self, w) -> None:
4950
v_lon = float(v.lon)
5051
v_lat = float(v.lat)
5152

53+
# Skip consecutive duplicate nodes. They create zero-length segments.
54+
if u_ref == v_ref:
55+
del u
56+
del v
57+
continue
58+
5259
d3 = {**d2}
53-
d3['segment'] = i
60+
d3['segment'] = segment_n
5461
d3['ndref'] = [u_ref, v_ref]
5562
self.G.add_edges_from([(u_ref, v_ref, d3)])
5663
self.G.add_node(u_ref, lon=u_lon, lat=u_lat)
5764
self.G.add_node(v_ref, lon=v_lon, lat=v_lat)
65+
segment_n += 1
5866
del u
5967
del v
6068

src/osm_osw_reformatter/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = '0.3.3'
1+
__version__ = '0.3.4'
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<osm version="0.6" generator="TDEI exporter" upload="false">
3+
<node visible="true" version="1" id="8223" lat="47.9217092" lon="-122.2499566"><tag k="ext:osm_version" v="2"/></node>
4+
<node visible="true" version="1" id="8224" lat="47.9217137" lon="-122.2499283"><tag k="ext:osm_version" v="2"/></node>
5+
<node visible="true" version="1" id="8232" lat="47.9217122" lon="-122.2499433"><tag k="ext:osm_version" v="2"/></node>
6+
<node visible="true" version="1" id="8237" lat="47.9217066" lon="-122.2499667"><tag k="ext:osm_version" v="2"/></node>
7+
<node visible="true" version="1" id="8241" lat="47.9217132" lon="-122.2499356"><tag k="ext:osm_version" v="2"/></node>
8+
<node visible="true" version="1" id="16255" lat="47.9217166" lon="-122.2495229"><tag k="ext:osm_version" v="2"/></node>
9+
<way visible="true" version="1" id="19002"><nd ref="8237"/><nd ref="8237"/><nd ref="8223"/><nd ref="8232"/><nd ref="8241"/><nd ref="8224"/><nd ref="16255"/><tag k="width" v="2.0"/><tag k="footway" v="sidewalk"/><tag k="highway" v="footway"/><tag k="incline" v="0.009"/><tag k="surface" v="concrete"/><tag k="ext:osm_version" v="3"/><tag k="ext:width_confidence" v="1.0"/><tag k="ext:sidewalk_corrected" v="yes"/></way>
10+
</osm>

tests/unit_tests/test_osm2osw/test_osm2osw.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
TEST_INVALID_NODE_TAGS_FILE = os.path.join(ROOT_DIR, 'test_files/node_with_invalid_tags.xml')
1616
TEST_TREE_FILE = os.path.join(ROOT_DIR, 'test_files/tree-test.xml')
1717
TEST_BUG_3477_FILE = os.path.join(ROOT_DIR, 'test_files/bug_3477.xml')
18+
TEST_BUG_3286_FILE = os.path.join(ROOT_DIR, 'test_files/bug_3286.xml')
1819

1920

2021
def is_valid_float(value):
@@ -350,6 +351,26 @@ async def run_test():
350351

351352
asyncio.run(run_test())
352353

354+
def test_bug_3286_consecutive_duplicate_nodes(self):
355+
osm_file_path = TEST_BUG_3286_FILE
356+
357+
async def run_test():
358+
osm2osw = OSM2OSW(osm_file=osm_file_path, workdir=OUTPUT_DIR, prefix='test')
359+
result = await osm2osw.convert()
360+
self.assertTrue(result.status)
361+
self.assertEqual(len(result.generated_files), 2)
362+
363+
for file_path in result.generated_files:
364+
if file_path.endswith('edges.geojson'):
365+
with open(file_path) as f:
366+
geojson = json.load(f)
367+
self.assertEqual(len(geojson.get("features", [])), 1)
368+
369+
for file_path in result.generated_files:
370+
os.remove(file_path)
371+
372+
asyncio.run(run_test())
373+
353374

354375
if __name__ == '__main__':
355376
unittest.main()

0 commit comments

Comments
 (0)