1"""crs module tests"""
2
3import json
4import logging
5import os
6import subprocess
7
8import pytest
9
10import rasterio
11from rasterio._crs import _CRS
12from rasterio.env import env_ctx_if_needed, Env
13from rasterio.errors import CRSError
14
15from .conftest import requires_gdal21, requires_gdal22, requires_gdal_lt_3
16
17
18# Items like "D_North_American_1983" characterize the Esri dialect
19# of WKT SRS.
20ESRI_PROJECTION_STRING = (
21    'PROJCS["USA_Contiguous_Albers_Equal_Area_Conic_USGS_version",'
22    'GEOGCS["GCS_North_American_1983",DATUM["D_North_American_1983",'
23    'SPHEROID["GRS_1980",6378137.0,298.257222101]],'
24    'PRIMEM["Greenwich",0.0],'
25    'UNIT["Degree",0.0174532925199433]],'
26    'PROJECTION["Albers"],'
27    'PARAMETER["false_easting",0.0],'
28    'PARAMETER["false_northing",0.0],'
29    'PARAMETER["central_meridian",-96.0],'
30    'PARAMETER["standard_parallel_1",29.5],'
31    'PARAMETER["standard_parallel_2",45.5],'
32    'PARAMETER["latitude_of_origin",23.0],'
33    'UNIT["Meter",1.0],'
34    'VERTCS["NAVD_1988",'
35    'VDATUM["North_American_Vertical_Datum_1988"],'
36    'PARAMETER["Vertical_Shift",0.0],'
37    'PARAMETER["Direction",1.0],UNIT["Centimeter",0.01]]]')
38
39
40def test_from_dict():
41    """Can create a _CRS from a dict"""
42    crs = _CRS.from_dict({'init': 'epsg:3857'})
43    assert crs.to_dict()['proj'] == 'merc'
44    assert 'PROJCS["WGS 84 / Pseudo-Mercator",GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84"' in crs.to_wkt()
45
46
47def test_from_dict_keywords():
48    """Can create a CRS from keyword args, ignoring unknowns"""
49    crs = _CRS.from_dict(init='epsg:3857', foo='bar')
50    assert crs.to_dict()['proj'] == 'merc'
51    assert 'PROJCS["WGS 84 / Pseudo-Mercator",GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84"' in crs.to_wkt()
52
53
54def test_from_epsg():
55    """Can create a CRS from EPSG code"""
56    crs = _CRS.from_epsg(4326)
57    assert crs.to_dict()['proj'] == 'longlat'
58
59
60@pytest.mark.parametrize('code', [0, -1, float('nan'), 1.3])
61def test_from_epsg_error(code):
62    """Raise exception with invalid EPSG code"""
63    with pytest.raises(ValueError):
64        assert _CRS.from_epsg(code)
65
66
67@pytest.mark.parametrize('proj,expected', [({'init': 'epsg:4326'}, True), ({'init': 'epsg:3857'}, False)])
68def test_is_geographic(proj, expected):
69    """CRS is or is not geographic"""
70    assert _CRS.from_dict(proj).is_geographic is expected
71
72
73@pytest.mark.parametrize('proj,expected', [({'init': 'epsg:4326'}, False), ({'init': 'epsg:3857'}, True)])
74def test_is_projected(proj, expected):
75    """CRS is or is not projected"""
76    assert _CRS.from_dict(proj).is_projected is expected
77
78
79def test_equality():
80    """CRS are or are not equal"""
81    _CRS.from_epsg(4326) == _CRS.from_proj4('+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs')
82
83
84def test_to_wkt():
85    """CRS converts to WKT"""
86    assert _CRS.from_dict({'init': 'epsg:4326'}).to_wkt().startswith('GEOGCS["WGS 84",DATUM')
87
88
89@pytest.mark.parametrize('proj_string', ['+init=epsg:4326', '+proj=longlat +datum=WGS84 +no_defs'])
90def test_to_epsg(proj_string):
91    """CRS has EPSG code"""
92    assert _CRS.from_proj4(proj_string).to_epsg(confidence_threshold=20) == 4326
93
94
95@pytest.mark.parametrize('proj_string', [ESRI_PROJECTION_STRING])
96def test_esri_wkt_to_epsg(proj_string):
97    """CRS has no EPSG code"""
98    assert _CRS.from_wkt(proj_string, morph_from_esri_dialect=True).to_epsg() is None
99
100
101def test_epsg_no_code_available():
102    """CRS has no EPSG code"""
103    lcc_crs = _CRS.from_proj4('+lon_0=-95 +ellps=GRS80 +y_0=0 +no_defs=True +proj=lcc +x_0=0 +units=m +lat_2=77 +lat_1=49 +lat_0=0')
104    assert lcc_crs.to_epsg() is None
105
106
107def test_from_wkt_invalid():
108    """Raise exception if input WKT is invalid"""
109    with pytest.raises(CRSError):
110        _CRS.from_wkt('bogus')
111
112
113@requires_gdal_lt_3
114@pytest.mark.parametrize('projection_string', [ESRI_PROJECTION_STRING])
115def test_from_esri_wkt_no_fix(projection_string):
116    """Test ESRI CRS morphing with no datum fixing"""
117    with Env():
118        crs = _CRS.from_wkt(projection_string)
119        assert 'DATUM["D_North_American_1983"' in crs.to_wkt()
120
121
122@requires_gdal_lt_3
123@pytest.mark.parametrize('projection_string', [ESRI_PROJECTION_STRING])
124def test_from_esri_wkt_fix_datum(projection_string):
125    """Test ESRI CRS morphing with datum fixing"""
126    with Env(GDAL_FIX_ESRI_WKT='DATUM'):
127        crs = _CRS.from_wkt(projection_string, morph_from_esri_dialect=True)
128        assert 'DATUM["North_American_Datum_1983"' in crs.to_wkt()
129
130
131@requires_gdal_lt_3
132def test_to_esri_wkt_fix_datum():
133    """Morph to Esri form"""
134    assert 'DATUM["D_North_American_1983"' in _CRS.from_dict(init='epsg:26913').to_wkt(morph_to_esri_dialect=True)
135
136
137def test_compound_crs():
138    """Parse compound WKT"""
139    wkt = """COMPD_CS["unknown",GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433],AUTHORITY["EPSG","4326"]],VERT_CS["unknown",VERT_DATUM["unknown",2005],UNIT["metre",1.0,AUTHORITY["EPSG","9001"]],AXIS["Up",UP]]]"""
140    assert _CRS.from_wkt(wkt).to_wkt().startswith('COMPD_CS')
141
142
143def test_exception_proj4():
144    """Get the exception message we expect"""
145    with pytest.raises(CRSError):
146        _CRS.from_proj4("+proj=bogus")
147
148
149def test_linear_units():
150    """CRS linear units can be had"""
151    assert _CRS.from_epsg(3857).linear_units == 'metre'
152