|
| 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}") |
0 commit comments