Fix OSM multipolygon buildings

This commit is contained in:
2026-08-03 18:15:22 +08:00
parent 37fe65b7ca
commit 95f8458ad2
11 changed files with 458 additions and 18 deletions

View File

@@ -356,6 +356,90 @@ class ParseOsmTest(unittest.TestCase):
os.unlink(handle.name)
class ParseOsmMultipolygonTest(unittest.TestCase):
def parse_text(self, text):
handle = tempfile.NamedTemporaryFile("w", suffix=".osm", delete=False,
encoding="utf-8")
handle.write(text)
handle.close()
try:
return parse_osm(handle.name)
finally:
os.unlink(handle.name)
def test_building_relation_uses_relation_tags_and_members(self):
_, ways, _ = self.parse_text("""<?xml version='1.0' encoding='UTF-8'?>
<osm version='0.6'>
<bounds minlon='114.0' minlat='30.0' maxlon='114.01' maxlat='30.01'/>
<node id='1' lon='114.001' lat='30.001'/>
<node id='2' lon='114.004' lat='30.001'/>
<node id='3' lon='114.004' lat='30.004'/>
<node id='4' lon='114.001' lat='30.004'/>
<node id='5' lon='114.002' lat='30.002'/>
<node id='6' lon='114.003' lat='30.002'/>
<node id='7' lon='114.003' lat='30.003'/>
<node id='8' lon='114.002' lat='30.003'/>
<way id='outer'><nd ref='1'/><nd ref='2'/><nd ref='3'/><nd ref='4'/><nd ref='1'/></way>
<way id='inner'><nd ref='5'/><nd ref='6'/><nd ref='7'/><nd ref='8'/><nd ref='5'/></way>
<relation id='65'>
<member type='way' ref='outer' role='outer'/>
<member type='way' ref='inner' role='inner'/>
<tag k='type' v='multipolygon'/>
<tag k='building' v='yes'/>
<tag k='height' v='8'/>
</relation>
</osm>
""")
relation_buildings = [way for way in ways if way["id"] == "65"]
self.assertEqual(len(relation_buildings), 1)
building = relation_buildings[0]
self.assertEqual(building["tags"]["building"], "yes")
self.assertEqual(building["tags"]["height"], "8")
self.assertEqual(building["source"], "relation")
self.assertEqual(len(building["coords"]), 5)
self.assertEqual(len(building["inner_coords"]), 1)
self.assertEqual(len(building["inner_coords"][0]), 5)
def test_open_outer_members_are_stitched(self):
_, ways, _ = self.parse_text("""<?xml version='1.0' encoding='UTF-8'?>
<osm version='0.6'>
<bounds minlon='114.0' minlat='30.0' maxlon='114.01' maxlat='30.01'/>
<node id='1' lon='114.001' lat='30.001'/>
<node id='2' lon='114.004' lat='30.001'/>
<node id='3' lon='114.004' lat='30.004'/>
<node id='4' lon='114.001' lat='30.004'/>
<way id='a'><nd ref='1'/><nd ref='2'/><nd ref='3'/></way>
<way id='b'><nd ref='3'/><nd ref='4'/><nd ref='1'/></way>
<relation id='66'>
<member type='way' ref='a' role='outer'/>
<member type='way' ref='b' role='outer'/>
<tag k='type' v='multipolygon'/>
<tag k='building' v='yes'/>
</relation>
</osm>
""")
building = [way for way in ways if way["id"] == "66"][0]
self.assertEqual(len(building["coords"]), 5)
self.assertEqual(building["coords"][0], building["coords"][-1])
def test_incomplete_relation_member_is_skipped_not_fatal(self):
_, ways, _ = self.parse_text("""<?xml version='1.0' encoding='UTF-8'?>
<osm version='0.6'>
<bounds minlon='114.0' minlat='30.0' maxlon='114.01' maxlat='30.01'/>
<node id='1' lon='114.001' lat='30.001'/>
<node id='2' lon='114.004' lat='30.001'/>
<way id='broken'><nd ref='1'/><nd ref='2'/></way>
<relation id='67'>
<member type='way' ref='broken' role='outer'/>
<member type='way' ref='missing' role='outer'/>
<tag k='type' v='multipolygon'/>
<tag k='building' v='yes'/>
</relation>
</osm>
""")
self.assertEqual([way["id"] for way in ways], ["broken"])
class TagsTest(unittest.TestCase):
def test_reads_key_value_children(self):
import xml.etree.ElementTree as ET