def test_get_id(self): buil = {"localId": "XXXXXX14XXXXXX"} part = {"localId": "XXXXXX14XXXXXX_part9"} pool = {"localId": "XXXXXX14XXXXXX_PI.99"} addr = {"localId": "99.999.9.9.XXXXXX14XXXXXX"} self.assertTrue(ConsLayer.get_id(part), buil["localId"]) self.assertTrue(ConsLayer.get_id(pool), buil["localId"]) self.assertTrue(ConsLayer.get_id(addr), buil["localId"])
def create_missing_parcels(self, *sources, split=None): """Create fake parcels for buildings not contained in any.""" pa_refs = [f["localId"] for f in self.getFeatures()] to_add = {} for source in sources: if source is None: continue for feat in source.getFeatures(): ref = ConsLayer.get_id(feat) if ref not in pa_refs: mp = Geometry.get_outer_rings(feat) geom = Geometry.fromMultiPolygonXY(mp) if ref in to_add: parcel = to_add[ref] geom = parcel.geometry().combine(geom) parcel.setGeometry(geom) to_add[ref] = parcel elif split is None or split.is_inside_area(geom): parcel = QgsFeature(self.fields()) parcel["localId"] = ref parcel.setGeometry(geom) to_add[ref] = parcel if to_add: self.writer.addFeatures(to_add.values()) log.debug(_("Added %d missing parcels"), len(to_add))
def get_groups_by_adjacent_buildings(self, buildings): """Get groups of parcel ids with buildings sharing walls in another parcel.""" exp = "NOT(localId ~ 'part')" bu_groups, __ = buildings.get_contacts_and_geometries(exp) bu_refs = {f.id(): ConsLayer.get_id(f) for f in buildings.search(exp)} geometries = {} pa_ids = {} pa_refs = {} pa_zone = {} for f in self.getFeatures(): geometries[f.id()] = QgsGeometry(f.geometry()) pa_ids[f["localId"]] = f.id() pa_refs[f.id()] = f["localId"] pa_zone[f.id()] = self.get_zone(f) adjs = defaultdict(list) for group in bu_groups: pids = set() for bid in group: ref = bu_refs[bid] pids.add(pa_ids[ref]) k = "-".join(set([pa_zone[fid] for fid in pids])) adjs[k].append(pids) mz_groups = {k for k in adjs.keys() if "-" in k} mz_groups |= {z for k in mz_groups for z in k.split("-")} pa_groups = merge_groups([adj for z in mz_groups for adj in adjs[z]]) for z, adj in adjs.items(): if z not in mz_groups: if len(adj) == 1: pa_groups.append(adj[0]) else: for group in merge_groups(adj): pa_groups.append(group) return pa_groups, pa_refs, geometries
def delete_void_parcels(self, *sources): """Remove parcels without buildings (or pools)/addresses.""" refs = [] for source in sources: if source is not None: for f in source.getFeatures(): refs.append(ConsLayer.get_id(f)) to_clean = [ f.id() for f in self.getFeatures() if f["localId"] not in refs ] if to_clean: self.writer.deleteFeatures(to_clean) log.debug(_("Removed %d void parcels"), len(to_clean))
class TestParcelLayer(unittest.TestCase): @mock.patch("catatom2osm.geo.layer.base.progressbar", mock.MagicMock()) @mock.patch("catatom2osm.geo.layer.base.log", m_log) def setUp(self): fn = "test/fixtures/parcel.gpkg|layername=parcel" self.parcel = ParcelLayer("38012") fixture = QgsVectorLayer(fn, "parcel", "ogr") self.assertTrue(fixture.isValid(), "Loading fixture") self.parcel.append(fixture) self.assertEqual(self.parcel.featureCount(), 186) fn = "test/fixtures/cons.gpkg|layername=cons" fixture2 = QgsVectorLayer(fn, "cons", "ogr") self.building = ConsLayer("MultiPolygon", "cons", "memory") self.building.append(fixture2) self.assertTrue(self.building.isValid(), "Loading fixture") def test_init(self): layer = ParcelLayer("38012") self.assertEqual(layer.fields()[0].name(), "localId") self.assertEqual(layer.fields()[1].name(), "parts") self.assertEqual(layer.rename["localId"], "inspireId_localId") def test_not_empty(self): layer = ParcelLayer("38012") self.assertGreater(len(layer.fields().toList()), 0) def test_delete_void_parcels(self): self.parcel.delete_void_parcels(self.building) self.assertEqual(self.parcel.featureCount(), 110) def test_create_missing_parcels(self): self.parcel.create_missing_parcels(self.building) self.assertEqual(self.parcel.featureCount(), 188) p = next(self.parcel.search("localId = '8642317CS5284S'")) self.assertEqual(len(Geometry.get_multipolygon(p)[0]), 1) def test_get_groups_by_adjacent_buildings(self): self.parcel.create_missing_parcels(self.building) pa_groups, pa_refs, __ = self.parcel.get_groups_by_adjacent_buildings( self.building) self.assertEqual(len(pa_groups), 21) self.assertEqual(sum([len(gr) for gr in pa_groups]), 85) @mock.patch("catatom2osm.geo.layer.base.progressbar", mock.MagicMock()) @mock.patch("catatom2osm.geo.layer.base.log", m_log) @mock.patch("catatom2osm.geo.layer.polygon.log", m_log) def test_merge_by_adjacent_buildings(self): self.building.remove_outside_parts() self.building.explode_multi_parts() self.building.clean() self.parcel.delete_void_parcels(self.building) self.parcel.create_missing_parcels(self.building) self.parcel.count_parts(self.building) pca = sum([f["parts"] for f in self.parcel.getFeatures()]) la = self.parcel.featureCount() tasks = self.parcel.merge_by_adjacent_buildings(self.building) pcd = sum([f["parts"] for f in self.parcel.getFeatures()]) ld = self.parcel.featureCount() cl = len([k for k, v in tasks.items() if k != v]) self.assertEqual(ld, la - cl) self.assertEqual(pca, pcd) pa_refs = [f["localId"] for f in self.parcel.getFeatures()] expected = [ "001000300CS52D", "001000400CS52D", "8641608CS5284S", "8641612CS5284S", "8641613CS5284S", "8641616CS5284S", "8641620CS5284S", "8641621CS5284S", "8641632CS5284S", "8641636CS5284S", "8641638CS5284S", "8641649CS5284S", "8641653CS5284S", "8641658CS5284S", "8641660CS5284S", "8642302CS5284S", "8642310CS5284S", "8642312CS5284S", "8642313CS5284S", "8642314CS5284S", "8642317CS5284S", "8642321CS5284S", "8642325CS5484N", "8642701CS5284S", "8742701CS5284S", "8742707CS5284S", "8742711CS5284S", "8742721CS5284S", "8839301CS5283N", "8840501CS5284S", "8841602CS5284S", "8841603CS5284S", "8844121CS5284S", "8940301CS5284S", "8940302CS5284S", "8940305CS5284S", "8940306CS5284S", "8940307CS5284S", "8940309CS5284S", "8941505CS5284S", "9041703CS5294S", "9041704CS5294S", "9041705CS5294S", "9041716CS5294S", "9041719CS5294S", "9042401CS5294S", "9042402CS5294S", "9042404CS5294S", ] self.assertEqual(pa_refs, expected) f = next(self.parcel.search("localId = '8840501CS5284S'")) self.assertEqual(f["parts"], 11) merged = [] for bu in self.building.getFeatures(): if self.building.is_building(bu): ref = self.building.get_id(bu) if ref not in pa_refs: merged.append(ref) self.assertEqual(len(merged), 71) self.assertTrue(all([tasks[ref] != ref for ref in merged])) @mock.patch("catatom2osm.geo.layer.base.progressbar", mock.MagicMock()) @mock.patch("catatom2osm.geo.layer.base.log", m_log) @mock.patch("catatom2osm.geo.layer.polygon.log", m_log) def test_count_parts(self): self.building.remove_outside_parts() self.building.explode_multi_parts() self.building.clean() self.parcel.delete_void_parcels(self.building) self.parcel.create_missing_parcels(self.building) parts_count = self.parcel.count_parts(self.building) self.assertEqual(sum(parts_count.values()), 324) self.assertEqual(len(parts_count), self.parcel.featureCount()) f = next(self.parcel.search("localId = '8840501CS5284S'")) self.assertEqual(f["parts"], 7) self.assertEqual(parts_count["8840501CS5284S"], 7) f = next(self.parcel.search("localId = '8840502CS5284S'")) self.assertEqual(f["parts"], 4) self.assertEqual(parts_count["8840502CS5284S"], 4) @mock.patch("catatom2osm.geo.layer.base.progressbar", mock.MagicMock()) @mock.patch("catatom2osm.geo.layer.base.log", m_log) @mock.patch("catatom2osm.geo.layer.polygon.log", m_log) def test_get_groups_by_parts_count(self): self.building.remove_outside_parts() self.building.explode_multi_parts() self.building.clean() self.parcel.delete_void_parcels(self.building) self.parcel.create_missing_parcels(self.building) self.parcel.count_parts(self.building) self.parcel.merge_by_adjacent_buildings(self.building) features = {pa.id(): pa for pa in self.parcel.getFeatures()} ( pa_groups, pa_refs, geometries, parts_count, ) = self.parcel.get_groups_by_parts_count(10, 100) self.assertEqual(len(parts_count), 48) self.assertEqual(len(pa_groups), 18) self.assertTrue( all([ sum([parts_count[pa_refs[fid]] for fid in group]) <= 10 for group in pa_groups ])) label_count = set([ len(set([self.parcel.get_zone(features[fid]) for fid in group])) for group in pa_groups ]) self.assertEqual(label_count, {1}) @mock.patch("catatom2osm.geo.layer.base.progressbar", mock.MagicMock()) @mock.patch("catatom2osm.geo.layer.base.log", m_log) @mock.patch("catatom2osm.geo.layer.polygon.log", m_log) def test_merge_by_parts_count(self): self.building.remove_outside_parts() self.building.explode_multi_parts() self.building.clean() self.parcel.delete_void_parcels(self.building) self.parcel.create_missing_parcels(self.building) self.parcel.merge_by_adjacent_buildings(self.building) pca = sum([f["parts"] for f in self.parcel.getFeatures()]) la = self.parcel.featureCount() tasks = self.parcel.merge_by_parts_count(20, 30) pcd = sum([f["parts"] for f in self.parcel.getFeatures()]) ld = self.parcel.featureCount() cl = len([k for k, v in tasks.items() if k != v]) self.assertEqual(ld, la - cl) self.assertEqual(pca, pcd)