Skip to content

Commit 4dadf4a

Browse files
Mtk112LKajan
authored andcommitted
Implement GeoTiff export
1 parent 8c9cf33 commit 4dadf4a

File tree

2 files changed

+154
-6
lines changed

2 files changed

+154
-6
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
import os
2+
3+
from osgeo import gdal
4+
from qgis.core import QgsMapRendererParallelJob, QgsMapSettings, QgsMapSettingsUtils
5+
from qgis.PyQt.QtCore import QSize, Qt
6+
from qgis.PyQt.QtGui import QColor
7+
from qgis.PyQt.QtWidgets import QApplication, QFileDialog
8+
9+
from arho_feature_template.project.layers.plan_layers import (
10+
LandUseAreaLayer,
11+
LandUsePointLayer,
12+
LineLayer,
13+
OtherAreaLayer,
14+
OtherPointLayer,
15+
PlanLayer,
16+
)
17+
from arho_feature_template.utils.misc_utils import get_active_plan_id, iface
18+
19+
20+
class GeoTiffCreator:
21+
def __init__(self, desired_pixel_size=0.5):
22+
"""Initialize the CreateGeoTiff class and fetch the required data."""
23+
self.desired_pixel_size = desired_pixel_size
24+
self.plan_layer = PlanLayer.get_from_project()
25+
26+
if not self.plan_layer:
27+
iface.messageBar().pushWarning("", "Ei aktiivista kaavaa.")
28+
return
29+
30+
self.feature = PlanLayer.get_feature_by_id(get_active_plan_id(), no_geometries=False)
31+
32+
if not self.feature:
33+
iface.messageBar().pushWarning("", "Ei aktiivista kaavaa.")
34+
return
35+
36+
def select_output_file(self):
37+
"""Opens a file dialog for the user to select the output GeoTIFF file."""
38+
geotiff_path, _ = QFileDialog.getSaveFileName(
39+
None, "Määritä GeoTIFF tallennuspolku", "", "GeoTIFF Files (*.tif)"
40+
)
41+
if not geotiff_path:
42+
iface.messageBar().pushWarning("", "Tallennuspolkua ei määritetty.")
43+
return None
44+
45+
self.create_geotiff(geotiff_path)
46+
47+
def create_geotiff(self, geotiff_path):
48+
"""Creates a GeoTIFF from the active plan layer and feature."""
49+
if not self.plan_layer or not self.feature:
50+
return
51+
52+
QApplication.setOverrideCursor(Qt.WaitCursor)
53+
54+
# Set buffered bounding box
55+
bbox = self.feature.geometry().boundingBox()
56+
buffer_percentage = 0.1
57+
buffer_x = bbox.width() * buffer_percentage
58+
buffer_y = bbox.height() * buffer_percentage
59+
buffered_bbox = bbox.buffered(max(buffer_x, buffer_y))
60+
61+
# Rendering settings
62+
settings = QgsMapSettings()
63+
settings.setLayers(
64+
[
65+
LandUseAreaLayer.get_from_project(),
66+
OtherAreaLayer.get_from_project(),
67+
LandUsePointLayer.get_from_project(),
68+
LineLayer.get_from_project(),
69+
OtherPointLayer.get_from_project(),
70+
PlanLayer.get_from_project(),
71+
]
72+
)
73+
settings.setBackgroundColor(QColor(255, 255, 255))
74+
width = int(buffered_bbox.width() / self.desired_pixel_size) * self.desired_pixel_size
75+
height = int(buffered_bbox.height() / self.desired_pixel_size) * self.desired_pixel_size
76+
buffered_bbox.setXMinimum(int(buffered_bbox.xMinimum()))
77+
buffered_bbox.setYMinimum(int(buffered_bbox.yMinimum()))
78+
buffered_bbox.setXMaximum(buffered_bbox.xMinimum() + width)
79+
buffered_bbox.setYMaximum(buffered_bbox.yMinimum() + height)
80+
settings.setExtent(buffered_bbox)
81+
82+
# Calculate image size
83+
pixels_x = int(buffered_bbox.width() / self.desired_pixel_size)
84+
pixels_y = int(buffered_bbox.height() / self.desired_pixel_size)
85+
settings.setOutputSize(QSize(pixels_x, pixels_y))
86+
87+
render = QgsMapRendererParallelJob(settings)
88+
89+
def finished():
90+
try:
91+
img = render.renderedImage()
92+
93+
# Save the image as PNG temporarily
94+
image_path = geotiff_path.replace(".tif", ".png")
95+
img.save(image_path, "PNG")
96+
97+
# Generate the World File (.pgw)
98+
pgw_content = QgsMapSettingsUtils.worldFileContent(settings)
99+
pgw_path = image_path.replace(".png", ".pgw")
100+
with open(pgw_path, "w") as f:
101+
f.write(pgw_content)
102+
103+
# Convert PNG to GeoTIFF
104+
self._create_geotiff_from_png(image_path, geotiff_path)
105+
106+
# Delete temporary PNG
107+
if os.path.exists(image_path):
108+
os.remove(image_path)
109+
110+
# Delete temporary World File (.pgw)
111+
if os.path.exists(pgw_path):
112+
os.remove(pgw_path)
113+
finally:
114+
QApplication.restoreOverrideCursor()
115+
116+
render.finished.connect(finished)
117+
render.start()
118+
119+
def _create_geotiff_from_png(self, image_path, geotiff_path):
120+
"""Convert the rendered PNG to GeoTIFF."""
121+
122+
ds = gdal.Open(image_path)
123+
124+
# Convert the PNG to GeoTIFF
125+
gdal.Translate(
126+
geotiff_path,
127+
ds,
128+
outputSRS="EPSG:3067",
129+
format="GTiff",
130+
creationOptions=["COMPRESS=LZW", "TILED=YES"],
131+
)
132+
133+
iface.messageBar().pushSuccess("", f"GeoTIFF tallennettu polkuun: {geotiff_path}")

