1import concurrent.futures 2import json 3 4import numpy 5import pytest 6 7import pyproj 8from pyproj import CRS 9from pyproj._crs import AuthorityMatchInfo 10from pyproj.crs import ( 11 CoordinateOperation, 12 CoordinateSystem, 13 Datum, 14 Ellipsoid, 15 PrimeMeridian, 16) 17from pyproj.crs.enums import CoordinateOperationType, DatumType 18from pyproj.enums import ProjVersion, WktVersion 19from pyproj.exceptions import CRSError 20from pyproj.transformer import TransformerGroup 21from test.conftest import ( 22 HAYFORD_ELLIPSOID_NAME, 23 PROJ_GTE_8, 24 assert_can_pickle, 25 get_wgs84_datum_name, 26 grids_available, 27) 28 29 30class CustomCRS(object): 31 def to_wkt(self): 32 return CRS.from_epsg(4326).to_wkt() 33 34 35def test_from_proj4_json(): 36 json_str = '{"proj": "longlat", "ellps": "WGS84", "datum": "WGS84"}' 37 proj = CRS.from_string(json_str) 38 with pytest.warns(UserWarning): 39 assert proj.to_proj4(4) == "+proj=longlat +datum=WGS84 +no_defs +type=crs" 40 assert proj.to_proj4(5) == "+proj=longlat +datum=WGS84 +no_defs +type=crs" 41 # Test with invalid JSON code 42 with pytest.raises(CRSError): 43 assert CRS.from_string("{foo: bar}") 44 45 46def test_from_proj4(): 47 proj = CRS.from_proj4("+proj=longlat +datum=WGS84 +no_defs +type=crs") 48 with pytest.warns(UserWarning): 49 assert proj.to_proj4() == "+proj=longlat +datum=WGS84 +no_defs +type=crs" 50 51 52def test_from_proj4__invalid(): 53 # Test with invalid JSON code 54 with pytest.raises(CRSError): 55 assert CRS.from_proj4(CRS(3857).to_wkt()) 56 57 58def test_from_epsg(): 59 proj = CRS.from_epsg(4326) 60 assert proj.to_epsg() == 4326 61 62 # Test with invalid EPSG code 63 with pytest.raises(CRSError): 64 assert CRS.from_epsg(0) 65 66 67def test_from_epsg_string(): 68 proj = CRS.from_string("epsg:4326") 69 assert proj.to_epsg() == 4326 70 71 # Test with invalid EPSG code 72 with pytest.raises(CRSError): 73 assert CRS.from_string("epsg:xyz") 74 75 76def test_from_string(): 77 wgs84_crs = CRS.from_string("+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs") 78 with pytest.warns(UserWarning): 79 assert wgs84_crs.to_proj4() == "+proj=longlat +datum=WGS84 +no_defs +type=crs" 80 # Make sure this doesn't get handled using the from_epsg() 81 # even though 'epsg' is in the string 82 with pytest.warns(FutureWarning): 83 epsg_init_crs = CRS.from_string("+init=epsg:26911 +units=m +no_defs=True") 84 with pytest.warns(UserWarning): 85 assert ( 86 epsg_init_crs.to_proj4() 87 == "+proj=utm +zone=11 +datum=NAD83 +units=m +no_defs +type=crs" 88 ) 89 90 91def test_from_string__invalid(): 92 with pytest.raises(CRSError, match="CRS input is not a string"): 93 CRS.from_string(4326) 94 95 96def test_initialize_projparams_with_kwargs(): 97 crs_mixed_args = CRS("+proj=utm +zone=10", ellps="WGS84") 98 crs_positional = CRS("+proj=utm +zone=10 +ellps=WGS84") 99 assert crs_mixed_args.is_exact_same(crs_positional) 100 101 102def test_bare_parameters(): 103 """Make sure that bare parameters (e.g., no_defs) are handled properly, 104 even if they come in with key=True. This covers interaction with pyproj, 105 which makes presents bare parameters as key=<bool>.""" 106 107 # Example produced by pyproj 108 proj = CRS.from_string( 109 "+proj=lcc +lon_0=-95 +ellps=GRS80 +y_0=0 +no_defs=True " 110 "+x_0=0 +units=m +lat_2=77 +lat_1=49 +lat_0=0" 111 ) 112 with pytest.warns(UserWarning): 113 assert "+no_defs" in proj.to_proj4(4) 114 115 # TODO: THIS DOES NOT WORK 116 proj = CRS.from_string( 117 "+lon_0=-95 +ellps=GRS80 +proj=lcc +y_0=0 +no_defs=False " 118 "+x_0=0 +units=m +lat_2=77 +lat_1=49 +lat_0=0" 119 ) 120 # assert "+no_defs" not in proj.to_proj4(4) 121 122 123def test_is_geographic(): 124 assert CRS("EPSG:4326").is_geographic is True 125 assert CRS("EPSG:3857").is_geographic is False 126 127 wgs84_crs = CRS.from_string("+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs") 128 assert wgs84_crs.is_geographic is True 129 130 nad27_crs = CRS.from_string("+proj=longlat +ellps=clrk66 +datum=NAD27 +no_defs") 131 assert nad27_crs.is_geographic is True 132 133 lcc_crs = CRS.from_string( 134 "+lon_0=-95 +ellps=GRS80 +y_0=0 +no_defs=True +proj=lcc +x_0=0 " 135 "+units=m +lat_2=77 +lat_1=49 +lat_0=0" 136 ) 137 assert lcc_crs.is_geographic is False 138 139 140def test_is_projected(): 141 assert CRS("EPSG:3857").is_projected is True 142 143 lcc_crs = CRS.from_string( 144 "+lon_0=-95 +ellps=GRS80 +y_0=0 +no_defs=True +proj=lcc +x_0=0 " 145 "+units=m +lat_2=77 +lat_1=49 +lat_0=0" 146 ) 147 assert CRS.from_user_input(lcc_crs).is_projected is True 148 149 wgs84_crs = CRS.from_string("+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs") 150 assert CRS.from_user_input(wgs84_crs).is_projected is False 151 152 153def test_is_compound(): 154 assert CRS("EPSG:4326+5773").is_compound 155 assert not CRS("EPSG:4326").is_compound 156 157 158def test_is_same_crs(): 159 crs1 = CRS("urn:ogc:def:crs:OGC::CRS84") 160 crs2 = CRS("EPSG:3857") 161 162 assert crs1 == crs1 163 assert crs1 != crs2 164 165 wgs84_crs = CRS.from_string("+proj=longlat +ellps=WGS84 +datum=WGS84") 166 assert crs1 == wgs84_crs 167 168 # Make sure that same projection with different parameter are not equal 169 lcc_crs1 = CRS.from_string( 170 "+lon_0=-95 +ellps=GRS80 +y_0=0 +no_defs=True +proj=lcc " 171 "+x_0=0 +units=m +lat_2=77 +lat_1=49 +lat_0=0" 172 ) 173 lcc_crs2 = CRS.from_string( 174 "+lon_0=-95 +ellps=GRS80 +y_0=0 +no_defs=True +proj=lcc " 175 "+x_0=0 +units=m +lat_2=77 +lat_1=45 +lat_0=0" 176 ) 177 assert lcc_crs1 != lcc_crs2 178 179 180def test_to_proj4(): 181 with pytest.warns(UserWarning): 182 assert ( 183 CRS("EPSG:4326").to_proj4(4) 184 == "+proj=longlat +datum=WGS84 +no_defs +type=crs" 185 ) 186 187 188def test_empty_json(): 189 with pytest.raises(CRSError): 190 CRS.from_string("{}") 191 with pytest.raises(CRSError): 192 CRS.from_string("[]") 193 with pytest.raises(CRSError): 194 CRS.from_string("") 195 196 197def test_has_wkt_property(): 198 with pytest.warns(FutureWarning): 199 assert ( 200 CRS({"init": "EPSG:4326"}) 201 .to_wkt("WKT1_GDAL") 202 .startswith('GEOGCS["WGS 84",DATUM') 203 ) 204 205 206def test_to_wkt_pretty(): 207 crs = CRS.from_epsg(4326) 208 assert "\n" in crs.to_wkt(pretty=True) 209 assert "\n" not in crs.to_wkt() 210 211 212def test_repr(): 213 with pytest.warns(FutureWarning): 214 assert repr(CRS({"init": "EPSG:4326"})) == ( 215 "<Geographic 2D CRS: +init=epsg:4326 +type=crs>\n" 216 "Name: WGS 84\n" 217 "Axis Info [ellipsoidal]:\n" 218 "- lon[east]: Longitude (degree)\n" 219 "- lat[north]: Latitude (degree)\n" 220 "Area of Use:\n" 221 "- name: World.\n" 222 "- bounds: (-180.0, -90.0, 180.0, 90.0)\n" 223 f"Datum: {get_wgs84_datum_name()}\n" 224 "- Ellipsoid: WGS 84\n" 225 "- Prime Meridian: Greenwich\n" 226 ) 227 228 229def test_repr__long(): 230 with pytest.warns(FutureWarning): 231 if PROJ_GTE_8: 232 wkt_str = 'GEOGCRS["WGS 84",ENSEMBLE["World Geodetic System 1' 233 else: 234 wkt_str = 'GEOGCRS["WGS 84",DATUM["World Geodetic System 1984' 235 assert repr(CRS(CRS({"init": "EPSG:4326"}).to_wkt())) == ( 236 f"<Geographic 2D CRS: {wkt_str} ...>\n" 237 "Name: WGS 84\n" 238 "Axis Info [ellipsoidal]:\n" 239 "- lon[east]: Longitude (degree)\n" 240 "- lat[north]: Latitude (degree)\n" 241 "Area of Use:\n" 242 "- name: World.\n" 243 "- bounds: (-180.0, -90.0, 180.0, 90.0)\n" 244 f"Datum: {get_wgs84_datum_name()}\n" 245 "- Ellipsoid: WGS 84\n" 246 "- Prime Meridian: Greenwich\n" 247 ) 248 249 250def test_repr_epsg(): 251 assert repr(CRS(CRS("EPSG:4326").to_wkt())) == ( 252 "<Geographic 2D CRS: EPSG:4326>\n" 253 "Name: WGS 84\n" 254 "Axis Info [ellipsoidal]:\n" 255 "- Lat[north]: Geodetic latitude (degree)\n" 256 "- Lon[east]: Geodetic longitude (degree)\n" 257 "Area of Use:\n" 258 "- name: World.\n" 259 "- bounds: (-180.0, -90.0, 180.0, 90.0)\n" 260 f"Datum: {get_wgs84_datum_name()}\n" 261 "- Ellipsoid: WGS 84\n" 262 "- Prime Meridian: Greenwich\n" 263 ) 264 265 266def test_repr__undefined(): 267 assert repr( 268 CRS( 269 "+proj=merc +a=6378137.0 +b=6378137.0 +nadgrids=@null" 270 " +lon_0=0.0 +x_0=0.0 +y_0=0.0 +units=m +no_defs" 271 ) 272 ) == ( 273 "<Bound CRS: +proj=merc +a=6378137.0 +b=6378137.0 +nadgrids=@nu ...>\n" 274 "Name: unknown\n" 275 "Axis Info [cartesian]:\n" 276 "- E[east]: Easting (metre)\n" 277 "- N[north]: Northing (metre)\n" 278 "Area of Use:\n" 279 "- undefined\n" 280 "Coordinate Operation:\n" 281 "- name: unknown to WGS84\n" 282 "- method: NTv2\n" 283 "Datum: unknown\n" 284 "- Ellipsoid: unknown\n" 285 "- Prime Meridian: Greenwich\n" 286 "Source CRS: unknown\n" 287 ) 288 289 290def test_repr_compound(): 291 assert repr(CRS.from_epsg(3901)) == ( 292 "<Compound CRS: EPSG:3901>\n" 293 "Name: KKJ / Finland Uniform Coordinate System + N60 height\n" 294 "Axis Info [cartesian|vertical]:\n" 295 "- X[north]: Northing (metre)\n" 296 "- Y[east]: Easting (metre)\n" 297 "- H[up]: Gravity-related height (metre)\n" 298 "Area of Use:\n" 299 "- name: Finland - onshore.\n" 300 "- bounds: (19.24, 59.75, 31.59, 70.09)\n" 301 "Datum: Kartastokoordinaattijarjestelma (1966)\n" 302 "- Ellipsoid: International 1924\n" 303 "- Prime Meridian: Greenwich\n" 304 "Sub CRS:\n" 305 "- KKJ / Finland Uniform Coordinate System\n" 306 "- N60 height\n" 307 ) 308 309 310def test_axis_info_compound(): 311 assert [axis.direction for axis in CRS.from_epsg(3901).axis_info] == [ 312 "north", 313 "east", 314 "up", 315 ] 316 317 318def test_dunder_str(): 319 with pytest.warns(FutureWarning): 320 assert str(CRS({"init": "EPSG:4326"})) == CRS({"init": "EPSG:4326"}).srs 321 322 323def test_epsg(): 324 with pytest.warns(FutureWarning): 325 assert CRS({"init": "EPSG:4326"}).to_epsg(20) == 4326 326 assert CRS({"init": "EPSG:4326"}).to_epsg() is None 327 assert CRS.from_user_input(4326).to_epsg() == 4326 328 assert CRS.from_epsg(4326).to_epsg() == 4326 329 assert CRS.from_user_input("epsg:4326").to_epsg() == 4326 330 331 332def test_datum(): 333 datum = CRS.from_epsg(4326).datum 334 assert "\n" in repr(datum) 335 if PROJ_GTE_8: 336 datum_wkt = 'ENSEMBLE["World Geodetic System 1984 ensemble"' 337 else: 338 datum_wkt = 'DATUM["World Geodetic System 1984"' 339 assert repr(datum).startswith(datum_wkt) 340 assert datum.to_wkt().startswith(datum_wkt) 341 assert datum == datum 342 assert datum.is_exact_same(datum) 343 344 345def test_datum_horizontal(): 346 assert CRS.from_epsg(5972).datum == CRS.from_epsg(25832).datum 347 348 349def test_datum_unknown(): 350 crs = CRS( 351 "+proj=omerc +lat_0=-36.10360962430914 " 352 "+lonc=147.06322917270154 +alpha=-54.786229796129035 " 353 "+k=1 +x_0=0 +y_0=0 +gamma=0 +ellps=WGS84 " 354 "+towgs84=0,0,0,0,0,0,0 +units=m +no_defs" 355 ) 356 assert crs.datum.name == "Unknown based on WGS84 ellipsoid" 357 358 359def test_epsg__not_found(): 360 assert CRS("+proj=longlat +datum=WGS84 +no_defs +towgs84=0,0,0").to_epsg(0) is None 361 assert ( 362 CRS.from_string("+proj=longlat +datum=WGS84 +no_defs +towgs84=0,0,0").to_epsg() 363 is None 364 ) 365 366 367def test_epsg__no_code_available(): 368 lcc_crs = CRS.from_string( 369 "+lon_0=-95 +ellps=GRS80 +y_0=0 +no_defs=True +proj=lcc " 370 "+x_0=0 +units=m +lat_2=77 +lat_1=49 +lat_0=0" 371 ) 372 assert lcc_crs.to_epsg() is None 373 374 375def test_crs_OSR_equivalence(): 376 crs1 = CRS.from_string("+proj=longlat +datum=WGS84 +no_defs") 377 crs2 = CRS.from_string("+proj=latlong +datum=WGS84 +no_defs") 378 with pytest.warns(FutureWarning): 379 crs3 = CRS({"init": "EPSG:4326"}) 380 assert crs1 == crs2 381 # these are not equivalent in proj.4 now as one uses degrees and the othe radians 382 assert crs1 == crs3 383 384 385def test_crs_OSR_no_equivalence(): 386 crs1 = CRS.from_string("+proj=longlat +datum=WGS84 +no_defs") 387 crs2 = CRS.from_string("+proj=longlat +datum=NAD27 +no_defs") 388 assert crs1 != crs2 389 390 391def test_init_from_wkt(): 392 wgs84 = CRS.from_string("+proj=longlat +datum=WGS84 +no_defs") 393 from_wkt = CRS(wgs84.to_wkt()) 394 assert wgs84.to_wkt() == from_wkt.to_wkt() 395 396 397def test_init_from_wkt_invalid(): 398 with pytest.raises(CRSError): 399 CRS("trash-54322") 400 with pytest.raises(CRSError): 401 CRS("") 402 403 404def test_from_wkt(): 405 wgs84 = CRS.from_string("+proj=longlat +datum=WGS84 +no_defs") 406 from_wkt = CRS.from_wkt(wgs84.to_wkt()) 407 assert wgs84.to_wkt() == from_wkt.to_wkt() 408 409 410def test_from_wkt_invalid(): 411 with pytest.raises(CRSError), pytest.warns(UserWarning): 412 CRS.from_wkt(CRS(4326).to_proj4()) 413 414 415def test_from_user_input_epsg(): 416 with pytest.warns(UserWarning): 417 assert "+proj=longlat" in CRS.from_user_input("EPSG:4326").to_proj4(4) 418 419 420def test_from_esri_wkt(): 421 projection_string = ( 422 'PROJCS["USA_Contiguous_Albers_Equal_Area_Conic_USGS_version",' 423 'GEOGCS["GCS_North_American_1983",DATUM["D_North_American_1983",' 424 'SPHEROID["GRS_1980",6378137.0,298.257222101]],' 425 'PRIMEM["Greenwich",0.0],' 426 'UNIT["Degree",0.0174532925199433]],' 427 'PROJECTION["Albers"],' 428 'PARAMETER["false_easting",0.0],' 429 'PARAMETER["false_northing",0.0],' 430 'PARAMETER["central_meridian",-96.0],' 431 'PARAMETER["standard_parallel_1",29.5],' 432 'PARAMETER["standard_parallel_2",45.5],' 433 'PARAMETER["latitude_of_origin",23.0],' 434 'UNIT["Meter",1.0],' 435 'VERTCS["NAVD_1988",' 436 'VDATUM["North_American_Vertical_Datum_1988"],' 437 'PARAMETER["Vertical_Shift",0.0],' 438 'PARAMETER["Direction",1.0],UNIT["Centimeter",0.01]]]' 439 ) 440 proj_crs_str = CRS.from_string(projection_string) 441 proj_crs_wkt = CRS(projection_string) 442 with pytest.warns(UserWarning): 443 assert proj_crs_str.to_proj4() == proj_crs_wkt.to_proj4() 444 assert proj_crs_str.to_proj4(4) == ( 445 "+proj=aea +lat_0=23 +lon_0=-96 +lat_1=29.5 " 446 "+lat_2=45.5 +x_0=0 +y_0=0 +datum=NAD83 +units=m +no_defs +type=crs" 447 ) 448 449 450def test_compound_crs(): 451 wkt = """COMPD_CS["unknown",GEOGCS["WGS 84",DATUM["WGS_1984", 452 SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]], 453 TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG","6326"]], 454 PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433], 455 AUTHORITY["EPSG","4326"]],VERT_CS["unknown", 456 VERT_DATUM["unknown",2005],UNIT["metre",1.0, 457 AUTHORITY["EPSG","9001"]],AXIS["Up",UP]]]""" 458 assert CRS(wkt).to_wkt("WKT1_GDAL").startswith('COMPD_CS["unknown",GEOGCS["WGS 84"') 459 460 461def test_ellipsoid(): 462 crs1 = CRS.from_epsg(4326) 463 assert f"{crs1.ellipsoid.inverse_flattening:.3f}" == "298.257" 464 assert f"{crs1.ellipsoid.semi_major_metre:.3f}" == "6378137.000" 465 assert f"{crs1.ellipsoid.semi_minor_metre:.3f}" == "6356752.314" 466 467 468def test_ellipsoid__semi_minor_not_computed(): 469 cc = CRS("+proj=geos +lon_0=-89.5 +a=6378137.0 +b=6356752.31 h=12345") 470 assert cc.datum.ellipsoid.semi_minor_metre == 6356752.31 471 assert cc.datum.ellipsoid.semi_major_metre == 6378137.0 472 assert not cc.datum.ellipsoid.is_semi_minor_computed 473 474 475def test_area_of_use(): 476 crs1 = CRS.from_epsg(4326) 477 assert crs1.area_of_use.bounds == (-180.0, -90.0, 180.0, 90.0) 478 assert crs1.area_of_use.name == "World." 479 480 481def test_from_user_input_custom_crs_class(): 482 assert CRS.from_user_input(CustomCRS()) == CRS.from_epsg(4326) 483 484 485def test_non_crs_error(): 486 with pytest.raises(CRSError, match="Input is not a CRS"): 487 CRS( 488 "+proj=pipeline +ellps=GRS80 +step +proj=merc " 489 "+step +proj=axisswap +order=2,1" 490 ) 491 492 493def test_sub_crs(): 494 crs = CRS.from_epsg(5972) 495 sub_crs_list = crs.sub_crs_list 496 assert len(sub_crs_list) == 2 497 assert sub_crs_list[0] == CRS.from_epsg(25832) 498 assert sub_crs_list[1] == CRS.from_epsg(5941) 499 assert crs.is_projected 500 assert crs.is_vertical 501 assert not crs.is_geographic 502 503 504def test_sub_crs__none(): 505 assert CRS.from_epsg(4326).sub_crs_list == [] 506 507 508def test_coordinate_system(): 509 crs = CRS.from_epsg(26915) 510 assert repr(crs.coordinate_system).startswith("CS[Cartesian") 511 assert crs.coordinate_system.name == "cartesian" 512 assert crs.coordinate_system.name == str(crs.coordinate_system) 513 assert crs.coordinate_system.axis_list == crs.axis_info 514 assert len(crs.coordinate_system.axis_list) == 2 515 516 517def test_coordinate_system_geog(): 518 crs = CRS.from_epsg(4326) 519 assert repr(crs.coordinate_system).startswith("CS[ellipsoidal") 520 assert crs.coordinate_system.name == "ellipsoidal" 521 assert crs.coordinate_system.name == str(crs.coordinate_system) 522 assert crs.coordinate_system.axis_list == crs.axis_info 523 assert repr(crs.coordinate_system.axis_list) == ( 524 "[Axis(name=Geodetic latitude, abbrev=Lat, direction=north, " 525 "unit_auth_code=EPSG, unit_code=9122, unit_name=degree), " 526 "Axis(name=Geodetic longitude, abbrev=Lon, direction=east, " 527 "unit_auth_code=EPSG, unit_code=9122, unit_name=degree)]" 528 ) 529 530 531def test_coordinate_operation(): 532 crs = CRS.from_epsg(26915) 533 assert repr(crs.coordinate_operation) == ( 534 "<Coordinate Operation: Conversion>\n" 535 "Name: UTM zone 15N\n" 536 "Method: Transverse Mercator\n" 537 "Area of Use:\n" 538 "- name: Between 96°W and 90°W, northern hemisphere between equator and 84°N, " 539 "onshore and offshore.\n" 540 "- bounds: (-96.0, 0.0, -90.0, 84.0)" 541 ) 542 assert crs.coordinate_operation.method_name == "Transverse Mercator" 543 assert crs.coordinate_operation.name == str(crs.coordinate_operation) 544 assert crs.coordinate_operation.method_auth_name == "EPSG" 545 assert crs.coordinate_operation.method_code == "9807" 546 assert crs.coordinate_operation.is_instantiable == 1 547 assert crs.coordinate_operation.has_ballpark_transformation == 0 548 assert crs.coordinate_operation.accuracy == -1.0 549 assert repr(crs.coordinate_operation.params) == ( 550 "[Param(name=Latitude of natural origin, auth_name=EPSG, code=8801, " 551 "value=0.0, unit_name=degree, unit_auth_name=EPSG, " 552 "unit_code=9102, unit_category=angular), " 553 "Param(name=Longitude of natural origin, auth_name=EPSG, code=8802, " 554 "value=-93.0, unit_name=degree, unit_auth_name=EPSG, " 555 "unit_code=9102, unit_category=angular), " 556 "Param(name=Scale factor at natural origin, auth_name=EPSG, code=8805, " 557 "value=0.9996, unit_name=unity, unit_auth_name=EPSG, " 558 "unit_code=9201, unit_category=scale), " 559 "Param(name=False easting, auth_name=EPSG, code=8806, value=500000.0, " 560 "unit_name=metre, unit_auth_name=EPSG, unit_code=9001, unit_category=linear), " 561 "Param(name=False northing, auth_name=EPSG, code=8807, value=0.0, " 562 "unit_name=metre, unit_auth_name=EPSG, unit_code=9001, unit_category=linear)]" 563 ) 564 assert crs.coordinate_operation.grids == [] 565 566 567def test_coordinate_operation_grids(): 568 cc = CoordinateOperation.from_epsg(1312) 569 if not cc.grids[0].full_name: 570 assert ( 571 repr(cc.grids) 572 == "[Grid(short_name=NTv1_0.gsb, full_name=, package_name=, url=, " 573 "direct_download=False, open_license=False, available=False)]" 574 ) 575 else: 576 assert ( 577 repr(cc.grids) 578 == "[Grid(short_name=NTv1_0.gsb, full_name=NTv1_0.gsb, package_name=, " 579 "url=, direct_download=False, open_license=False, available=False)]" 580 ) 581 582 583@pytest.mark.grid 584def test_coordinate_operation_grids__alternative_grid_name(): 585 cc = CoordinateOperation.from_epsg(1312, True) 586 assert len(cc.grids) == 1 587 grid = cc.grids[0] 588 assert grid.direct_download is True 589 assert grid.open_license is True 590 assert grid.short_name == "ca_nrc_ntv1_can.tif" 591 if grids_available(grid.short_name): 592 assert grid.available is True 593 assert grid.full_name.endswith(grid.short_name) 594 else: 595 assert grid.available is False 596 assert grid.full_name == "" 597 assert grid.package_name == "" 598 assert grid.url == "https://cdn.proj.org/ca_nrc_ntv1_can.tif" 599 600 601def test_coordinate_operation__missing(): 602 crs = CRS.from_epsg(4326) 603 assert crs.coordinate_operation is None 604 605 606def test_coordinate_operation__from_epsg(): 607 cc = CoordinateOperation.from_epsg(16031) 608 assert cc.method_auth_name == "EPSG" 609 assert cc.method_code == "9807" 610 611 612def test_coordinate_operation__from_authority(): 613 cc = CoordinateOperation.from_authority("EPSG", 16031) 614 assert cc.method_auth_name == "EPSG" 615 assert cc.method_code == "9807" 616 617 618@pytest.mark.parametrize( 619 "user_input", 620 [ 621 1671, 622 ("EPSG", 1671), 623 "urn:ogc:def:coordinateOperation:EPSG::1671", 624 CoordinateOperation.from_epsg(1671), 625 CoordinateOperation.from_epsg(1671).to_json_dict(), 626 "RGF93 to WGS 84 (1)", 627 ], 628) 629def test_coordinate_operation__from_user_input(user_input): 630 assert CoordinateOperation.from_user_input( 631 user_input 632 ) == CoordinateOperation.from_epsg(1671) 633 634 635def test_coordinate_operation__from_user_input__invalid(): 636 with pytest.raises(CRSError, match="Invalid coordinate operation"): 637 CoordinateOperation.from_user_input({}) 638 639 640def test_coordinate_operation__from_epsg__empty(): 641 with pytest.raises(CRSError, match="Invalid authority"): 642 CoordinateOperation.from_epsg(1) 643 644 645def test_coordinate_operation__from_authority__empty(): 646 with pytest.raises(CRSError, match="Invalid authority"): 647 CoordinateOperation.from_authority("BOB", 4326) 648 649 650def test_datum__from_epsg(): 651 if PROJ_GTE_8: 652 datum_wkt = ( 653 'ENSEMBLE["World Geodetic System 1984 ensemble",' 654 'MEMBER["World Geodetic System 1984 (Transit)",' 655 'ID["EPSG",1166]],MEMBER["World Geodetic System 1984 (G730)",' 656 'ID["EPSG",1152]],MEMBER["World Geodetic System 1984 (G873)",' 657 'ID["EPSG",1153]],MEMBER["World Geodetic System 1984 (G1150)",' 658 'ID["EPSG",1154]],MEMBER["World Geodetic System 1984 (G1674)",' 659 'ID["EPSG",1155]],MEMBER["World Geodetic System 1984 (G1762)",' 660 'ID["EPSG",1156]],ELLIPSOID["WGS 84",6378137,298.257223563,' 661 'LENGTHUNIT["metre",1],ID["EPSG",7030]],' 662 'ENSEMBLEACCURACY[2.0],ID["EPSG",6326]]' 663 ) 664 else: 665 datum_wkt = ( 666 'DATUM["World Geodetic System 1984",' 667 'ELLIPSOID["WGS 84",6378137,298.257223563,' 668 'LENGTHUNIT["metre",1]],ID["EPSG",6326]]' 669 ) 670 assert Datum.from_epsg("6326").to_wkt() == datum_wkt 671 672 673def test_datum__from_authority(): 674 dt = Datum.from_authority("EPSG", 6326) 675 assert dt.name == get_wgs84_datum_name() 676 677 678def test_datum__from_epsg__invalid(): 679 with pytest.raises(CRSError, match="Invalid authority"): 680 Datum.from_epsg(1) 681 682 683def test_datum__from_authority__invalid(): 684 with pytest.raises(CRSError, match="Invalid authority"): 685 Datum.from_authority("BOB", 1) 686 687 688@pytest.mark.parametrize( 689 "user_input", 690 [ 691 6326, 692 ("EPSG", 6326), 693 "urn:ogc:def:ensemble:EPSG::6326" 694 if PROJ_GTE_8 695 else "urn:ogc:def:datum:EPSG::6326", 696 Datum.from_epsg(6326), 697 Datum.from_epsg(6326).to_json_dict(), 698 "World Geodetic System 1984", 699 ], 700) 701def test_datum__from_user_input(user_input): 702 assert Datum.from_user_input(user_input) == Datum.from_epsg(6326) 703 704 705def test_datum__from_user_input__invalid(): 706 with pytest.raises(CRSError, match="Invalid datum"): 707 Datum.from_user_input({}) 708 709 710def test_prime_meridian__from_epsg(): 711 assert PrimeMeridian.from_epsg(8903).to_wkt() == ( 712 'PRIMEM["Paris",2.5969213,ANGLEUNIT["grad",0.0157079632679489],ID["EPSG",8903]]' 713 ) 714 715 716def test_prime_meridian__from_authority(): 717 assert PrimeMeridian.from_authority("EPSG", 8903).name == "Paris" 718 719 720def test_prime_meridian__from_epsg__invalid(): 721 with pytest.raises(CRSError, match="Invalid authority"): 722 PrimeMeridian.from_epsg(1) 723 724 725def test_prime_meridian__from_authority__invalid(): 726 with pytest.raises(CRSError, match="Invalid authority"): 727 PrimeMeridian.from_authority("Bob", 1) 728 729 730@pytest.mark.parametrize( 731 "user_input", 732 [ 733 8901, 734 ("EPSG", 8901), 735 "urn:ogc:def:meridian:EPSG::8901", 736 PrimeMeridian.from_epsg(8901), 737 PrimeMeridian.from_epsg(8901).to_json_dict(), 738 "Greenwich", 739 ], 740) 741def test_prime_meridian__from_user_input(user_input): 742 assert PrimeMeridian.from_user_input(user_input) == PrimeMeridian.from_epsg(8901) 743 744 745def test_prime_meridian__from_user_input__invalid(): 746 with pytest.raises(CRSError, match="Invalid prime meridian"): 747 PrimeMeridian.from_user_input({}) 748 749 750def test_ellipsoid__from_epsg(): 751 assert Ellipsoid.from_epsg(7030).to_wkt() == ( 752 'ELLIPSOID["WGS 84",6378137,298.257223563,' 753 'LENGTHUNIT["metre",1],ID["EPSG",7030]]' 754 ) 755 756 757def test_ellipsoid__from_authority(): 758 assert Ellipsoid.from_authority("EPSG", 7030).name == "WGS 84" 759 760 761def test_ellipsoid__from_epsg__invalid(): 762 with pytest.raises(CRSError, match="Invalid authority"): 763 Ellipsoid.from_epsg(1) 764 765 766def test_ellipsoid__from_authority__invalid(): 767 with pytest.raises(CRSError, match="Invalid authority"): 768 Ellipsoid.from_authority("BOB", 1) 769 770 771@pytest.mark.parametrize( 772 "user_input", 773 [ 774 7001, 775 ("EPSG", 7001), 776 "urn:ogc:def:ellipsoid:EPSG::7001", 777 Ellipsoid.from_epsg(7001), 778 Ellipsoid.from_epsg(7001).to_json_dict(), 779 "Airy 1830", 780 ], 781) 782def test_ellipsoid__from_user_input(user_input): 783 assert Ellipsoid.from_user_input(user_input) == Ellipsoid.from_epsg(7001) 784 785 786def test_ellipsoid__from_user_input__invalid(): 787 with pytest.raises(CRSError, match="Invalid ellipsoid"): 788 Ellipsoid.from_user_input({}) 789 790 791CS_JSON_DICT = { 792 "$schema": "https://proj.org/schemas/v0.2/projjson.schema.json", 793 "type": "CoordinateSystem", 794 "subtype": "Cartesian", 795 "axis": [ 796 {"name": "Easting", "abbreviation": "E", "direction": "east", "unit": "metre"}, 797 { 798 "name": "Northing", 799 "abbreviation": "N", 800 "direction": "north", 801 "unit": "metre", 802 }, 803 ], 804} 805 806 807@pytest.mark.parametrize( 808 "user_input", 809 [ 810 CS_JSON_DICT, 811 json.dumps(CS_JSON_DICT), 812 CoordinateSystem.from_json_dict(CS_JSON_DICT), 813 ], 814) 815def test_coordinate_system__from_user_input(user_input): 816 assert CoordinateSystem.from_user_input( 817 user_input 818 ) == CoordinateSystem.from_json_dict(CS_JSON_DICT) 819 820 821@pytest.mark.parametrize( 822 "user_input", 823 [ 824 7001, 825 ("EPSG", 7001), 826 "urn:ogc:def:ellipsoid:EPSG::7001", 827 Ellipsoid.from_epsg(7001), 828 Ellipsoid.from_epsg(7001).to_json_dict(), 829 ], 830) 831def test_coordinate_system__from_user_input__invalid(user_input): 832 with pytest.raises(CRSError, match="Invalid"): 833 CoordinateSystem.from_user_input(user_input) 834 835 836def test_bound_crs_is_geographic(): 837 assert CRS( 838 "proj=longlat datum=WGS84 no_defs ellps=WGS84 towgs84=0,0,0" 839 ).is_geographic 840 841 842def test_coordinate_operation_towgs84_three(): 843 crs = CRS("+proj=latlong +ellps=GRS80 +towgs84=-199.87,74.79,246.62") 844 assert crs.coordinate_operation.towgs84 == [-199.87, 74.79, 246.62] 845 846 847def test_coordinate_operation_towgs84_seven(): 848 crs = CRS( 849 "+proj=tmerc +lat_0=0 +lon_0=15 +k=0.9996 +x_0=2520000 +y_0=0 " 850 "+ellps=intl +towgs84=-122.74,-34.27,-22.83,-1.884,-3.400,-3.030,-15.62" 851 ) 852 assert crs.coordinate_operation.towgs84 == [ 853 -122.74, 854 -34.27, 855 -22.83, 856 -1.884, 857 -3.4, 858 -3.03, 859 -15.62, 860 ] 861 862 863def test_axis_info_bound(): 864 crs = CRS( 865 "+proj=tmerc +lat_0=0 +lon_0=15 +k=0.9996 +x_0=2520000 +y_0=0 " 866 "+ellps=intl +towgs84=-122.74,-34.27,-22.83,-1.884,-3.400,-3.030,-15.62" 867 ) 868 assert [axis.direction for axis in crs.axis_info] == ["east", "north"] 869 870 871def test_coordinate_operation_towgs84_missing(): 872 crs = CRS("epsg:3004") 873 assert crs.coordinate_operation.towgs84 == [] 874 875 876@pytest.mark.parametrize( 877 "wkt_version_str, wkt_version_enum", 878 [ 879 ("WKT1_GDAL", WktVersion.WKT1_GDAL), 880 ("WKT2_2018", WktVersion.WKT2_2018), 881 ("WKT2_2018_SIMPLIFIED", WktVersion.WKT2_2018_SIMPLIFIED), 882 ("WKT2_2019", WktVersion.WKT2_2019), 883 ("WKT2_2019_SIMPLIFIED", WktVersion.WKT2_2019_SIMPLIFIED), 884 ("WKT2_2015", WktVersion.WKT2_2015), 885 ("WKT2_2015_SIMPLIFIED", WktVersion.WKT2_2015_SIMPLIFIED), 886 ], 887) 888def test_to_wkt_enum(wkt_version_str, wkt_version_enum): 889 crs = CRS.from_epsg(4326) 890 assert crs.to_wkt(wkt_version_str) == crs.to_wkt(wkt_version_enum) 891 892 893def test_to_wkt_enum__invalid(): 894 crs = CRS.from_epsg(4326) 895 with pytest.raises(ValueError, match="Invalid value"): 896 crs.to_wkt("WKT_INVALID") 897 898 899def test_to_proj4_enum(): 900 crs = CRS.from_epsg(4326) 901 with pytest.warns(UserWarning): 902 assert crs.to_proj4(4) == crs.to_proj4(ProjVersion.PROJ_4) 903 assert crs.to_proj4(5) == crs.to_proj4(ProjVersion.PROJ_5) 904 905 906def test_datum_equals(): 907 datum = Datum.from_epsg(6326) 908 assert datum == 6326 909 assert not datum != 6326 910 assert datum != "invalid" 911 912 913@pytest.mark.parametrize( 914 "input_str", 915 [ 916 "urn:ogc:def:ensemble:EPSG::6326" 917 if PROJ_GTE_8 918 else "urn:ogc:def:datum:EPSG::6326", 919 "World Geodetic System 1984", 920 ], 921) 922def test_datum__from_string(input_str): 923 dd = Datum.from_string(input_str) 924 if PROJ_GTE_8: 925 assert dd.name == "World Geodetic System 1984 ensemble" 926 assert dd.type_name == "Datum Ensemble" 927 else: 928 assert dd.name == "World Geodetic System 1984" 929 assert dd.type_name == "Geodetic Reference Frame" 930 931 932@pytest.mark.parametrize( 933 "input_str, type_name", 934 [ 935 ('ENGINEERINGDATUM["Engineering datum"]', "Engineering Datum"), 936 ('PDATUM["Mean Sea Level",ANCHOR["1013.25 hPa at 15°C"]]', "Parametric Datum"), 937 ( 938 'TDATUM["Gregorian calendar",CALENDAR["proleptic Gregorian"],' 939 "TIMEORIGIN[0000-01-01]]", 940 "Temporal Datum", 941 ), 942 ], 943) 944def test_datum__from_string__type_name(input_str, type_name): 945 dd = Datum.from_string(input_str) 946 assert dd.type_name == type_name 947 948 949@pytest.mark.parametrize( 950 "input_name", ["World Geodetic System 1984", "WGS84", "WGS 84"] 951) 952def test_datum__from_name(input_name): 953 dd = Datum.from_name(input_name) 954 assert dd.name == get_wgs84_datum_name() 955 956 957@pytest.mark.parametrize("auth_name", [None, "ESRI"]) 958def test_datum_from_name__auth_type(auth_name): 959 dd = Datum.from_name( 960 "WGS_1984_Geoid", 961 auth_name=auth_name, 962 datum_type=DatumType.VERTICAL_REFERENCE_FRAME, 963 ) 964 assert dd.name == "WGS_1984_Geoid" 965 assert dd.type_name == "Vertical Reference Frame" 966 967 968def test_datum_from_name__any_type(): 969 dd = Datum.from_name("WGS_1984_Geoid") 970 assert dd.name == "WGS_1984_Geoid" 971 assert dd.type_name == "Vertical Reference Frame" 972 973 974@pytest.mark.parametrize( 975 "invalid_str", ["3-598y5-98y", "urn:ogc:def:ellipsoid:EPSG::7001"] 976) 977def test_datum__from_name__invalid(invalid_str): 978 with pytest.raises(CRSError, match="Invalid datum name:"): 979 Datum.from_name(invalid_str) 980 981 982def test_datum__from_name__invalid_type(): 983 with pytest.raises(CRSError, match="Invalid datum name: WGS84"): 984 Datum.from_name("WGS84", datum_type="VERTICAL_REFERENCE_FRAME") 985 986 987@pytest.mark.parametrize( 988 "invalid_str", ["3-598y5-98y", "urn:ogc:def:ellipsoid:EPSG::7001"] 989) 990def test_datum__from_string__invalid(invalid_str): 991 with pytest.raises(CRSError, match="Invalid datum string"): 992 Datum.from_string(invalid_str) 993 994 995def test_ellipsoid_equals(): 996 ellipsoid = Ellipsoid.from_epsg(7001) 997 assert ellipsoid == 7001 998 assert not ellipsoid != 7001 999 assert ellipsoid != "invalid" 1000 1001 1002@pytest.mark.parametrize("input_str", ["urn:ogc:def:ellipsoid:EPSG::7001", "Airy 1830"]) 1003def test_ellipsoid__from_string(input_str): 1004 ee = Ellipsoid.from_string(input_str) 1005 assert ee.name == "Airy 1830" 1006 1007 1008@pytest.mark.parametrize( 1009 "input_str,long_name", 1010 [ 1011 ("Airy 1830", "Airy 1830"), 1012 ("intl", HAYFORD_ELLIPSOID_NAME), 1013 (HAYFORD_ELLIPSOID_NAME, HAYFORD_ELLIPSOID_NAME), 1014 ], 1015) 1016def test_ellipsoid__from_name(input_str, long_name): 1017 ee = Ellipsoid.from_name(input_str) 1018 assert ee.name == long_name 1019 1020 1021@pytest.mark.parametrize("invalid_str", ["3-598y5-98y", "urn:ogc:def:datum:EPSG::6326"]) 1022def test_ellipsoid__from_name__invalid(invalid_str): 1023 with pytest.raises(CRSError, match="Invalid ellipsoid name"): 1024 Ellipsoid.from_name(invalid_str) 1025 1026 1027def test_ellipsoid__from_name__invalid__auth(): 1028 with pytest.raises(CRSError, match="Invalid ellipsoid name"): 1029 Ellipsoid.from_name("intl", auth_name="ESRI") 1030 1031 1032@pytest.mark.parametrize("invalid_str", ["3-598y5-98y", "urn:ogc:def:datum:EPSG::6326"]) 1033def test_ellipsoid__from_string__invalid(invalid_str): 1034 with pytest.raises(CRSError, match="Invalid ellipsoid string"): 1035 Ellipsoid.from_string(invalid_str) 1036 1037 1038def test_prime_meridian_equals(): 1039 pm = PrimeMeridian.from_epsg(8901) 1040 assert pm == 8901 1041 assert not pm != 8901 1042 assert pm != "invalid" 1043 1044 1045@pytest.mark.parametrize("input_str", ["urn:ogc:def:meridian:EPSG::8901", "Greenwich"]) 1046def test_prime_meridian__from_string(input_str): 1047 pm = PrimeMeridian.from_string(input_str) 1048 assert pm.name == "Greenwich" 1049 1050 1051@pytest.mark.parametrize("invalid_str", ["3-598y5-98y", "urn:ogc:def:datum:EPSG::6326"]) 1052def test_prime_meridian__from_string__invalid(invalid_str): 1053 with pytest.raises(CRSError, match="Invalid prime meridian string"): 1054 PrimeMeridian.from_string(invalid_str) 1055 1056 1057def test_prime_meridian__from_name(): 1058 pm = PrimeMeridian.from_name("Greenwich") 1059 assert pm.name == "Greenwich" 1060 1061 1062@pytest.mark.parametrize("invalid_str", ["3-598y5-98y", "urn:ogc:def:datum:EPSG::6326"]) 1063def test_prime_meridian__from_name__invalid(invalid_str): 1064 with pytest.raises(CRSError, match="Invalid prime meridian name"): 1065 PrimeMeridian.from_name(invalid_str) 1066 1067 1068def test_coordinate_operation_equals(): 1069 co = CoordinateOperation.from_epsg(1671) 1070 assert co == 1671 1071 assert not co != 1671 1072 assert co != "invalid" 1073 1074 1075@pytest.mark.parametrize( 1076 "input_str", ["urn:ogc:def:coordinateOperation:EPSG::1671", "RGF93 to WGS 84 (1)"] 1077) 1078def test_coordinate_operation__from_string(input_str): 1079 co = CoordinateOperation.from_string(input_str) 1080 assert co.name == "RGF93 to WGS 84 (1)" 1081 1082 1083def test_coordinate_operation__from_name(): 1084 co = CoordinateOperation.from_name("UTM zone 12N") 1085 assert co.name == "UTM zone 12N" 1086 1087 1088def test_coordinate_operation__from_name_auth_type(): 1089 co = CoordinateOperation.from_name( 1090 "ITRF_2000_To_WGS_1984", 1091 auth_name="ESRI", 1092 coordinate_operation_type=CoordinateOperationType.TRANSFORMATION, 1093 ) 1094 assert co.name == "ITRF_2000_To_WGS_1984" 1095 1096 1097@pytest.mark.parametrize("invalid_str", ["3-598y5-98y", "urn:ogc:def:datum:EPSG::6326"]) 1098def test_coordinate_operation__from_name__invalid(invalid_str): 1099 with pytest.raises(CRSError, match="Invalid coordinate operation name"): 1100 CoordinateOperation.from_name(invalid_str) 1101 1102 1103@pytest.mark.parametrize("invalid_str", ["3-598y5-98y", "urn:ogc:def:datum:EPSG::6326"]) 1104def test_coordinate_operation__from_string__invalid(invalid_str): 1105 with pytest.raises(CRSError, match="Invalid coordinate operation string"): 1106 CoordinateOperation.from_string(invalid_str) 1107 1108 1109_COORDINATE_SYSTEM_STR = ( 1110 '{"$schema":"https://proj.org/schemas/v0.2/projjson.schema.json",' 1111 '"type":"CoordinateSystem","subtype":"ellipsoidal",' 1112 '"axis":[{"name":"Geodetic latitude","abbreviation":"Lat",' 1113 '"direction":"north","unit":"degree"},' 1114 '{"name":"Geodetic longitude","abbreviation":"Lon",' 1115 '"direction":"east","unit":"degree"}],' 1116 '"id":{"authority":"EPSG","code":6422}}' 1117) 1118 1119 1120def test_coordinate_system__equals(): 1121 cs = CoordinateSystem.from_string(_COORDINATE_SYSTEM_STR) 1122 assert cs == _COORDINATE_SYSTEM_STR 1123 assert not cs != _COORDINATE_SYSTEM_STR 1124 assert cs != "invalid" 1125 1126 1127def test_coordinate_system__from_string(): 1128 cs = CoordinateSystem.from_string(_COORDINATE_SYSTEM_STR) 1129 assert cs.name == "ellipsoidal" 1130 1131 1132@pytest.mark.parametrize( 1133 "invalid_cs_string", ["3-598y5-98y", "urn:ogc:def:datum:EPSG::6326"] 1134) 1135def test_coordinate_system__from_string__invalid(invalid_cs_string): 1136 with pytest.raises(CRSError, match="Invalid coordinate system string"): 1137 CoordinateSystem.from_string(invalid_cs_string) 1138 1139 1140def test_to_proj4_enum__invalid(): 1141 crs = CRS.from_epsg(4326) 1142 with pytest.raises(ValueError, match="Invalid value"), pytest.warns(UserWarning): 1143 crs.to_proj4(1) 1144 1145 1146def test_geodetic_crs(): 1147 cc = CRS("epsg:3004") 1148 assert cc.geodetic_crs.to_epsg() == 4265 1149 1150 1151def test_itrf_init(): 1152 crs = CRS("ITRF2000") 1153 assert crs.name == "ITRF2000" 1154 1155 1156def test_compound_crs_init(): 1157 crs = CRS("EPSG:2393+5717") 1158 assert crs.name == "KKJ / Finland Uniform Coordinate System + N60 height" 1159 1160 1161def test_compound_crs_urn_init(): 1162 crs = CRS("urn:ogc:def:crs,crs:EPSG::2393,crs:EPSG::5717") 1163 assert crs.name == "KKJ / Finland Uniform Coordinate System + N60 height" 1164 1165 1166def test_from_authority__ignf(): 1167 cc = CRS.from_authority("IGNF", "ETRS89UTM28") 1168 assert cc.to_authority() == ("IGNF", "ETRS89UTM28") 1169 assert cc.to_authority("EPSG") == ("EPSG", "25828") 1170 assert cc.to_epsg() == 25828 1171 1172 1173def test_ignf_authority_repr(): 1174 assert repr(CRS.from_authority("IGNF", "ETRS89UTM28")).startswith( 1175 "<Projected CRS: IGNF:ETRS89UTM28>" 1176 ) 1177 1178 1179def test_crs_hash(): 1180 """hashes of equivalent CRS are equal""" 1181 assert hash(CRS.from_epsg(3857)) == hash(CRS.from_epsg(3857)) 1182 1183 1184def test_crs_hash_unequal(): 1185 """hashes of non-equivalent CRS are not equal""" 1186 assert hash(CRS.from_epsg(3857)) != hash(CRS.from_epsg(4326)) 1187 1188 1189def test_crs_init_user_input(): 1190 assert CRS(("IGNF", "ETRS89UTM28")).to_authority() == ("IGNF", "ETRS89UTM28") 1191 assert CRS(4326).to_epsg() == 4326 1192 1193 proj4_dict = {"proj": "longlat", "datum": "WGS84", "no_defs": None, "type": "crs"} 1194 with pytest.warns(UserWarning): 1195 assert CRS({"proj": "lonlat", "datum": "WGS84"}).to_dict() == proj4_dict 1196 assert CRS(proj="lonlat", datum="WGS84").to_dict() == proj4_dict 1197 assert ( 1198 CRS('{"proj": "longlat", "ellps": "WGS84", "datum": "WGS84"}').to_dict() 1199 == proj4_dict 1200 ) 1201 assert CRS(CRS(4326)).is_exact_same(CRS(CustomCRS())) 1202 1203 1204def test_crs_is_exact_same__non_crs_input(): 1205 assert CRS(4326).is_exact_same("epsg:4326") 1206 with pytest.warns(FutureWarning): 1207 assert not CRS(4326).is_exact_same("+init=epsg:4326") 1208 1209 1210def test_to_string__no_auth(): 1211 proj = CRS("+proj=latlong +ellps=GRS80 +towgs84=-199.87,74.79,246.62") 1212 assert ( 1213 proj.to_string() 1214 == "+proj=latlong +ellps=GRS80 +towgs84=-199.87,74.79,246.62 +type=crs" 1215 ) 1216 1217 1218def test_to_string__auth(): 1219 assert CRS(("IGNF", "ETRS89UTM28")).to_string() == "IGNF:ETRS89UTM28" 1220 1221 1222def test_srs__no_plus(): 1223 assert ( 1224 CRS("proj=longlat datum=WGS84 no_defs").srs 1225 == "proj=longlat datum=WGS84 no_defs type=crs" 1226 ) 1227 1228 1229def test_equals_different_type(): 1230 assert CRS("epsg:4326") != "" 1231 assert not CRS("epsg:4326") == "" 1232 1233 assert CRS("epsg:4326") != 27700 1234 assert not CRS("epsg:4326") == 27700 1235 1236 assert not CRS("epsg:4326") != 4326 1237 assert CRS("epsg:4326") == 4326 1238 1239 1240def test_is_exact_same_different_type(): 1241 assert not CRS("epsg:4326").is_exact_same(None) 1242 1243 1244def test_compare_crs_non_crs(): 1245 assert CRS.from_epsg(4326) != 4.2 1246 assert CRS.from_epsg(4326) == 4326 1247 with pytest.warns(FutureWarning): 1248 assert CRS.from_dict({"init": "epsg:4326"}) == {"init": "epsg:4326"} 1249 assert CRS.from_dict({"init": "epsg:4326"}) != "epsg:4326" 1250 assert CRS("epsg:4326") == CustomCRS() 1251 1252 1253def test_is_geocentric__bound(): 1254 with pytest.warns(FutureWarning): 1255 ccs = CRS("+init=epsg:4328 +towgs84=0,0,0") 1256 assert ccs.is_geocentric 1257 1258 1259def test_is_geocentric(): 1260 ccs = CRS.from_epsg(4328) 1261 assert ccs.is_geocentric 1262 1263 1264def test_is_vertical(): 1265 cc = CRS.from_epsg(5717) 1266 assert cc.is_vertical 1267 1268 1269def test_is_engineering(): 1270 eng_wkt = ( 1271 'ENGCRS["A construction site CRS",\n' 1272 'EDATUM["P1",ANCHOR["Peg in south corner"]],\n' 1273 'CS[Cartesian,2],\nAXIS["site east",southWest,ORDER[1]],\n' 1274 'AXIS["site north",southEast,ORDER[2]],\n' 1275 'LENGTHUNIT["metre",1.0],\n' 1276 'TIMEEXTENT["date/time t1","date/time t2"]]' 1277 ) 1278 assert CRS(eng_wkt).is_engineering 1279 1280 1281def test_source_crs__bound(): 1282 with pytest.warns(FutureWarning): 1283 assert CRS("+init=epsg:4328 +towgs84=0,0,0").source_crs.name == "unknown" 1284 1285 1286def test_source_crs__missing(): 1287 assert CRS("epsg:4326").source_crs is None 1288 1289 1290def test_target_crs__bound(): 1291 with pytest.warns(FutureWarning): 1292 assert CRS("+init=epsg:4328 +towgs84=0,0,0").target_crs.name == "WGS 84" 1293 1294 1295def test_target_crs__missing(): 1296 assert CRS("epsg:4326").target_crs is None 1297 1298 1299def test_whitepace_between_equals(): 1300 crs = CRS( 1301 "+proj =lcc +lat_1= 30.0 +lat_2= 35.0 +lat_0=30.0 +lon_0=87.0 +x_0=0 +y_0=0" 1302 ) 1303 assert crs.srs == ( 1304 "+proj=lcc +lat_1=30.0 +lat_2=35.0 +lat_0=30.0 " 1305 "+lon_0=87.0 +x_0=0 +y_0=0 +type=crs" 1306 ) 1307 1308 1309def test_to_dict_no_proj4(): 1310 crs = CRS( 1311 { 1312 "a": 6371229.0, 1313 "b": 6371229.0, 1314 "lon_0": -10.0, 1315 "o_lat_p": 30.0, 1316 "o_lon_p": 0.0, 1317 "o_proj": "longlat", 1318 "proj": "ob_tran", 1319 } 1320 ) 1321 with pytest.warns(UserWarning): 1322 assert crs.to_dict() == { 1323 "R": 6371229, 1324 "lon_0": -10, 1325 "no_defs": None, 1326 "o_lat_p": 30, 1327 "o_lon_p": 0, 1328 "o_proj": "longlat", 1329 "proj": "ob_tran", 1330 "type": "crs", 1331 } 1332 1333 1334def test_to_dict_from_dict(): 1335 cc = CRS.from_epsg(4326) 1336 with pytest.warns(UserWarning): 1337 assert CRS.from_dict(cc.to_dict()).name == "unknown" 1338 1339 1340def test_from_dict__invalid(): 1341 with pytest.raises(CRSError, match="CRS input is not a dict"): 1342 CRS.from_dict(4326) 1343 1344 1345@pytest.mark.parametrize( 1346 "class_type", 1347 [Datum, Ellipsoid, PrimeMeridian, CoordinateOperation, CoordinateSystem], 1348) 1349def test_incorrectly_initialized(class_type): 1350 with pytest.raises(RuntimeError): 1351 class_type() 1352 1353 1354def test_scope__remarks(): 1355 co = CoordinateOperation.from_epsg("8048") 1356 assert "GDA94" in co.scope 1357 assert "Scale difference" in co.remarks 1358 1359 1360def test_crs__scope__remarks__missing(): 1361 cc = CRS("+proj=latlon") 1362 assert cc.scope is None 1363 assert cc.remarks is None 1364 1365 1366def test_operations_missing(): 1367 cc = CRS(("IGNF", "ETRS89UTM28")) 1368 assert cc.coordinate_operation.operations == () 1369 1370 1371def test_operations(): 1372 transformer = TransformerGroup(28356, 7856).transformers[0] 1373 coord_op = CoordinateOperation.from_string(transformer.to_wkt()) 1374 assert coord_op.operations == transformer.operations 1375 1376 1377def test_operations__scope_remarks(): 1378 operation = TransformerGroup(28356, 7856).transformers[0].operations[1] 1379 coord_op = CoordinateOperation.from_string(operation.to_wkt()) 1380 assert coord_op == operation 1381 assert coord_op.remarks == operation.remarks 1382 assert coord_op.scope == operation.scope 1383 1384 1385def test_crs_equals(): 1386 assert CRS(4326).equals("epsg:4326") 1387 1388 1389def test_crs_equals__ignore_axis_order(): 1390 with pytest.warns(FutureWarning): 1391 assert CRS("epsg:4326").equals("+init=epsg:4326", ignore_axis_order=True) 1392 1393 1394@pytest.mark.parametrize( 1395 "crs_input", 1396 [ 1397 "+proj=utm +zone=15", 1398 26915, 1399 "+proj=utm +zone=15 +towgs84=0,0,0", 1400 "EPSG:26915+5717", 1401 ], 1402) 1403def test_utm_zone(crs_input): 1404 assert CRS(crs_input).utm_zone == "15N" 1405 1406 1407@pytest.mark.parametrize("crs_input", ["+proj=tmerc", "epsg:4326"]) 1408def test_utm_zone__none(crs_input): 1409 assert CRS(crs_input).utm_zone is None 1410 1411 1412def test_numpy_bool_kwarg_false(): 1413 # Issue 564 1414 south = numpy.array(50) < 0 1415 crs = CRS( 1416 proj="utm", zone=32, ellipsis="WGS84", datum="WGS84", units="m", south=south 1417 ) 1418 assert "south" not in crs.srs 1419 1420 1421def test_numpy_bool_kwarg_true(): 1422 # Issue 564 1423 south = numpy.array(50) > 0 1424 crs = CRS( 1425 proj="utm", zone=32, ellipsis="WGS84", datum="WGS84", units="m", south=south 1426 ) 1427 assert "+south " in crs.srs 1428 1429 1430@pytest.mark.skipif( 1431 pyproj._datadir._USE_GLOBAL_CONTEXT, reason="Global Context not Threadsafe." 1432) 1433def test_crs_multithread(): 1434 # https://github.com/pyproj4/pyproj/issues/782 1435 crs = CRS(4326) 1436 1437 def to_wkt(num): 1438 return crs.to_wkt() 1439 1440 with concurrent.futures.ThreadPoolExecutor(max_workers=2) as executor: 1441 for result in executor.map(to_wkt, range(10)): 1442 pass 1443 1444 1445def test_crs_multiprocess(): 1446 # https://github.com/pyproj4/pyproj/issues/933 1447 with concurrent.futures.ProcessPoolExecutor(max_workers=2) as executor: 1448 for result in executor.map(CRS, [4326 for _ in range(10)]): 1449 pass 1450 1451 1452def test_coordinate_operation__to_proj4(): 1453 operation = CoordinateOperation.from_string( 1454 "+proj=pipeline +step +proj=axisswap +order=2,1 +step " 1455 "+proj=unitconvert +xy_in=deg +xy_out=rad +step " 1456 "+proj=webmerc +lat_0=0 +lon_0=0 +x_0=0 +y_0=0 +ellps=WGS84" 1457 ) 1458 proj_string = operation.to_proj4() 1459 assert "+proj=pipeline" in proj_string 1460 assert "\n" not in proj_string 1461 1462 1463def test_coordinate_operation__to_proj4__pretty(): 1464 operation = CoordinateOperation.from_string( 1465 "+proj=pipeline +step +proj=axisswap +order=2,1 +step " 1466 "+proj=unitconvert +xy_in=deg +xy_out=rad +step " 1467 "+proj=webmerc +lat_0=0 +lon_0=0 +x_0=0 +y_0=0 +ellps=WGS84" 1468 ) 1469 proj_string = operation.to_proj4(pretty=True) 1470 assert "+proj=pipeline" in proj_string 1471 assert "\n" in proj_string 1472 1473 1474@pytest.mark.parametrize( 1475 "crs_input", 1476 [ 1477 "EPSG:4326", 1478 "EPSG:2056", 1479 ], 1480) 1481def test_to_3d(crs_input): 1482 crs = CRS(crs_input) 1483 assert len(crs.axis_info) == 2 1484 crs_3d = crs.to_3d() 1485 assert len(crs_3d.axis_info) == 3 1486 vert_axis = crs_3d.axis_info[-1] 1487 assert vert_axis.name == "Ellipsoidal height" 1488 assert vert_axis.unit_name == "metre" 1489 assert vert_axis.direction == "up" 1490 assert crs_3d.to_3d() == crs_3d 1491 assert crs_3d.name == crs.name 1492 1493 1494def test_to_3d__name(): 1495 crs_3d = CRS("EPSG:2056").to_3d(name="TEST") 1496 assert crs_3d.name == "TEST" 1497 1498 1499def test_crs__pickle(tmp_path): 1500 assert_can_pickle(CRS("epsg:4326"), tmp_path) 1501 1502 1503def test_is_derived(): 1504 assert CRS( 1505 "+proj=ob_tran +o_proj=longlat +o_lat_p=0 +o_lon_p=0 +lon_0=0" 1506 ).is_derived 1507 assert not CRS("+proj=latlon").is_derived 1508 1509 1510def test_inheritance__from_methods(): 1511 class ChildCRS(CRS): 1512 def new_method(self): 1513 return 1 1514 1515 def assert_inheritance_valid(new_crs): 1516 assert new_crs.new_method() == 1 1517 assert isinstance(new_crs, ChildCRS) 1518 assert isinstance(new_crs.geodetic_crs, ChildCRS) 1519 assert isinstance(new_crs.source_crs, (type(None), ChildCRS)) 1520 assert isinstance(new_crs.target_crs, (type(None), ChildCRS)) 1521 assert isinstance(new_crs.to_3d(), ChildCRS) 1522 for sub_crs in new_crs.sub_crs_list: 1523 assert isinstance(sub_crs, ChildCRS) 1524 1525 assert_inheritance_valid(ChildCRS.from_epsg(4326)) 1526 assert_inheritance_valid(ChildCRS.from_string("EPSG:2056")) 1527 with pytest.warns(FutureWarning): 1528 assert_inheritance_valid(ChildCRS.from_proj4("+init=epsg:4328 +towgs84=0,0,0")) 1529 assert_inheritance_valid(ChildCRS.from_user_input("EPSG:4326+5773")) 1530 assert_inheritance_valid(ChildCRS.from_json(CRS(4326).to_json())) 1531 assert_inheritance_valid(ChildCRS.from_json_dict(CRS(4326).to_json_dict())) 1532 assert_inheritance_valid(ChildCRS.from_wkt(CRS(4326).to_wkt())) 1533 1534 1535def test_list_authority(): 1536 assert CRS("+proj=utm +zone=15").list_authority() == [ 1537 AuthorityMatchInfo(auth_name="EPSG", code="32615", confidence=70) 1538 ] 1539 1540 1541def test_list_authority__multiple(): 1542 auth_list = CRS("+proj=longlat").list_authority() 1543 assert AuthorityMatchInfo(auth_name="OGC", code="CRS84", confidence=70) in auth_list 1544 assert AuthorityMatchInfo(auth_name="EPSG", code="4326", confidence=70) in auth_list 1545