diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d4d417..adacb26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Change log +### 0.3.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. + ### 0.3.3 - 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. - Fix closed ext-only ways such as `ext:demolished:building=yes` to emit polygon output without falling through to point geometry construction. diff --git a/src/osm_osw_reformatter/serializer/osm/osm_graph.py b/src/osm_osw_reformatter/serializer/osm/osm_graph.py index 57617f2..ea5b965 100644 --- a/src/osm_osw_reformatter/serializer/osm/osm_graph.py +++ b/src/osm_osw_reformatter/serializer/osm/osm_graph.py @@ -34,6 +34,7 @@ def way(self, w) -> None: d2 = {**d, **OSWWayNormalizer(tags).normalize()} + segment_n = 0 for i in range(len(w.nodes) - 1): u = w.nodes[i] v = w.nodes[i + 1] @@ -49,12 +50,19 @@ def way(self, w) -> None: v_lon = float(v.lon) v_lat = float(v.lat) + # Skip consecutive duplicate nodes. They create zero-length segments. + if u_ref == v_ref: + del u + del v + continue + d3 = {**d2} - d3['segment'] = i + d3['segment'] = segment_n d3['ndref'] = [u_ref, v_ref] self.G.add_edges_from([(u_ref, v_ref, d3)]) self.G.add_node(u_ref, lon=u_lon, lat=u_lat) self.G.add_node(v_ref, lon=v_lon, lat=v_lat) + segment_n += 1 del u del v diff --git a/src/osm_osw_reformatter/version.py b/src/osm_osw_reformatter/version.py index 80eb7f9..bfeb9e7 100644 --- a/src/osm_osw_reformatter/version.py +++ b/src/osm_osw_reformatter/version.py @@ -1 +1 @@ -__version__ = '0.3.3' +__version__ = '0.3.4' diff --git a/tests/unit_tests/test_files/bug_3286.xml b/tests/unit_tests/test_files/bug_3286.xml new file mode 100644 index 0000000..2bcbff9 --- /dev/null +++ b/tests/unit_tests/test_files/bug_3286.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/tests/unit_tests/test_osm2osw/test_osm2osw.py b/tests/unit_tests/test_osm2osw/test_osm2osw.py index b22506d..3fb4d6c 100644 --- a/tests/unit_tests/test_osm2osw/test_osm2osw.py +++ b/tests/unit_tests/test_osm2osw/test_osm2osw.py @@ -15,6 +15,7 @@ TEST_INVALID_NODE_TAGS_FILE = os.path.join(ROOT_DIR, 'test_files/node_with_invalid_tags.xml') TEST_TREE_FILE = os.path.join(ROOT_DIR, 'test_files/tree-test.xml') TEST_BUG_3477_FILE = os.path.join(ROOT_DIR, 'test_files/bug_3477.xml') +TEST_BUG_3286_FILE = os.path.join(ROOT_DIR, 'test_files/bug_3286.xml') def is_valid_float(value): @@ -350,6 +351,26 @@ async def run_test(): asyncio.run(run_test()) + def test_bug_3286_consecutive_duplicate_nodes(self): + osm_file_path = TEST_BUG_3286_FILE + + async def run_test(): + osm2osw = OSM2OSW(osm_file=osm_file_path, workdir=OUTPUT_DIR, prefix='test') + result = await osm2osw.convert() + self.assertTrue(result.status) + self.assertEqual(len(result.generated_files), 2) + + for file_path in result.generated_files: + if file_path.endswith('edges.geojson'): + with open(file_path) as f: + geojson = json.load(f) + self.assertEqual(len(geojson.get("features", [])), 1) + + for file_path in result.generated_files: + os.remove(file_path) + + asyncio.run(run_test()) + if __name__ == '__main__': unittest.main()