2

I have been trying to aggregate points in QGIS for couple of weeks now. I am developing a small plugin that should allow simple Hexabinning using Uber's h3 plugin. Upon clicking the hexabinning button, the following output should be shown. The output is achieved using the qgis-h3-toolkit-plugin: https://github.com/arongergely/qgis-h3-toolkit-plugin

enter image description here

However, I have found it very hard to integrate code from this plugin. I have been instantiating all the three classes and calling their functions, but there are so many different parameters. See the three classes and functions here https://github.com/arongergely/qgis-h3-toolkit-plugin/blob/main/h3_toolkit/processing/algorithms.py

In my plugin, I have a slider which takes the hexabinning resolution input from the user. Then upon clicking the 'Bin' button, it should call a script/function and the above output should be shown. I am trying to write is a simple script that shows the density of points in hexagons.

I have run the following script: it displays the hexagons, but does not show density (from https://github.com/maphew/mhwcode/blob/1ef8338e20ff24ddbe741af225e556b9d81ec416/gis/qgis/h3-grid-from-layer.py)

# START BINNING    
def binning_function(self):
    # self.hextest = HexTest()
    # ed = self.hextest.begin()
    # er = self.hextest.run()        
    print('binning function')
self.debug = False

###---------- Edit these variables ----------
# Min & max h3 resolution levels, from 0 to 15 (global to sub-meter)
# High resolutions over broad areas can be slow and consume a lot of storage space
# https://h3geo.org/docs/core-library/restable
# Resolution 7 is ~2,000m across, 9 is ~320m across, 11 is ~45m (in YT Albers)
self.min_resolution = 0
self.max_resolution = 9

# Output files are {prefix}_{resolution}: Hex_3, Hex_4, ...
self.out_name_prefix = "Hex"

self.geographic_coordsys = "EPSG:4617"  # e.g. WGS84, NAD83(CSRS)
self.output_projection = "EPSG:3579"  # placeholder, not currently used
# --------------------------------------------

projectPath = os.path.dirname(QgsProject.instance().fileName())
self.geo_csrs = QgsCoordinateReferenceSystem(self.geographic_coordsys)
self.out_csrs = QgsCoordinateReferenceSystem(self.output_projection)

dataPath = os.path.join(projectPath, "data/")
if not os.path.exists(dataPath):
    os.mkdir(dataPath)

#instead of chooser, just use active layer, and selected features within that layer
self.mylayer = iface.activeLayer()
if self.mylayer.selectedFeatures():
    params = {'INPUT':self.mylayer, 'OUTPUT':'memory:sel'}
    self.mylayer = processing.run("qgis:saveselectedfeatures", params)["OUTPUT"]
    if self.debug:
        QgsProject.instance().addMapLayer(self.mylayer)

self.run()

def log(self, item): return QgsMessageLog.logMessage(str(item))

def proj_to_geo(self, in_layer): """Project to geographic coordinate system, in memory. H3 needs all coordinates in decimal degrees""" params = { "INPUT": self.mylayer, "TARGET_CRS": self.geographic_coordsys, "OUTPUT": "memory:dd_", } geo_lyr = processing.run("native:reprojectlayer", params)["OUTPUT"] if self.debug: QgsProject.instance().addMapLayer(geo_lyr) return geo_lyr

def poly_from_extent(self, layer): """Return polygon as coordinate list from layer's extent Ex: [(-142.0, 74.0), (-115.0, 74.0), (-115.0, 54.0), (-142.0, 54.0)]

Adapted from
https://gis.stackexchange.com/questions/245811/getting-layer-extent-in-pyqgis
"""
ext = layer.extent()
xmin = ext.xMinimum()
xmax = ext.xMaximum()
ymin = ext.yMinimum()
ymax = ext.yMaximum()
return [(xmin, ymax), (xmax, ymax), (xmax, ymin), (xmin, ymin)]


def hexes_within_layer_extent(self, layer, level): """Return list of HexID within layer's extent In: qgis layer object, hex resolution level (0-15) Out: ['8412023ffffffff', '84029d5ffffffff', '8413a93ffffffff'] """ ext_poly = self.poly_from_extent(layer) hex_ids = set(h3.polyfill_polygon(ext_poly, res=level, lnglat_order=True)) self.log(f"Hex IDs within extent poly: {str(len(hex_ids))}") return hex_ids

def run(self): geo_layer = self.proj_to_geo(self.mylayer)

# For each resolution level fetch geometry of each hex feature and write to shapefile with id
for res in range(self.min_resolution, self.max_resolution + 1):
    self.log("Resolution: {res}")
    fields = QgsFields()
    fields.append(QgsField("id", QVariant.String))
    shpfile = os.path.join(self.dataPath, f"{self.out_name_prefix}_{res}.shp")
    writer = QgsVectorFileWriter(
        shpfile, "UTF8", fields, QgsWkbTypes.Polygon, driverName="ESRI Shapefile"
    )
    features = []
    for id in set(self.hexes_within_layer_extent(geo_layer, res)):
        f = QgsFeature()
        f.setGeometry(
            QgsGeometry.fromPolygonXY(
                [
                    # note reversing back to X,Y
                    [QgsPointXY(c[1], c[0]) for c in h3.h3_to_geo_boundary(id)]
                ]
            )
        )
        f.setAttributes([id])
        if self.debug:
            self.log(f"Hex: {id} " + str(h3.h3_to_geo_boundary(id)))
        features.append(f)
    writer.addFeatures(features)
    del writer
    self.log("Features out: " + str(len(features)))

    processing.run("qgis:definecurrentprojection", {"INPUT": shpfile, "CRS": self.geo_csrs})

    layer = QgsVectorLayer(shpfile, f"{self.out_name_prefix} {res}", "ogr")
    QgsProject.instance().addMapLayer(layer)

END BINNING

When the above binning_function() procedure is called from my plugin, the following is output. Only the hexabins are shown. I would like to see the counts for each hexabin, and the output should be the same as the first output I have posted.

The count function There is count performed in lines 54-62 of this script: https://github.com/ThomasG77/30DayMapChallenge/blob/master/day4_hexagons/data/h3-processing.py

There is also count performed starting from line 497 of the H3-toolkit here https://github.com/arongergely/qgis-h3-toolkit-plugin/blob/main/h3_toolkit/processing/algorithms.py (see lines 666-715)

Also, I just want one layer created as output - not the 0-9 layers. The output should be based on hexabinning value chosen by the user through a QT horizontal slider.

enter image description here

My dataset: https://mapsafe.xyz/all_clusters_kamloops.zip

Related Post: Using Uber H3 hexagon grids in QGIS

PolyGeo
  • 65,136
  • 29
  • 109
  • 338
sharmapn
  • 61
  • 4

0 Answers0