1 2# -*- coding: utf-8 -*- 3"""QGIS Unit tests for QgsDatumTransforms. 4 5.. note:: This program is free software; you can redistribute it and/or modify 6it under the terms of the GNU General Public License as published by 7the Free Software Foundation; either version 2 of the License, or 8(at your option) any later version. 9""" 10__author__ = 'Nyall Dawson' 11__date__ = '2019-05-25' 12__copyright__ = 'Copyright 2019, The QGIS Project' 13 14from qgis.core import ( 15 QgsProjUtils, 16 QgsCoordinateReferenceSystem, 17 QgsDatumTransform 18) 19from qgis.testing import (start_app, 20 unittest, 21 ) 22from utilities import unitTestDataPath 23 24start_app() 25TEST_DATA_DIR = unitTestDataPath() 26 27 28class TestPyQgsDatumTransform(unittest.TestCase): 29 30 @unittest.skipIf(QgsProjUtils.projVersionMajor() < 6, 'Not a proj6 build') 31 def testOperations(self): 32 ops = QgsDatumTransform.operations(QgsCoordinateReferenceSystem(), 33 QgsCoordinateReferenceSystem()) 34 self.assertEqual(ops, []) 35 ops = QgsDatumTransform.operations(QgsCoordinateReferenceSystem('EPSG:3111'), 36 QgsCoordinateReferenceSystem()) 37 self.assertEqual(ops, []) 38 ops = QgsDatumTransform.operations(QgsCoordinateReferenceSystem(), 39 QgsCoordinateReferenceSystem('EPSG:3111')) 40 self.assertEqual(ops, []) 41 42 ops = QgsDatumTransform.operations(QgsCoordinateReferenceSystem('EPSG:3111'), 43 QgsCoordinateReferenceSystem('EPSG:3111')) 44 self.assertEqual(len(ops), 1) 45 self.assertTrue(ops[0].name) 46 self.assertEqual(ops[0].proj, '+proj=noop') 47 self.assertEqual(ops[0].accuracy, 0.0) 48 self.assertTrue(ops[0].isAvailable) 49 50 ops = QgsDatumTransform.operations(QgsCoordinateReferenceSystem('EPSG:3111'), 51 QgsCoordinateReferenceSystem('EPSG:4283')) 52 self.assertEqual(len(ops), 1) 53 self.assertTrue(ops[0].name) 54 self.assertEqual(ops[0].proj, '+proj=pipeline +step +inv +proj=lcc +lat_0=-37 +lon_0=145 +lat_1=-36 +lat_2=-38 +x_0=2500000 +y_0=2500000 +ellps=GRS80 +step +proj=unitconvert +xy_in=rad +xy_out=deg') 55 self.assertEqual(ops[0].accuracy, -1.0) 56 self.assertTrue(ops[0].isAvailable) 57 58 ops = QgsDatumTransform.operations(QgsCoordinateReferenceSystem('EPSG:3111'), 59 QgsCoordinateReferenceSystem('EPSG:28355')) 60 self.assertEqual(len(ops), 1) 61 self.assertTrue(ops[0].name) 62 self.assertEqual(ops[0].proj, '+proj=pipeline +step +inv +proj=lcc +lat_0=-37 +lon_0=145 +lat_1=-36 +lat_2=-38 +x_0=2500000 +y_0=2500000 +ellps=GRS80 +step +proj=utm +zone=55 +south +ellps=GRS80') 63 self.assertEqual(ops[0].accuracy, 0.0) 64 self.assertTrue(ops[0].isAvailable) 65 66 # uses a grid file 67 ops = QgsDatumTransform.operations(QgsCoordinateReferenceSystem('EPSG:4283'), 68 QgsCoordinateReferenceSystem('EPSG:7844')) 69 self.assertGreaterEqual(len(ops), 5) 70 71 op1_index = [i for i in range(len(ops)) if ops[i].proj == '+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=push +v_3 +step +proj=cart +ellps=GRS80 +step +proj=helmert +x=0.06155 +y=-0.01087 +z=-0.04019 +rx=-0.0394924 +ry=-0.0327221 +rz=-0.0328979 +s=-0.009994 +convention=coordinate_frame +step +inv +proj=cart +ellps=GRS80 +step +proj=pop +v_3 +step +proj=unitconvert +xy_in=rad +xy_out=deg'][0] 72 self.assertTrue(ops[op1_index].name) 73 self.assertEqual(ops[op1_index].proj, '+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=push +v_3 +step +proj=cart +ellps=GRS80 +step +proj=helmert +x=0.06155 +y=-0.01087 +z=-0.04019 +rx=-0.0394924 +ry=-0.0327221 +rz=-0.0328979 +s=-0.009994 +convention=coordinate_frame +step +inv +proj=cart +ellps=GRS80 +step +proj=pop +v_3 +step +proj=unitconvert +xy_in=rad +xy_out=deg') 74 self.assertTrue(ops[op1_index].isAvailable) 75 self.assertEqual(ops[op1_index].accuracy, 0.01) 76 self.assertEqual(len(ops[op1_index].grids), 0) 77 78 if QgsProjUtils.projVersionMajor() == 6: 79 op2_index = [i for i in range(len(ops)) if ops[i].proj == '+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=hgridshift +grids=GDA94_GDA2020_conformal_and_distortion.gsb +step +proj=unitconvert +xy_in=rad +xy_out=deg'][0] 80 else: 81 op2_index = [i for i in range(len(ops)) if ops[ 82 i].proj == '+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=hgridshift +grids=au_icsm_GDA94_GDA2020_conformal_and_distortion.tif +step +proj=unitconvert +xy_in=rad +xy_out=deg'][ 83 0] 84 self.assertTrue(ops[op2_index].name) 85 if QgsProjUtils.projVersionMajor() == 6: 86 self.assertEqual(ops[op2_index].proj, '+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=hgridshift +grids=GDA94_GDA2020_conformal_and_distortion.gsb +step +proj=unitconvert +xy_in=rad +xy_out=deg') 87 else: 88 self.assertEqual(ops[op2_index].proj, 89 '+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=hgridshift +grids=au_icsm_GDA94_GDA2020_conformal_and_distortion.tif +step +proj=unitconvert +xy_in=rad +xy_out=deg') 90 self.assertEqual(ops[op2_index].accuracy, 0.05) 91 self.assertEqual(len(ops[op2_index].grids), 1) 92 if QgsProjUtils.projVersionMajor() == 6: 93 self.assertEqual(ops[op2_index].grids[0].shortName, 'GDA94_GDA2020_conformal_and_distortion.gsb') 94 else: 95 self.assertEqual(ops[op2_index].grids[0].shortName, 'au_icsm_GDA94_GDA2020_conformal_and_distortion.tif') 96 self.assertEqual(ops[op2_index].grids[0].fullName, '') 97 if QgsProjUtils.projVersionMajor() == 6: 98 self.assertTrue(ops[op2_index].grids[0].packageName) 99 self.assertIn('http', ops[op2_index].grids[0].url) 100 self.assertTrue(ops[op2_index].grids[0].directDownload) 101 self.assertTrue(ops[op2_index].grids[0].openLicense) 102 103 if QgsProjUtils.projVersionMajor() == 6: 104 op3_index = [i for i in range(len(ops)) if ops[i].proj == '+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=hgridshift +grids=GDA94_GDA2020_conformal.gsb +step +proj=unitconvert +xy_in=rad +xy_out=deg'][0] 105 else: 106 op3_index = [i for i in range(len(ops)) if ops[ 107 i].proj == '+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=hgridshift +grids=au_icsm_GDA94_GDA2020_conformal.tif +step +proj=unitconvert +xy_in=rad +xy_out=deg'][ 108 0] 109 self.assertTrue(ops[op3_index].name) 110 if QgsProjUtils.projVersionMajor() == 6: 111 self.assertEqual(ops[op3_index].proj, '+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=hgridshift +grids=GDA94_GDA2020_conformal.gsb +step +proj=unitconvert +xy_in=rad +xy_out=deg') 112 else: 113 self.assertEqual(ops[op3_index].proj, 114 '+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=hgridshift +grids=au_icsm_GDA94_GDA2020_conformal.tif +step +proj=unitconvert +xy_in=rad +xy_out=deg') 115 self.assertEqual(ops[op3_index].accuracy, 0.05) 116 self.assertEqual(len(ops[op3_index].grids), 1) 117 if QgsProjUtils.projVersionMajor() == 6: 118 self.assertEqual(ops[op3_index].grids[0].shortName, 'GDA94_GDA2020_conformal.gsb') 119 else: 120 self.assertEqual(ops[op3_index].grids[0].shortName, 'au_icsm_GDA94_GDA2020_conformal.tif') 121 self.assertEqual(ops[op3_index].grids[0].fullName, '') 122 if QgsProjUtils.projVersionMajor() == 6: 123 self.assertTrue(ops[op3_index].grids[0].packageName) 124 self.assertIn('http', ops[op3_index].grids[0].url) 125 self.assertTrue(ops[op3_index].grids[0].directDownload) 126 self.assertTrue(ops[op3_index].grids[0].openLicense) 127 128 if QgsProjUtils.projVersionMajor() == 6: 129 op4_index = [i for i in range(len(ops)) if ops[i].proj == '+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=hgridshift +grids=GDA94_GDA2020_conformal_cocos_island.gsb +step +proj=unitconvert +xy_in=rad +xy_out=deg'][0] 130 else: 131 op4_index = [i for i in range(len(ops)) if ops[ 132 i].proj == '+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=hgridshift +grids=au_icsm_GDA94_GDA2020_conformal_cocos_island.tif +step +proj=unitconvert +xy_in=rad +xy_out=deg'][ 133 0] 134 self.assertTrue(ops[op4_index].name) 135 if QgsProjUtils.projVersionMajor() == 6: 136 self.assertEqual(ops[op4_index].proj, '+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=hgridshift +grids=GDA94_GDA2020_conformal_cocos_island.gsb +step +proj=unitconvert +xy_in=rad +xy_out=deg') 137 else: 138 self.assertEqual(ops[op4_index].proj, 139 '+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=hgridshift +grids=au_icsm_GDA94_GDA2020_conformal_cocos_island.tif +step +proj=unitconvert +xy_in=rad +xy_out=deg') 140 self.assertEqual(ops[op4_index].accuracy, 0.05) 141 self.assertEqual(len(ops[op4_index].grids), 1) 142 if QgsProjUtils.projVersionMajor() == 6: 143 self.assertEqual(ops[op4_index].grids[0].shortName, 'GDA94_GDA2020_conformal_cocos_island.gsb') 144 else: 145 self.assertEqual(ops[op4_index].grids[0].shortName, 'au_icsm_GDA94_GDA2020_conformal_cocos_island.tif') 146 self.assertEqual(ops[op4_index].grids[0].fullName, '') 147 if QgsProjUtils.projVersionMajor() == 6: 148 self.assertTrue(ops[op4_index].grids[0].packageName) 149 self.assertIn('http', ops[op4_index].grids[0].url) 150 151 if QgsProjUtils.projVersionMajor() == 6: 152 op5_index = [i for i in range(len(ops)) if ops[i].proj == '+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=hgridshift +grids=GDA94_GDA2020_conformal_christmas_island.gsb +step +proj=unitconvert +xy_in=rad +xy_out=deg'][0] 153 else: 154 op5_index = [i for i in range(len(ops)) if ops[ 155 i].proj == '+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=hgridshift +grids=au_icsm_GDA94_GDA2020_conformal_christmas_island.tif +step +proj=unitconvert +xy_in=rad +xy_out=deg'][ 156 0] 157 self.assertTrue(ops[op5_index].name) 158 if QgsProjUtils.projVersionMajor() == 6: 159 self.assertEqual(ops[op5_index].proj, '+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=hgridshift +grids=GDA94_GDA2020_conformal_christmas_island.gsb +step +proj=unitconvert +xy_in=rad +xy_out=deg') 160 else: 161 self.assertEqual(ops[op5_index].proj, 162 '+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=hgridshift +grids=au_icsm_GDA94_GDA2020_conformal_christmas_island.tif +step +proj=unitconvert +xy_in=rad +xy_out=deg') 163 self.assertEqual(ops[op5_index].accuracy, 0.05) 164 self.assertEqual(len(ops[op5_index].grids), 1) 165 if QgsProjUtils.projVersionMajor() == 6: 166 self.assertEqual(ops[op5_index].grids[0].shortName, 'GDA94_GDA2020_conformal_christmas_island.gsb') 167 else: 168 self.assertEqual(ops[op5_index].grids[0].shortName, 'au_icsm_GDA94_GDA2020_conformal_christmas_island.tif') 169 self.assertEqual(ops[op5_index].grids[0].fullName, '') 170 if QgsProjUtils.projVersionMajor() == 6: 171 self.assertTrue(ops[op5_index].grids[0].packageName) 172 self.assertIn('http', ops[op5_index].grids[0].url) 173 174 # uses a pivot datum (technically a proj test, but this will help me sleep at night ;) 175 ops = QgsDatumTransform.operations(QgsCoordinateReferenceSystem('EPSG:3111'), 176 QgsCoordinateReferenceSystem('EPSG:7899')) 177 178 self.assertGreaterEqual(len(ops), 3) 179 180 op1_index = [i for i in range(len(ops)) if ops[i].proj == '+proj=pipeline +step +inv +proj=lcc +lat_0=-37 +lon_0=145 +lat_1=-36 +lat_2=-38 +x_0=2500000 +y_0=2500000 +ellps=GRS80 +step +proj=push +v_3 +step +proj=cart +ellps=GRS80 +step +proj=helmert +x=0.06155 +y=-0.01087 +z=-0.04019 +rx=-0.0394924 +ry=-0.0327221 +rz=-0.0328979 +s=-0.009994 +convention=coordinate_frame +step +inv +proj=cart +ellps=GRS80 +step +proj=pop +v_3 +step +proj=lcc +lat_0=-37 +lon_0=145 +lat_1=-36 +lat_2=-38 +x_0=2500000 +y_0=2500000 +ellps=GRS80'][0] 181 self.assertTrue(ops[op1_index].name) 182 self.assertEqual(ops[op1_index].proj, '+proj=pipeline +step +inv +proj=lcc +lat_0=-37 +lon_0=145 +lat_1=-36 +lat_2=-38 +x_0=2500000 +y_0=2500000 +ellps=GRS80 +step +proj=push +v_3 +step +proj=cart +ellps=GRS80 +step +proj=helmert +x=0.06155 +y=-0.01087 +z=-0.04019 +rx=-0.0394924 +ry=-0.0327221 +rz=-0.0328979 +s=-0.009994 +convention=coordinate_frame +step +inv +proj=cart +ellps=GRS80 +step +proj=pop +v_3 +step +proj=lcc +lat_0=-37 +lon_0=145 +lat_1=-36 +lat_2=-38 +x_0=2500000 +y_0=2500000 +ellps=GRS80') 183 self.assertTrue(ops[op1_index].isAvailable) 184 self.assertEqual(ops[op1_index].accuracy, 0.01) 185 self.assertEqual(len(ops[op1_index].grids), 0) 186 187 if QgsProjUtils.projVersionMajor() == 6: 188 op2_index = [i for i in range(len(ops)) if ops[i].proj == '+proj=pipeline +step +inv +proj=lcc +lat_0=-37 +lon_0=145 +lat_1=-36 +lat_2=-38 +x_0=2500000 +y_0=2500000 +ellps=GRS80 +step +proj=hgridshift +grids=GDA94_GDA2020_conformal_and_distortion.gsb +step +proj=lcc +lat_0=-37 +lon_0=145 +lat_1=-36 +lat_2=-38 +x_0=2500000 +y_0=2500000 +ellps=GRS80'][0] 189 else: 190 op2_index = [i for i in range(len(ops)) if ops[ 191 i].proj == '+proj=pipeline +step +inv +proj=lcc +lat_0=-37 +lon_0=145 +lat_1=-36 +lat_2=-38 +x_0=2500000 +y_0=2500000 +ellps=GRS80 +step +proj=hgridshift +grids=au_icsm_GDA94_GDA2020_conformal_and_distortion.tif +step +proj=lcc +lat_0=-37 +lon_0=145 +lat_1=-36 +lat_2=-38 +x_0=2500000 +y_0=2500000 +ellps=GRS80'][ 192 0] 193 self.assertTrue(ops[op2_index].name) 194 if QgsProjUtils.projVersionMajor() == 6: 195 self.assertEqual(ops[op2_index].proj, '+proj=pipeline +step +inv +proj=lcc +lat_0=-37 +lon_0=145 +lat_1=-36 +lat_2=-38 +x_0=2500000 +y_0=2500000 +ellps=GRS80 +step +proj=hgridshift +grids=GDA94_GDA2020_conformal_and_distortion.gsb +step +proj=lcc +lat_0=-37 +lon_0=145 +lat_1=-36 +lat_2=-38 +x_0=2500000 +y_0=2500000 +ellps=GRS80') 196 else: 197 self.assertEqual(ops[op2_index].proj, 198 '+proj=pipeline +step +inv +proj=lcc +lat_0=-37 +lon_0=145 +lat_1=-36 +lat_2=-38 +x_0=2500000 +y_0=2500000 +ellps=GRS80 +step +proj=hgridshift +grids=au_icsm_GDA94_GDA2020_conformal_and_distortion.tif +step +proj=lcc +lat_0=-37 +lon_0=145 +lat_1=-36 +lat_2=-38 +x_0=2500000 +y_0=2500000 +ellps=GRS80') 199 self.assertEqual(ops[op2_index].accuracy, 0.05) 200 self.assertEqual(len(ops[op2_index].grids), 1) 201 if QgsProjUtils.projVersionMajor() == 6: 202 self.assertEqual(ops[op2_index].grids[0].shortName, 'GDA94_GDA2020_conformal_and_distortion.gsb') 203 else: 204 self.assertEqual(ops[op2_index].grids[0].shortName, 'au_icsm_GDA94_GDA2020_conformal_and_distortion.tif') 205 self.assertEqual(ops[op2_index].grids[0].fullName, '') 206 if QgsProjUtils.projVersionMajor() == 6: 207 self.assertTrue(ops[op2_index].grids[0].packageName) 208 self.assertIn('http', ops[op2_index].grids[0].url) 209 self.assertTrue(ops[op2_index].grids[0].directDownload) 210 self.assertTrue(ops[op2_index].grids[0].openLicense) 211 212 if QgsProjUtils.projVersionMajor() == 6: 213 op3_index = [i for i in range(len(ops)) if ops[i].proj == '+proj=pipeline +step +inv +proj=lcc +lat_0=-37 +lon_0=145 +lat_1=-36 +lat_2=-38 +x_0=2500000 +y_0=2500000 +ellps=GRS80 +step +proj=hgridshift +grids=GDA94_GDA2020_conformal.gsb +step +proj=lcc +lat_0=-37 +lon_0=145 +lat_1=-36 +lat_2=-38 +x_0=2500000 +y_0=2500000 +ellps=GRS80'][0] 214 else: 215 op3_index = [i for i in range(len(ops)) if ops[ 216 i].proj == '+proj=pipeline +step +inv +proj=lcc +lat_0=-37 +lon_0=145 +lat_1=-36 +lat_2=-38 +x_0=2500000 +y_0=2500000 +ellps=GRS80 +step +proj=hgridshift +grids=au_icsm_GDA94_GDA2020_conformal.tif +step +proj=lcc +lat_0=-37 +lon_0=145 +lat_1=-36 +lat_2=-38 +x_0=2500000 +y_0=2500000 +ellps=GRS80'][ 217 0] 218 self.assertTrue(ops[op3_index].name) 219 if QgsProjUtils.projVersionMajor() == 6: 220 self.assertEqual(ops[op3_index].proj, '+proj=pipeline +step +inv +proj=lcc +lat_0=-37 +lon_0=145 +lat_1=-36 +lat_2=-38 +x_0=2500000 +y_0=2500000 +ellps=GRS80 +step +proj=hgridshift +grids=GDA94_GDA2020_conformal.gsb +step +proj=lcc +lat_0=-37 +lon_0=145 +lat_1=-36 +lat_2=-38 +x_0=2500000 +y_0=2500000 +ellps=GRS80') 221 else: 222 self.assertEqual(ops[op3_index].proj, 223 '+proj=pipeline +step +inv +proj=lcc +lat_0=-37 +lon_0=145 +lat_1=-36 +lat_2=-38 +x_0=2500000 +y_0=2500000 +ellps=GRS80 +step +proj=hgridshift +grids=au_icsm_GDA94_GDA2020_conformal.tif +step +proj=lcc +lat_0=-37 +lon_0=145 +lat_1=-36 +lat_2=-38 +x_0=2500000 +y_0=2500000 +ellps=GRS80') 224 self.assertEqual(ops[op3_index].accuracy, 0.05) 225 self.assertEqual(len(ops[op3_index].grids), 1) 226 if QgsProjUtils.projVersionMajor() == 6: 227 self.assertEqual(ops[op3_index].grids[0].shortName, 'GDA94_GDA2020_conformal.gsb') 228 else: 229 self.assertEqual(ops[op3_index].grids[0].shortName, 'au_icsm_GDA94_GDA2020_conformal.tif') 230 self.assertEqual(ops[op3_index].grids[0].fullName, '') 231 if QgsProjUtils.projVersionMajor() == 6: 232 self.assertTrue(ops[op3_index].grids[0].packageName) 233 self.assertIn('http', ops[op3_index].grids[0].url) 234 self.assertTrue(ops[op3_index].grids[0].directDownload) 235 self.assertTrue(ops[op3_index].grids[0].openLicense) 236 237 @unittest.skipIf(QgsProjUtils.projVersionMajor() < 7, 'Not a proj >= 7 build') 238 def testNoLasLos(self): 239 """ 240 Test that operations which rely on an NADCON5 grid shift file (which are unsupported by Proj... at time of writing !) are not returned 241 """ 242 ops = QgsDatumTransform.operations(QgsCoordinateReferenceSystem('EPSG:4138'), 243 QgsCoordinateReferenceSystem('EPSG:4269')) 244 self.assertEqual(len(ops), 2) 245 self.assertTrue(ops[0].name) 246 self.assertTrue(ops[0].proj) 247 self.assertTrue(ops[1].name) 248 self.assertTrue(ops[1].proj) 249 250 251if __name__ == '__main__': 252 unittest.main() 253