arho_feature_template/plugin.py

+21-6
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from qgis.PyQt.QtGui import QIcon
99
from qgis.PyQt.QtWidgets import QAction, QWidget
1010

11+
from arho_feature_template.core.geotiff_creator import GeoTiffCreator
1112
from arho_feature_template.core.plan_manager import PlanManager
1213
from arho_feature_template.gui.dialogs.plugin_settings import PluginSettings
1314
from arho_feature_template.gui.docks.validation_dock import ValidationDock
@@ -220,6 +221,16 @@ def initGui(self) -> None: # noqa N802
220221
add_to_toolbar=True,
221222
)
222223

224+
self.edit_lifecycles_action = self.add_action(
225+
text="Kaavan elinkaaret",
226+
icon=QgsApplication.getThemeIcon("mIconFieldDate.svg"),
227+
# icon=QIcon(resources_path("icons", "toolbar", "tallenna_jsonina2.svg")),
228+
triggered_callback=self.edit_lifecycles,
229+
add_to_menu=True,
230+
add_to_toolbar=True,
231+
status_tip="Muokkaa kaavan elinkaaria",
232+
)
233+
223234
self.serialize_plan_action = self.add_action(
224235
text="Tallenna kaava JSON",
225236
# icon=QgsApplication.getThemeIcon("mActionFileSaveAs.svg"),
@@ -230,14 +241,13 @@ def initGui(self) -> None: # noqa N802
230241
status_tip="Tallenna aktiivinen kaava JSON muodossa",
231242
)
232243

233-
self.edit_lifecycles_action = self.add_action(
234-
text="Kaavan elinkaaret",
235-
icon=QgsApplication.getThemeIcon("mIconFieldDate.svg"),
236-
# icon=QIcon(resources_path("icons", "toolbar", "tallenna_jsonina2.svg")),
237-
triggered_callback=self.edit_lifecycles,
244+
self.create_geotiff_action = self.add_action(
245+
text="Tallenna kaavakartta",
246+
icon=QgsApplication.getThemeIcon("mActionAddRasterLayer.svg"),
247+
triggered_callback=self.create_geotiff,
238248
add_to_menu=True,
239249
add_to_toolbar=True,
240-
status_tip="Muokkaa kaavan elinkaaria",
250+
status_tip="Tallenna aktiivinen kaava geotiff muodossa",
241251
)
242252

243253
self.plugin_settings_action = self.add_action(
@@ -280,6 +290,11 @@ def edit_lifecycles(self):
280290
"""Edit lifecycles of currently active plan."""
281291
self.plan_manager.edit_lifecycles()
282292

293+
def create_geotiff(self):
294+
"""Create geotiff from currently active plan."""
295+
geotiff_creator = GeoTiffCreator()
296+
geotiff_creator.select_output_file()
297+
283298
def unload(self) -> None:
284299
"""Removes the plugin menu item and icon from QGIS GUI."""
285300
# Handle signals

0 commit comments

Comments
 (0)