267 lines
9.1 KiB
Python
267 lines
9.1 KiB
Python
|
#!/usr/bin/env python3
|
||
|
# ******************************************************************************
|
||
|
#
|
||
|
# Project: GDAL Python Interface
|
||
|
# Purpose: Application for "warping" an image by just updating it's SRS
|
||
|
# and geotransform.
|
||
|
# Author: Frank Warmerdam, warmerdam@pobox.com
|
||
|
#
|
||
|
# ******************************************************************************
|
||
|
# Copyright (c) 2012, Frank Warmerdam
|
||
|
#
|
||
|
# Permission is hereby granted, free of charge, to any person obtaining a
|
||
|
# copy of this software and associated documentation files (the "Software"),
|
||
|
# to deal in the Software without restriction, including without limitation
|
||
|
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||
|
# and/or sell copies of the Software, and to permit persons to whom the
|
||
|
# Software is furnished to do so, subject to the following conditions:
|
||
|
#
|
||
|
# The above copyright notice and this permission notice shall be included
|
||
|
# in all copies or substantial portions of the Software.
|
||
|
#
|
||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||
|
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||
|
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||
|
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||
|
# DEALINGS IN THE SOFTWARE.
|
||
|
# ******************************************************************************
|
||
|
|
||
|
import math
|
||
|
import sys
|
||
|
|
||
|
from osgeo import gdal, osr
|
||
|
|
||
|
###############################################################################
|
||
|
|
||
|
|
||
|
def fmt_loc(srs_obj, loc):
|
||
|
if srs_obj.IsProjected():
|
||
|
return '%12.3f %12.3f' % (loc[0], loc[1])
|
||
|
return '%12.8f %12.8f' % (loc[0], loc[1])
|
||
|
|
||
|
###############################################################################
|
||
|
|
||
|
|
||
|
def move(filename, t_srs, s_srs=None, pixel_threshold=None):
|
||
|
|
||
|
# -------------------------------------------------------------------------
|
||
|
# Open the file.
|
||
|
# -------------------------------------------------------------------------
|
||
|
ds = gdal.Open(filename)
|
||
|
|
||
|
# -------------------------------------------------------------------------
|
||
|
# Compute the current (s_srs) locations of the four corners and center
|
||
|
# of the image.
|
||
|
# -------------------------------------------------------------------------
|
||
|
corners_names = [
|
||
|
'Upper Left',
|
||
|
'Lower Left',
|
||
|
'Upper Right',
|
||
|
'Lower Right',
|
||
|
'Center']
|
||
|
|
||
|
corners_pixel_line = [
|
||
|
(0, 0, 0),
|
||
|
(0, ds.RasterYSize, 0),
|
||
|
(ds.RasterXSize, 0, 0),
|
||
|
(ds.RasterXSize, ds.RasterYSize, 0),
|
||
|
(ds.RasterXSize / 2.0, ds.RasterYSize / 2.0, 0.0)]
|
||
|
|
||
|
orig_gt = ds.GetGeoTransform()
|
||
|
|
||
|
corners_s_geo = []
|
||
|
for item in corners_pixel_line:
|
||
|
corners_s_geo.append(
|
||
|
(orig_gt[0] + item[0] * orig_gt[1] + item[1] * orig_gt[2],
|
||
|
orig_gt[3] + item[0] * orig_gt[4] + item[1] * orig_gt[5],
|
||
|
item[2]))
|
||
|
|
||
|
# -------------------------------------------------------------------------
|
||
|
# Prepare a transformation from source to destination srs.
|
||
|
# -------------------------------------------------------------------------
|
||
|
if s_srs is None:
|
||
|
s_srs = ds.GetProjectionRef()
|
||
|
|
||
|
s_srs_obj = osr.SpatialReference()
|
||
|
s_srs_obj.SetFromUserInput(s_srs)
|
||
|
|
||
|
t_srs_obj = osr.SpatialReference()
|
||
|
t_srs_obj.SetFromUserInput(t_srs)
|
||
|
|
||
|
tr = osr.CoordinateTransformation(s_srs_obj, t_srs_obj)
|
||
|
|
||
|
# -------------------------------------------------------------------------
|
||
|
# Transform the corners
|
||
|
# -------------------------------------------------------------------------
|
||
|
|
||
|
corners_t_geo = tr.TransformPoints(corners_s_geo)
|
||
|
|
||
|
# -------------------------------------------------------------------------
|
||
|
# Compute a new geotransform for the image in the target SRS. For now
|
||
|
# we just use the top left, top right, and bottom left to produce the
|
||
|
# geotransform. The result will be exact at these three points by
|
||
|
# definition, but if the underlying transformation is not affine it will
|
||
|
# be wrong at the center and bottom right. It would be better if we
|
||
|
# used all five points for a least squares fit but that is a bit beyond
|
||
|
# me for now.
|
||
|
# -------------------------------------------------------------------------
|
||
|
ul = corners_t_geo[0]
|
||
|
ur = corners_t_geo[2]
|
||
|
ll = corners_t_geo[1]
|
||
|
|
||
|
new_gt = (ul[0],
|
||
|
(ur[0] - ul[0]) / ds.RasterXSize,
|
||
|
(ll[0] - ul[0]) / ds.RasterYSize,
|
||
|
ul[1],
|
||
|
(ur[1] - ul[1]) / ds.RasterXSize,
|
||
|
(ll[1] - ul[1]) / ds.RasterYSize)
|
||
|
|
||
|
inv_new_gt = gdal.InvGeoTransform(new_gt)
|
||
|
|
||
|
# -------------------------------------------------------------------------
|
||
|
# Report results for the five locations.
|
||
|
# -------------------------------------------------------------------------
|
||
|
|
||
|
corners_t_new_geo = []
|
||
|
error_geo = []
|
||
|
error_pixel_line = []
|
||
|
corners_pixel_line_new = []
|
||
|
|
||
|
print('___Corner___ ________Original________ _______Adjusted_________ ______ Err (geo) ______ _Err (pix)_')
|
||
|
|
||
|
for i in range(len(corners_s_geo)): # pylint: disable=consider-using-enumerate
|
||
|
|
||
|
item = corners_pixel_line[i]
|
||
|
corners_t_new_geo.append(
|
||
|
(new_gt[0] + item[0] * new_gt[1] + item[1] * new_gt[2],
|
||
|
new_gt[3] + item[0] * new_gt[4] + item[1] * new_gt[5],
|
||
|
item[2]))
|
||
|
|
||
|
error_geo.append((corners_t_new_geo[i][0] - corners_t_geo[i][0],
|
||
|
corners_t_new_geo[i][1] - corners_t_geo[i][1],
|
||
|
0.0))
|
||
|
|
||
|
item = corners_t_geo[i]
|
||
|
corners_pixel_line_new.append(
|
||
|
(inv_new_gt[0] + item[0] * inv_new_gt[1] + item[1] * inv_new_gt[2],
|
||
|
inv_new_gt[3] + item[0] * inv_new_gt[4] + item[1] * inv_new_gt[5],
|
||
|
item[2]))
|
||
|
|
||
|
error_pixel_line.append(
|
||
|
(corners_pixel_line_new[i][0] - corners_pixel_line[i][0],
|
||
|
corners_pixel_line_new[i][1] - corners_pixel_line[i][1],
|
||
|
corners_pixel_line_new[i][2] - corners_pixel_line[i][2]))
|
||
|
|
||
|
print('%-11s %s %s %s %5.2f %5.2f' %
|
||
|
(corners_names[i],
|
||
|
fmt_loc(s_srs_obj, corners_s_geo[i]),
|
||
|
fmt_loc(t_srs_obj, corners_t_geo[i]),
|
||
|
fmt_loc(t_srs_obj, error_geo[i]),
|
||
|
error_pixel_line[i][0],
|
||
|
error_pixel_line[i][1]))
|
||
|
|
||
|
print('')
|
||
|
|
||
|
# -------------------------------------------------------------------------
|
||
|
# Do we want to update the file?
|
||
|
# -------------------------------------------------------------------------
|
||
|
max_error = 0
|
||
|
for err_item in error_pixel_line:
|
||
|
this_error = math.sqrt(err_item[0] * err_item[0] + err_item[1] * err_item[1])
|
||
|
if this_error > max_error:
|
||
|
max_error = this_error
|
||
|
|
||
|
update = False
|
||
|
if pixel_threshold is not None:
|
||
|
if pixel_threshold > max_error:
|
||
|
update = True
|
||
|
|
||
|
# -------------------------------------------------------------------------
|
||
|
# Apply the change coordinate system and geotransform.
|
||
|
# -------------------------------------------------------------------------
|
||
|
if update:
|
||
|
ds = None
|
||
|
ds = gdal.Open(filename, gdal.GA_Update)
|
||
|
|
||
|
print('Updating file...')
|
||
|
ds.SetGeoTransform(new_gt)
|
||
|
ds.SetProjection(t_srs_obj.ExportToWkt())
|
||
|
print('Done.')
|
||
|
|
||
|
elif pixel_threshold is None:
|
||
|
print('No error threshold in pixels selected with -et, file not updated.')
|
||
|
|
||
|
else:
|
||
|
print("""Maximum check point error is %.5f pixels which exceeds the
|
||
|
error threshold so the file has not been updated.""" % max_error)
|
||
|
|
||
|
ds = None
|
||
|
|
||
|
###############################################################################
|
||
|
|
||
|
|
||
|
def Usage():
|
||
|
print("""gdalmove.py [-s_srs <srs_defn>] -t_srs <srs_defn>
|
||
|
[-et <max_pixel_err>] target_file""")
|
||
|
return 1
|
||
|
|
||
|
|
||
|
def main(argv):
|
||
|
# Default GDAL argument parsing.
|
||
|
|
||
|
argv = gdal.GeneralCmdLineProcessor(argv)
|
||
|
if argv is None:
|
||
|
return 0
|
||
|
|
||
|
if len(argv) == 1:
|
||
|
return Usage()
|
||
|
|
||
|
# Script argument defaults
|
||
|
s_srs = None
|
||
|
t_srs = None
|
||
|
filename = None
|
||
|
pixel_threshold = None
|
||
|
|
||
|
# Script argument parsing.
|
||
|
|
||
|
i = 1
|
||
|
while i < len(argv):
|
||
|
|
||
|
if argv[i] == '-s_srs' and i < len(argv) - 1:
|
||
|
s_srs = argv[i + 1]
|
||
|
i += 1
|
||
|
|
||
|
elif argv[i] == '-t_srs' and i < len(argv) - 1:
|
||
|
t_srs = argv[i + 1]
|
||
|
i += 1
|
||
|
|
||
|
elif argv[i] == '-et' and i < len(argv) - 1:
|
||
|
pixel_threshold = float(argv[i + 1])
|
||
|
i += 1
|
||
|
|
||
|
elif filename is None:
|
||
|
filename = argv[i]
|
||
|
|
||
|
else:
|
||
|
print('Urecognised argument: ' + argv[i])
|
||
|
return Usage()
|
||
|
|
||
|
i = i + 1
|
||
|
# next argument
|
||
|
|
||
|
if filename is None:
|
||
|
print('Missing name of file to operate on, but required.')
|
||
|
return Usage()
|
||
|
|
||
|
if t_srs is None:
|
||
|
print('Target SRS (-t_srs) missing, but required.')
|
||
|
return Usage()
|
||
|
|
||
|
move(filename, t_srs, s_srs, pixel_threshold)
|
||
|
|
||
|
|
||
|
if __name__ == '__main__':
|
||
|
sys.exit(main(sys.argv))
|