18 votos

Dividir un polígono en polígonos de igual área utilizando QGIS

Hay un Plugin en QGIS 2.18 llamado "Divisor de Polígonos" que divide un polígono irregular en polígonos más pequeños de igual área de un tamaño especificado/personalizado pero en la versión QGIS 3.x no hay Plugin "Divisor de Polígonos" o herramienta para realizar esa tarea.

¿Cómo divido/parto el polígono irregular en polígonos de igual área/partes en QGIS 3.6?

Como se muestra en la siguiente imagen:

example

25voto

V Shreyas Puntos 84

Todas las herramientas necesarias se pueden encontrar en la caja de herramientas de procesamiento y hay que ser un poco innovador para conseguirlo. Básicamente adoptando este método se pueden lograr resultados bastante buenos, aunque no le da ángulos de 90 grados en todos los lugares, pero aproximadamente igual área:

1.Crear suficiente puntos aleatorios dentro de un polígono. Hice 10 000 10 000 random points inside a polygon

  1. Ejecute la agrupación k-means para los puntos aleatorios. Utiliza tantos clusters como áreas quieras:

Clustered points colored based on cluster_id

  1. Ejecute Vector Geometry --> Aggregate y utilice el id del cluster como campo de agregación. De esta forma obtendrás una capa multipunto a partir de los clusters.

  2. Ejecutar Geometría vectorial --> centroides de los puntos agregados Centroids for each new area

  3. Ejecutar polígonos de Voronoi para los centroides. Utilice suficiente memoria intermedia para cubrir todo el polígono original. Para mí esto era alrededor del 40% Split layer

  4. Crear líneas a partir de los polígonos voronoi (Geometría vectorial --> polígonos a líneas). Actualmente creo que sólo se puede dividir una capa de polígonos con líneas y no con otra capa de polígonos (no 100% seguro de esto, pero esto funciona con seguridad). Line layer used for splitting

  5. Finalmente ejecuta Dividir con líneas y utilice su capa de polígonos original como entrada y las líneas voronoi como capa de división:

Final result!

5voto

ARUNBALAN NV Puntos 101

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 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

test_input

División en 2 partes con

my_class = LayerIntoParts('test_area_4', 2)
my_class.dissolve_result()

test_split_2

División en 3 partes con

my_class = LayerIntoParts('test_area_4', 3)
my_class.dissolve_result()

test_split_3


Notas: ( TODO )

  • Parámetros 'HSPACING' y 'VSPACING' deben definirse de antemano
  • No funciona con multipolígonos

Referencias:

4voto

ARUNBALAN NV Puntos 101

Desde el 24 de septiembre de 2020 el "Divisor de polígonos" El plugin vuelve a ser compatible con las versiones de QGIS 3.x.

example

P.D. También se mencionó como un comentario

1voto

Michal Ferov Puntos 296

Recientemente he actualizado "parcialmente" el Plugin de división de polígonos a QGIS 3, creado por Uroc Ilic. Ya está disponible en el repositorio de QGIS.

Pero sólo funciona para polígonos convexos, es decir, poco irregulares.

0voto

Cyril Puntos 141

Este es un enfoque que podría probarse:

1) Hasta ahora, lo más probable es que sea imposible separar con precisión un polígono complejo mediante líneas horizontales y verticales.

2) El problema radica en la combinación de líneas rectas y curvas.

3) Sólo es posible separar con precisión un polígono complejo con líneas curvas.

4) Sí, con pequeños errores es posible dividir el polígono por líneas horizontales y verticales y se acumularán en el segundo (tercer, etc.) nivel, es decir, el 1er nivel me permitirá dividir con precisión el polígono curvilíneo en 2 partes exactamente iguales, y a partir del segundo nivel, cuando tengamos lados con líneas rectas y curvas, el geoinstrumento empezará a acumular errores...

"...hasta que la carretera esté desbordada" :-).

5) En las siguientes figuras se muestra un ejemplo del estudio

enter image description here

y

enter image description here 6) Quizá algún día reconsidere mi punto de vista, pero eso es lo que me ha venido a la cabeza ahora :-)...

El siguiente paso que hay que pensar es cómo hallar el valor medio del polígono y, a continuación, llevar el área de las subparcelas al valor medio del polígono.

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X