Skip to content

Commit

Permalink
Also include raw raster layer files in zipped raster layer uploads
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Nov 21, 2023
1 parent 01abd85 commit f73a9a1
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 39 deletions.
121 changes: 84 additions & 37 deletions felt/core/layer_exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,13 @@
import tempfile
import uuid
import zipfile
import math
from dataclasses import dataclass
from pathlib import Path
from typing import (
Optional,
List
List,
Tuple
)

from qgis.PyQt.QtCore import (
Expand Down Expand Up @@ -52,7 +54,10 @@
QgsSimpleLineSymbolLayer,
QgsSimpleMarkerSymbolLayer,
QgsEllipseSymbolLayer,
QgsReadWriteContext
QgsReadWriteContext,
QgsRasterPipe,
QgsRasterNuller,
QgsRasterRange
)

from .enums import LayerExportResult
Expand Down Expand Up @@ -336,17 +341,16 @@ def export_vector_layer(
style=self.representative_layer_style(layer)
)

def export_raster_layer(
self,
layer: QgsRasterLayer,
feedback: Optional[QgsFeedback] = None) -> LayerExportDetails:
def run_raster_writer(self,
layer: QgsRasterLayer,
file_name: str,
use_style: bool,
feedback: Optional[QgsFeedback] = None) \
-> Tuple[LayerExportResult, Optional[str]]:
"""
Exports a raster layer into a format acceptable for Felt
Runs a raster write operation for the layer
"""

dest_file = self.generate_file_name('.tif')

writer = QgsRasterFileWriter(dest_file)
writer = QgsRasterFileWriter(file_name)
writer.setOutputFormat('GTiff')
writer.setOutputProviderKey('gdal')
writer.setTiledMode(False)
Expand All @@ -359,26 +363,28 @@ def export_raster_layer(
])

extent = layer.extent()
raster_pipe = layer.pipe()
projector = raster_pipe.projector()
if use_style:
raster_pipe = layer.pipe()
else:
raster_pipe = QgsRasterPipe()
raster_pipe.set(layer.dataProvider().clone())
nuller = QgsRasterNuller()
for band in range(1, layer.dataProvider().bandCount()+1):
additional_no_data_values = layer.dataProvider().userNoDataValues(
band)
source_no_data = layer.dataProvider().sourceNoDataValue(band)
if not math.isnan(source_no_data):
additional_no_data_values.append(
QgsRasterRange(
layer.dataProvider().sourceNoDataValue(band),
layer.dataProvider().sourceNoDataValue(band)
)
)
if additional_no_data_values:
nuller.setNoData(band, additional_no_data_values)
raster_pipe.insert(1, nuller)

dest_crs = layer.crs()
# disable local reprojection for now - see #14
if False: # pylint: disable=using-constant-test
dest_crs = QgsCoordinateReferenceSystem('EPSG:3857')
projector.setCrs(
layer.crs(),
dest_crs,
self.transform_context
)

to_3857_transform = QgsCoordinateTransform(
layer.crs(),
dest_crs,
self.transform_context
)
extent = to_3857_transform.transformBoundingBox(extent)

width = layer.width()
if feedback:
block_feedback = QgsRasterBlockFeedback()
Expand Down Expand Up @@ -411,6 +417,32 @@ def export_raster_layer(
QgsRasterFileWriter.WriterError.WriteCanceled:
None,
}[res]

layer_export_result = {
QgsRasterFileWriter.WriterError.NoError:
LayerExportResult.Success,
QgsRasterFileWriter.WriterError.WriteCanceled:
LayerExportResult.Canceled,
}[res]

return layer_export_result, error_message

def export_raster_layer(
self,
layer: QgsRasterLayer,
feedback: Optional[QgsFeedback] = None) -> LayerExportDetails:
"""
Exports a raster layer into a format acceptable for Felt
"""
raw_dest_file = self.generate_file_name('.tif')
styled_dest_file = raw_dest_file.replace('.tif', '_styled.tif')

layer_export_result, error_message = self.run_raster_writer(
layer,
file_name=styled_dest_file,
use_style=True,
feedback=feedback)

if error_message:
Logger.instance().log_error_json(
{
Expand All @@ -420,16 +452,31 @@ def export_raster_layer(
)
raise LayerPackagingException(error_message)

layer_export_result = {
QgsRasterFileWriter.WriterError.NoError:
LayerExportResult.Success,
QgsRasterFileWriter.WriterError.WriteCanceled:
LayerExportResult.Canceled,
}[res]
filenames = [styled_dest_file]
if layer_export_result != LayerExportResult.Canceled:
# also write raw raster

layer_export_result, error_message = self.run_raster_writer(
layer,
file_name=raw_dest_file,
use_style=False,
feedback=feedback)

if error_message:
Logger.instance().log_error_json(
{
'type': Logger.PACKAGING_RASTER,
'error': 'Error packaging layer: {}'.format(
error_message)
}
)
raise LayerPackagingException(error_message)

filenames.append(raw_dest_file)

return LayerExportDetails(
representative_filename=dest_file,
filenames=[dest_file],
representative_filename=raw_dest_file,
filenames=filenames,
result=layer_export_result,
error_message=error_message,
qgis_style_xml=self._get_original_style_xml(layer)
Expand Down
28 changes: 26 additions & 2 deletions felt/test/test_layer_exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,14 +172,17 @@ def test_raster_conversion(self):
self.assertEqual(result.filename[-4:], '.zip')
with zipfile.ZipFile(result.filename) as z:
tif_files = [f for f in z.namelist() if f.endswith('tif')]
self.assertEqual(len(tif_files), 1)
self.assertEqual(len(tif_files), 2)

styled_tif = [f for f in tif_files if '_styled' in f][0]

self.assertEqual(
result.qgis_style_xml[:58],
"<!DOCTYPE qgis PUBLIC 'http://mrcc.com/qgis.dtd' 'SYSTEM'>"
)

out_layer = QgsRasterLayer(
'/vsizip/{}/{}'.format(result.filename, tif_files[0]),
'/vsizip/{}/{}'.format(result.filename, styled_tif),
'test')
self.assertTrue(out_layer.isValid())
self.assertEqual(out_layer.width(), 373)
Expand All @@ -204,6 +207,27 @@ def test_raster_conversion(self):
self.assertAlmostEqual(out_layer.extent().yMaximum(),
45.8117014376, 3)

raw_tif = [f for f in tif_files if '_styled' not in f][0]
out_layer = QgsRasterLayer(
'/vsizip/{}/{}'.format(result.filename, raw_tif),
'test')
self.assertTrue(out_layer.isValid())
self.assertEqual(out_layer.width(), 373)
self.assertEqual(out_layer.height(), 350)
self.assertEqual(out_layer.bandCount(), 1)
self.assertEqual(out_layer.dataProvider().dataType(1),
Qgis.DataType.Float32)
self.assertEqual(out_layer.crs(),
QgsCoordinateReferenceSystem('EPSG:4326'))
self.assertAlmostEqual(out_layer.extent().xMinimum(),
18.6662979442, 3)
self.assertAlmostEqual(out_layer.extent().xMaximum(),
18.7035979442, 3)
self.assertAlmostEqual(out_layer.extent().yMinimum(),
45.7767014376, 3)
self.assertAlmostEqual(out_layer.extent().yMaximum(),
45.8117014376, 3)


if __name__ == "__main__":
suite = unittest.makeSuite(LayerExporterTest)
Expand Down

0 comments on commit f73a9a1

Please sign in to comment.