TransWikia.com

Creating nested grid in QGIS

Geographic Information Systems Asked on June 29, 2021

How can I create something like this as a layer in QGIS?

I currently have one large Grid set with "ID"s, and I’m wanting to create nested layers of 5 x 5 grids 5 levels down. (so for example grid A is 62500m x 62500m, and within grid A I want 25 tiles of 12500m x 1500m, and in each of those I want 25 tiles of 500m x 500m, and in each of those I want 25 tiles of 100m x 100m) with an "ID" specific to the tile it’s embedded within (ex.: 2_3_4_5_A)

enter image description here

One Answer

This answer was given to the original question prior the edit:

Here is a graphical model you can use:

enter image description here

So basically, you first need to create your parent grid as rectangles and order them with an SQL statement, that allows to order by X asc and Y desc: select *, ROW_NUMBER() OVER(ORDER BY ST_MinY(geometry) desc, ST_MinX(geometry) asc) as id FROM input1 (see @JGH's answer here: https://gis.stackexchange.com/a/317621/107424). Now assign the new feature ids as ids ("id" = $id). Then densify these rectangles (expression here @Subgrids - 1) and extract the vertices. From these vertices, which contain an angle field, you can now create orthogonal lines by using this expression:

CASE
 WHEN "angle" in (90)
  THEN make_line($geometry,make_point($x,$y-@VerticalSpacing))
 WHEN "angle" in (0)
  THEN make_line($geometry,make_point($x+@HorizontalSpacing,$y))
END

The lines will exactly fill one parent grid. You can use these to divide your parent grid into a child grid. Then you need to order the child-grid-cells similar as before: select *, ROW_NUMBER() OVER(ORDER BY ST_MinY(geometry) desc, ST_MinX(geometry) asc) as id FROM input1.

Finally add the child-grid-id by using this expression: to_string(id) || '_' || to_string(array_find(array_agg("id"||'_'||-$id,"id"),"id"||'_'||-$id)+1) as requested at Increment value based on grouped field. Done.

enter image description here

If you want to use Letters instead of Numbers, you need to implement a letter-lookup such as array_get(string_to_array('A,B,C,D,E,F'),$id+1) or as I explained here: https://gis.stackexchange.com/a/401200/107424

The model works with metric and degree units. Tested in QGIS 3.16.1. Proof:

enter image description here


For reference, here the model exported as Python script:

"""
Model exported as python.
Name : NestedGrid
Group : GISSE
With QGIS : 31601
"""

from qgis.core import QgsProcessing
from qgis.core import QgsProcessingAlgorithm
from qgis.core import QgsProcessingMultiStepFeedback
from qgis.core import QgsProcessingParameterCrs
from qgis.core import QgsProcessingParameterExtent
from qgis.core import QgsProcessingParameterNumber
from qgis.core import QgsProcessingParameterFeatureSink
from qgis.core import QgsExpression
import processing


class Nestedgrid(QgsProcessingAlgorithm):

    def initAlgorithm(self, config=None):
        self.addParameter(QgsProcessingParameterCrs('CRS', 'CRS', defaultValue='EPSG:3857'))
        self.addParameter(QgsProcessingParameterExtent('Extent', 'Extent', defaultValue=None))
        self.addParameter(QgsProcessingParameterNumber('HorizontalSpacing', 'Horizontal Spacing of Parent Grid', type=QgsProcessingParameterNumber.Double, minValue=0, defaultValue=10000))
        self.addParameter(QgsProcessingParameterNumber('VerticalSpacing', 'Vertical Spacing of Parent Grid', type=QgsProcessingParameterNumber.Double, minValue=0, defaultValue=10000))
        self.addParameter(QgsProcessingParameterNumber('Subgrids', 'Subgrids (x*x)', type=QgsProcessingParameterNumber.Integer, minValue=0, defaultValue=3))
        self.addParameter(QgsProcessingParameterFeatureSink('Parent_grid', 'Parent_Grid', type=QgsProcessing.TypeVectorAnyGeometry, createByDefault=True, supportsAppend=True, defaultValue=None))
        self.addParameter(QgsProcessingParameterFeatureSink('Child_grid', 'Child_Grid', type=QgsProcessing.TypeVectorAnyGeometry, createByDefault=True, supportsAppend=True, defaultValue=None))

    def processAlgorithm(self, parameters, context, model_feedback):
        # Use a multi-step feedback, so that individual child algorithm progress reports are adjusted for the
        # overall progress through the model
        feedback = QgsProcessingMultiStepFeedback(9, model_feedback)
        results = {}
        outputs = {}

        # Create grid
        # Create the original parent grid
        alg_params = {
            'CRS': parameters['CRS'],
            'EXTENT': parameters['Extent'],
            'HOVERLAY': 0,
            'HSPACING': parameters['HorizontalSpacing'],
            'TYPE': 2,
            'VOVERLAY': 0,
            'VSPACING': parameters['VerticalSpacing'],
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['CreateGrid'] = processing.run('native:creategrid', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

        feedback.setCurrentStep(1)
        if feedback.isCanceled():
            return {}

        # Execute SQL
        # Order the Grid 1) from West to East and 2) from North to South
        alg_params = {
            'INPUT_DATASOURCES': outputs['CreateGrid']['OUTPUT'],
            'INPUT_GEOMETRY_CRS': None,
            'INPUT_GEOMETRY_FIELD': '',
            'INPUT_GEOMETRY_TYPE': None,
            'INPUT_QUERY': 'select *, ROW_NUMBER() OVER(ORDER BY  ST_MinY(geometry) desc, ST_MinX(geometry) asc) as idnFROM input1',
            'INPUT_UID_FIELD': '',
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['ExecuteSql'] = processing.run('qgis:executesql', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

        feedback.setCurrentStep(2)
        if feedback.isCanceled():
            return {}

        # Refactor fields
        # Create the id's regarding the new sorting of the features
        alg_params = {
            'FIELDS_MAPPING': [{'expression': '$id','length': 42,'name': 'id','precision': 0,'type': 10},{'expression': '$id','length': 20,'name': 'parentid','precision': 0,'type': 4}],
            'INPUT': outputs['ExecuteSql']['OUTPUT'],
            'OUTPUT': parameters['Parent_grid']
        }
        outputs['RefactorFields'] = processing.run('native:refactorfields', alg_params, context=context, feedback=feedback, is_child_algorithm=True)
        results['Parent_grid'] = outputs['RefactorFields']['OUTPUT']

        feedback.setCurrentStep(3)
        if feedback.isCanceled():
            return {}

        # Densify by count
        # Densify the grid: add extra vertices that will be used as origin for the orthogonal lines used for splitting the grid
        alg_params = {
            'INPUT': outputs['RefactorFields']['OUTPUT'],
            'VERTICES': QgsExpression(' @Subgrids - 1').evaluate(),
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['DensifyByCount'] = processing.run('native:densifygeometries', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

        feedback.setCurrentStep(4)
        if feedback.isCanceled():
            return {}

        # Extract vertices
        # Extract the (extra) vertices. They will have an angle field, we can use to identify the needed vertices for the split
        alg_params = {
            'INPUT': outputs['DensifyByCount']['OUTPUT'],
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['ExtractVertices'] = processing.run('native:extractvertices', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

        feedback.setCurrentStep(5)
        if feedback.isCanceled():
            return {}

        # Geometry by expression
        # Create orthogonal lines at the desified vertices of the parent grid.
        # The lines will exactly fill one parent grid and can be used to divide it into the child grid at the next step.
        alg_params = {
            'EXPRESSION': 'CASErn WHEN "angle" in (90) --270rn  THEN make_line($geometry,make_point($x,$y-@VerticalSpacing ))rn WHEN "angle" in (0) -- 180rn  THEN make_line($geometry,make_point($x+@HorizontalSpacing,$y))rnEND',
            'INPUT': outputs['ExtractVertices']['OUTPUT'],
            'OUTPUT_GEOMETRY': 1,
            'WITH_M': False,
            'WITH_Z': False,
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['GeometryByExpression'] = processing.run('native:geometrybyexpression', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

        feedback.setCurrentStep(6)
        if feedback.isCanceled():
            return {}

        # Split with lines
        # Cut the Parent Grid into the Child Grid
        alg_params = {
            'INPUT': outputs['RefactorFields']['OUTPUT'],
            'LINES': outputs['GeometryByExpression']['OUTPUT'],
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['SplitWithLines'] = processing.run('native:splitwithlines', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

        feedback.setCurrentStep(7)
        if feedback.isCanceled():
            return {}

        # Execute SQL
        # Order the Grid 1) from West to East and 2) from North to South, see: https://gis.stackexchange.com/a/317621/107424
        alg_params = {
            'INPUT_DATASOURCES': outputs['SplitWithLines']['OUTPUT'],
            'INPUT_GEOMETRY_CRS': None,
            'INPUT_GEOMETRY_FIELD': '',
            'INPUT_GEOMETRY_TYPE': None,
            'INPUT_QUERY': 'select *, ROW_NUMBER() OVER(ORDER BY  ST_MinY(geometry) desc, ST_MinX(geometry) asc) as idnFROM input1',
            'INPUT_UID_FIELD': '',
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['ExecuteSql'] = processing.run('qgis:executesql', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

        feedback.setCurrentStep(8)
        if feedback.isCanceled():
            return {}

        # Refactor fields
        # Create the Child IDs, see: https://gis.stackexchange.com/a/376725/107424
        alg_params = {
            'FIELDS_MAPPING': [{'expression': 'to_string(id) || '_' || to_string(array_find(array_agg("id"||'_'||-$id,"id"),"id"||'_'||-$id)+1)','length': 42,'name': 'id','precision': 0,'type': 10},{'expression': 'id','length': 20,'name': 'parentid','precision': 0,'type': 4},{'expression': 'array_find(array_agg("id"||'_'||-$id,"id"),"id"||'_'||-$id)+1','length': 20,'name': 'childid','precision': 0,'type': 4}],
            'INPUT': outputs['ExecuteSql']['OUTPUT'],
            'OUTPUT': parameters['Child_grid']
        }
        outputs['RefactorFields'] = processing.run('native:refactorfields', alg_params, context=context, feedback=feedback, is_child_algorithm=True)
        results['Child_grid'] = outputs['RefactorFields']['OUTPUT']
        return results

    def name(self):
        return 'NestedGrid'

    def displayName(self):
        return 'NestedGrid'

    def group(self):
        return 'GISSE'

    def groupId(self):
        return 'GISSE'

    def createInstance(self):
        return Nestedgrid()

Answered by MrXsquared on June 29, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP