En este intento, he intentado llevar a cabo la idea de @Christophe sugerida en los comentarios. Por favor, no sea duro, es definitivamente torcido y frágil, sin embargo, es de alguna manera de trabajo. El script no incluye la parte de depuración.
Proceda con Plugins > Python Console > Show Editor
y pegue el script siguiente
# defining required imports
import sys
# defining a class
class LayerIntoParts:
# initiating layer's name to split and number of parts provided from user
def __init__(self, layer_name, segments):
self.layer_name = layer_name
self.segments = segments
# setting up the input layer and checking it's type
def layer_geometry_check(self):
# loading layer
layer = QgsProject.instance().mapLayersByName(self.layer_name)[0]
if not layer.isValid():
raise ValueError("Layer failed to load!")
sys.exit
# checking geometry type, will work only with polygons and multipolygons
if layer.wkbType() == QgsWkbTypes.Polygon:
layer_valid = layer
# converting multipart into single part
elif layer.wkbType() == QgsWkbTypes.MultiPolygon:
layer_valid = processing.run('native:multiparttosingleparts',
{'INPUT': layer,
'OUTPUT': "memory:"}
)["OUTPUT"]
# giving an error for not polygons and multipolygons
else:
raise ValueError("This geometry type is not supported!\Only Polygons/Multipolygons are allowed.")
sys.exit
# returning the validated layer
return layer_valid
# defining required parameters
def layer_and_parameters(self):
# inheriting the output from 'layer_geometry_check' method
layer = self.layer_geometry_check()
# getting the layer's area
area_total = [f.geometry().area() for f in layer.getFeatures()]
area_total = area_total[0]
# getting the layer's extent and adjusting it for processing functions
layer_extent = layer.extent()
layer_extent_string = "{},{},{},{}".format(layer_extent.xMinimum(), layer_extent.xMaximum(), layer_extent.yMinimum(), layer_extent.yMaximum())
# building a dictionary to store the output parameters
param = {
'layer': layer,
'layer_crs': layer.crs(),
'layer_extent': layer_extent_string,
'layer_area_total': area_total,
'area_segment': area_total / self.segments
}
# returning the dictionary with output parameters
return param
# creating grid for the input layer
def layer_2_grid(self):
# inheriting the output from 'layer_and_parameters' method
param = self.layer_and_parameters()
# creating grid for the layer with 'qgis:creategrid' algorithm
layer_grid = processing.run('qgis:creategrid',
{'TYPE': 2,
'CRS': param['layer_crs'],
'EXTENT': param['layer_extent'],
'HSPACING': 10000,
'VSPACING': 10000,
'HOVERLAY': 0,
'VOVERLAY': 0,
'OUTPUT': "memory:"}
)["OUTPUT"]
# deleting redundant attributes (appear by default when using the algorithm to create a grid)
layer_grid_clean = processing.run('qgis:deletecolumn',
{'INPUT': layer_grid,
'COLUMN': ['left', 'right', 'top', 'bottom'],
'OUTPUT': "memory:"}
)["OUTPUT"]
# returning layer as a grid
return layer_grid_clean
# clipping the grid with the input layer
def clip_grid_and_layer(self):
# inheriting the output from 'layer_2_grid' method
grid = self.layer_2_grid()
# inheriting the output from 'layer_and_parameters' method
layer = self.layer_and_parameters()['layer']
# clipping the grid with the layer by means of 'qgis:clip' algorithm
layer_clip = processing.run('qgis:clip',
{'INPUT': grid,
'OVERLAY': layer,
'OUTPUT': "memory:"}
)["OUTPUT"]
# returning the clipped grid
return layer_clip
# categorizing the clipped grid layer
def categorize(self):
# inheriting the output from 'clip_grid_and_layer' method
layer_clip = self.clip_grid_and_layer()
# inheriting the output from 'layer_and_parameters' method
param = self.layer_and_parameters()
# transforming clipped grid layer's elements into a list with
# id, calculated area, and geometry
elements = []
for f in layer_clip.getFeatures():
case = {"f_id": f.id(), "area": f.geometry().area(), "geom": f.geometry()}
elements.append(case)
# lists for used cells' ids
used_ids_i = []
used_ids_j = []
# variable for categories
n = 0
# looping through all cells
for i in elements:
area_calc = i['area']
used_ids_i.append(i['f_id'])
for j in elements:
# conditions: calculated area less than a "golden" area of a segment, no duplicates of cells, and sufficient intersection of edges between cells
if area_calc <= param['area_segment'] and j['f_id'] not in used_ids_i and i['f_id'] not in used_ids_j and (i['geom'].intersection(i['geom'])).length() > 0:
used_ids_j.append(j['f_id'])
area_calc += j['area']
n += 1
# signing a category to each cell
i['cat'] = 'cat_' + str(n)
i['area_calc'] = area_calc
# returning categorized cells as list
return elements
# dissolving categorized cells and outputting the result
def dissolve_result(self):
# inheriting the output from 'layer_and_parameters' method
param = self.layer_and_parameters()
# inheriting the output from 'categorize' method
elements_raw = self.categorize()
# creating an empty later for output
vl = QgsVectorLayer("Polygon?crs={}".format(param['layer_crs'].authid()), "output", "memory")
# accessing the empty layer
pr = vl.dataProvider()
# embedding new field values
pr.addAttributes([QgsField("id", QVariant.Int),
QgsField("cat", QVariant.String),
QgsField("area_calc", QVariant.Double),
QgsField("area_gold", QVariant.Double)
])
# updating the empty layer
vl.updateFields()
# looping through a list of categorized cells and inserting them into the empty layer
for elem in elements_raw:
feat = QgsFeature()
feat.setGeometry(elem['geom'])
feat.setAttributes([elem['f_id'], elem['cat'], round(elem['area_calc'], 8), round(param['area_segment'], 8)])
pr.addFeature(feat)
vl.updateExtents()
# dissolving by category by means of 'qgis:dissolve' algorithm
layer_dissolved = processing.run('qgis:dissolve',
{'FIELD': ['cat'],
'INPUT': vl,
'OUTPUT': 'memory:'}
)["OUTPUT"]
# deleting 'cat' attribute using 'qgis:deletecolumn' algorithm
layer_dissolved_clean = processing.run('qgis:deletecolumn',
{'INPUT': layer_dissolved,
'COLUMN': ['cat'],
'OUTPUT': "memory:"}
)["OUTPUT"]
# updating 'id' attribute
with edit(layer_dissolved_clean):
for feature in layer_dissolved_clean.getFeatures():
feature.setAttribute(feature.fieldNameIndex('id'), feature.id())
layer_dissolved_clean.updateFeature(feature)
# returning function output
return QgsProject.instance().addMapLayer(layer_dissolved_clean)
my_class = LayerIntoParts('test_area_4', 2)
my_class.dissolve_result()
Ajuste el my_class = LayerIntoParts('name of your layer', num_of_parts)
Pulse Run script
y obtener la salida
Pruebas. Estoy utilizando un polígono de Córcega que fue tomada de Natural Earth | Admin 0 - Países
División en 2 partes con
my_class = LayerIntoParts('test_area_4', 2)
my_class.dissolve_result()
División en 3 partes con
my_class = LayerIntoParts('test_area_4', 3)
my_class.dissolve_result()
Notas: ( TODO )
- Parámetros
'HSPACING'
y 'VSPACING'
deben definirse de antemano
- No funciona con multipolígonos
Referencias: