Skip to content

Commit

Permalink
Merge pull request #787 from kartoza/fiji
Browse files Browse the repository at this point in the history
Fiji
  • Loading branch information
timlinux authored Jan 24, 2025
2 parents 38d12a6 + 69d4140 commit 4de5f6e
Show file tree
Hide file tree
Showing 29 changed files with 2,037 additions and 1,538 deletions.
2 changes: 1 addition & 1 deletion config.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"author": "Kartoza",
"email": "[email protected]",
"description": "Gender Enabling Environments Spatial Tool",
"version": "0.5.0",
"version": "0.5.1",
"changelog": "",
"server": false
}
Expand Down
19 changes: 18 additions & 1 deletion geest/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ def initGui(self): # pylint: disable=missing-function-docstring
self.run_action = QAction(icon, "GEEST Settings", self.iface.mainWindow())
self.run_action.triggered.connect(self.run)
self.iface.addToolBarIcon(self.run_action)

self.debug_running = False
# Create the dock widget
self.dock_widget = GeestDock(
parent=self.iface.mainWindow(),
Expand Down Expand Up @@ -247,6 +247,8 @@ def unload(self): # pylint: disable=missing-function-docstring
Unload the plugin from QGIS.
Removes all added actions, widgets, and options to ensure a clean unload.
"""

self.kill_debug()
# Save geometry before unloading
self.save_geometry()

Expand Down Expand Up @@ -286,11 +288,24 @@ def unload(self): # pylint: disable=missing-function-docstring
self.dock_widget.deleteLater()
self.dock_widget = None

def kill_debug(self):
# Note that even though this kills the debugpy process I still
# cannot successfully restart the debugger in vscode without restarting QGIS
if self.debug_running:
import psutil
import signal

"""Find the PID of the process listening on the specified port."""
for conn in psutil.net_connections(kind="tcp"):
if conn.laddr.port == 9000 and conn.status == psutil.CONN_LISTEN:
os.kill(conn.pid, signal.SIGTERM)

def debug(self):
"""
Enters debug mode.
Shows a message to attach a debugger to the process.
"""
self.kill_debug()
self.display_information_message_box(
title="GEEST",
message="Close this dialog then open VSCode and start your debug client.",
Expand All @@ -306,6 +321,8 @@ def debug(self):
title="GEEST",
message="Visual Studio Code debugger is now attached on port 9000",
)
self.debug_action.setEnabled(False) # prevent user starting it twice
self.debug_running = True

def run(self):
"""
Expand Down
7 changes: 6 additions & 1 deletion geest/core/algorithms/area_iterator.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,12 @@ def __iter__(self) -> Iterator[Tuple[QgsGeometry, QgsGeometry, float]]:
return

# Iterate over each polygon feature and calculate progress
for index, polygon_feature in enumerate(self.polygon_layer.getFeatures()):

# Sort polygon features by area in ascending order
sorted_features = sorted(
self.polygon_layer.getFeatures(), key=lambda f: f.geometry().area()
)
for index, polygon_feature in enumerate(sorted_features):
polygon_id: int = polygon_feature.id()
# Request the corresponding bbox feature based on the polygon's ID
feature_request: QgsFeatureRequest = QgsFeatureRequest().setFilterFid(
Expand Down
1 change: 1 addition & 0 deletions geest/core/tasks/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
from .study_area import StudyAreaProcessingTask
from .ors_checker import OrsCheckerTask
from .grid_from_bbox import GridFromBbox
109 changes: 109 additions & 0 deletions geest/core/tasks/grid_from_bbox.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import time
from osgeo import ogr
from qgis.core import QgsTask
from geest.utilities import log_message


class GridFromBbox(QgsTask):
"""
A QGIS task to generate grid cells in a bounding box chunk, check intersections,
and store them in memory for later writing.
"""

def __init__(self, chunk_id, bbox_chunk, geom, cell_size, feedback):
super().__init__(f"CreateGridChunkTask-{chunk_id}", QgsTask.CanCancel)
self.chunk_id = chunk_id
self.bbox_chunk = bbox_chunk # (x_start, x_end, y_start, y_end)
self.geom = geom
self.cell_size = cell_size
self.feedback = feedback
self.run_time = 0.0
self.features_out = [] # store geometries here

def run(self):
log_message(f"##################################")
log_message(f"Processing chunk {self.chunk_id}...")
log_message(f"Chunk bbox: {self.bbox_chunk}")
log_message(f"##################################")
start_time = time.time()
x_start, x_end, y_start, y_end = self.bbox_chunk

# Convert geom to OGR if needed
# If self.geom is an ogr.Geometry, skip this step
# If it's a PyQGIS geometry, convert to WKB or so, e.g.:
# ogr_geom = ogr.CreateGeometryFromWkb(self.geom.asWkb())

# We'll assume self.geom is already an ogr.Geometry
skip_intersection_check = False

chunk_bounds = ogr.CreateGeometryFromWkt(
f"POLYGON(({x_start} {y_start}, {x_end} {y_start}, "
f"{x_end} {y_end}, {x_start} {y_end}, {x_start} {y_start}))"
)

# If chunk bounding box is fully outside of the geometry, skip chunk
if not self.geom.Intersects(chunk_bounds):
log_message(
f"Chunk {self.chunk_id} is completely outside geometry, skipping."
)
return True

# If the whole chunk is within the geometry, skip intersection check
if self.geom.Contains(chunk_bounds):
log_message(
f"Whole chunk is within the geometry, we will skip check intersection for each feature..."
)
skip_intersection_check = True
else:
log_message(
f"Whole chunk is NOT within the geometry, we will check intersection for each feature..."
)

x = x_start
while x < x_end:
x2 = x + self.cell_size
if x2 <= x:
break
y = y_start
while y < y_end:
y2 = y + self.cell_size
if y2 <= y:
break
# Create cell polygon in memory
ring = ogr.Geometry(ogr.wkbLinearRing)
ring.AddPoint(x, y)
ring.AddPoint(x, y2)
ring.AddPoint(x2, y2)
ring.AddPoint(x2, y)
ring.AddPoint(x, y)

cell_polygon = ogr.Geometry(ogr.wkbPolygon)
cell_polygon.AddGeometry(ring)

# Check intersection
if not skip_intersection_check:
if self.geom.Intersects(cell_polygon):
self.features_out.append(cell_polygon)
else:
self.features_out.append(cell_polygon)

y = y2
x = x2

end_time = time.time()
self.run_time = end_time - start_time
# self.feedback.pushInfo(
log_message(
f"Chunk {self.chunk_id} processed in {end_time - start_time:.2f} s; created {len(self.features_out)} features."
)

return True

def finished(self, result):
# This is called in the main thread after `run` completes
# We do *not* write to the data source here if we want to avoid concurrency issues.
pass

def cancel(self):
super().cancel()
# clean up if needed
Loading

0 comments on commit 4de5f6e

Please sign in to comment.