To be run from the QGIS Python Console.
The new raster will be in the same folder and with the same extension (tested only with tif
), to the name shift by x and by y will be added, e.g. in.tif
-> in_10_20.tif
.
After the first run, later just call shift_layer(100, -3050)
, by default the active layer will be used.
Edit 2023.12.19: change default value of layer
parameter of shift_layer
function from iface.activeLayer()
to iface.activeLayer
, so active layer woulb be recalculated each time the function is called with the default parameter
import pathlib
from pathlib import Path
from typing import Union, Callable
def shift_layer(
shift_x: float = 0, shift_y: float = 0,
layer: Union[
QgsMapLayer, Callable[[None], QgsMapLayer]] = iface.activeLayer,
add_layer: bool = True) -> None:
# if iface.activeLayer() is used as default value for layer,
# the active layer at the moment of declaration will be used
if isinstance(layer, QgsMapLayer):
_layer = layer
else:
_layer = layer()
src = pathlib.Path(_layer.source())
stem_suffix = f'{shift_x}_{shift_y}'
target: Path = src.with_stem(src.stem + stem_suffix)
shifted_extent_gdal_order: tuple[float, float, float, float] = (
get_shifted_extent_ggal_order(_layer, shift_x, shift_y)
)
shifted_raster: str = shift_raster(
_layer, shifted_extent_gdal_order, target
)['OUTPUT']
if not add_layer:
return None
iface.addRasterLayer(shifted_raster, _layer.name() + stem_suffix)
def shift_raster(
layer: QgsRasterLayer,
new_extent_gdal_order: tuple[float, float, float, float],
target: Path) -> dict[str, str]:
assert isinstance(layer, QgsRasterLayer), 'not raster layer'
return processing.run('gdal:translate', {
'INPUT': layer.source(),
'EXTRA': f"-a_ullr {' '.join(map(str, new_extent_gdal_order))}",
'OUTPUT': str(target)
})
def get_shifted_extent_gdal_order(
layer: QgsMapLayer,
shift_x: float = 0,
shift_y: float = 0) -> tuple[float, float, float, float]:
return (
layer.extent().xMinimum() + shift_x,
layer.extent().yMaximum() + shift_y,
layer.extent().xMaximum() + shift_x,
layer.extent().yMinimum() + shift_y
)
if __name__ == '__console__':
shift_layer(100, -3050)