Skip to content

titan.common

General functions useful in most geospatial contexts.

convert_gcs_path(file_path)

Parses a file path and converts from a google cloud storage path to a gdal-readable path if necessary.

Parameters:

Name Type Description Default
file_path str

the input file path to check and convert.

required

Returns:

Type Description
str

The modified file path if it's a GCS path, the original string if not.

Source code in titan/common.py
17
18
19
20
21
22
23
24
25
26
27
28
29
def convert_gcs_path(file_path: str) -> str:
    """Parses a file path and converts from a google cloud storage path to a gdal-readable path if necessary.

    Args:
        file_path: the input file path to check and convert.

    Returns:
        The modified file path if it's a GCS path, the original string if not.
    """
    if "gs://" in file_path:
        return file_path.replace("gs://", "/vsigs/")
    else:
        return file_path

creation_options_to_dict(creation_options)

Translates creation options from gdal formats to rasterio format.

Parameters:

Name Type Description Default
creation_options list

gdal driver creation options.

required

Returns:

Name Type Description
rio_co dict

creation options in rasterio's expected dictionary format.

Source code in titan/common.py
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
def creation_options_to_dict(creation_options: list) -> dict:
    """Translates creation options from gdal formats to rasterio format.

    Args:
        creation_options: gdal driver creation options.

    Returns:
        rio_co: creation options in rasterio's expected dictionary format.
    """

    rio_co = {}
    for option in creation_options:

        if "=" in option:
            name, value = option.split("=")
        elif ":" in option:
            name, value = option.split(":")
        else:
            continue

        rio_co[name.upper()] = value.upper()

    return rio_co

crs_match(crs1, crs2)

Determines whether two coordinate reference systems are the same.

Parameters:

Name Type Description Default
crs1 pyproj.CRS

the first CRS, from a rasterio dataset, a GeoDataFrame, or a string with projection parameters.

required
crs2 pyproj.CRS

the second CRS, from the same sources above.

required

Returns:

Name Type Description
matches bool

Boolean for whether the CRSs match.

Source code in titan/common.py
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
def crs_match(crs1: pyproj.CRS, crs2: pyproj.CRS) -> bool:
    """Determines whether two coordinate reference systems are the same.

    Args:
        crs1: the first CRS, from a rasterio dataset, a GeoDataFrame, or a string with projection parameters.
        crs2: the second CRS, from the same sources above.

    Returns:
        matches: Boolean for whether the CRSs match.
    """
    # normalize string inputs via rasterio
    if type(crs1) is str:
        crs1 = string_to_crs(crs1)
    if type(crs2) is str:
        crs2 = string_to_crs(crs2)

    matches = crs1 == crs2

    return matches

dimensions_match(raster1, raster2, precision=DEFAULT_PRECISION)

Determines whether two raster extents and pixel sizes match.

Parameters:

Name Type Description Default
raster1 str

path to the first raster to compare.

required
raster2 str

path to the second raster to compare.

required
precision float

the number of decimal points to round the affine transform. handles minor projection differences.

DEFAULT_PRECISION

Returns:

Name Type Description
matches bool

Boolean for whether the dimensions match.

Source code in titan/common.py
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
def dimensions_match(raster1: str, raster2: str, precision: float = DEFAULT_PRECISION) -> bool:
    """Determines whether two raster extents and pixel sizes match.

    Args:
        raster1: path to the first raster to compare.
        raster2: path to the second raster to compare.
        precision: the number of decimal points to round the affine transform.
            handles minor projection differences.

    Returns:
        matches: Boolean for whether the dimensions match.
    """
    matches = True
    with rio.open(raster1, "r") as src1, rio.open(raster2, "r") as src2:

        if src1.width != src2.width or src1.height != src2.height:
            matches = False

        affine1 = [np.around(item, precision) for item in src1.transform]
        affine2 = [np.around(item, precision) for item in src2.transform]
        if affine1 != affine2:
            matches = False

    return matches

dtype_size(dtype)

Returns the number of bytes for a raster data type.

Parameters:

Name Type Description Default
dtype Union[str, int]

the data type to check. accepts rasterio str dtypes or gdal int dtypes.

required

Returns:

Name Type Description
size int

data type size in bytes.

Source code in titan/common.py
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
def dtype_size(dtype: Union[str, int]) -> int:
    """Returns the number of bytes for a raster data type.

    Args:
        dtype: the data type to check. accepts rasterio str dtypes or gdal int dtypes.

    Returns:
        size: data type size in bytes.
    """
    if type(dtype) is int:
        if dtype == gdal.GDT_Byte:
            size = 1
        elif dtype == gdal.GDT_UInt16:
            size = 2
        elif dtype == gdal.GDT_UInt32:
            size = 4
        elif dtype == gdal.GDT_Int16:
            size = 2
        elif dtype == gdal.GDT_Int32:
            size = 4
        elif dtype == gdal.GDT_Float32:
            size = 4
        elif dtype == gdal.GDT_Float64:
            size = 8

    elif type(dtype) is str:
        if dtype == "byte":
            size = 1
        elif dtype == "uint8":
            size = 1
        elif dtype == "uint16":
            size = 2
        elif dtype == "uint32":
            size = 4
        elif dtype == "int16":
            size = 2
        elif dtype == "int32":
            size = 4
        elif dtype == "float32":
            size = 4
        elif dtype == "float64":
            size = 8

    return size

get_overview_levels(width, height)

Compute the overviews to visualize a dataset at any zoom level.

Parameters:

Name Type Description Default
width int

the raster width

required
height int

the raster height

required

Returns:

Name Type Description
overview_levels List[int]

the levels to specify with e.g. gdal's ds.BuildOverviews()

Source code in titan/common.py
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
def get_overview_levels(width: int, height: int) -> List[int]:
    """Compute the overviews to visualize a dataset at any zoom level.

    Args:
        width: the raster width
        height: the raster height

    Returns:
        overview_levels: the levels to specify with e.g. gdal's ds.BuildOverviews()
    """
    min_size = min(width, height)
    overview_levels = []
    overview_level = 2
    while True:
        if min_size // overview_level == 0:
            break
        overview_levels.append(overview_level)
        overview_level *= 2

    return overview_levels

get_reprojected_pixel_size(input_raster, dst_crs)

Compute the output pixel size for a raster in a new CRS

Parameters:

Name Type Description Default
input_raster str

path to a raster file

required
dst_crs pyproj.CRS

output projection to estimate

required

Returns:

Type Description
Tuple[float, float]

xres, yres: reprojected pixel size

Source code in titan/common.py
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
def get_reprojected_pixel_size(input_raster: str, dst_crs: pyproj.CRS) -> Tuple[float, float]:
    """Compute the output pixel size for a raster in a new CRS

    Args:
        input_raster: path to a raster file
        dst_crs: output projection to estimate

    Returns:
        xres, yres: reprojected pixel size
    """
    with rio.open(input_raster, "r") as src:
        src_crs = src.crs
        transform = src.transform

    window = rio.windows.Window(0, 0, 1, 1)
    src_bounds = rio.windows.bounds(window, transform)
    dst_bounds = transform_bounds(src_crs, dst_crs, *src_bounds)
    xmin, ymin, xmax, ymax = dst_bounds
    xres = xmax - xmin
    yres = ymax - ymin

    return xres, yres

is_raster(file_path)

Verifies whether a file is a valid raster dataset.

Parameters:

Name Type Description Default
file_path str

the input file to check.

required

Returns:

Name Type Description
is_raster bool

true/false for whether the file is a raster.

Source code in titan/common.py
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
def is_raster(file_path: str) -> bool:
    """Verifies whether a file is a valid raster dataset.

    Args:
        file_path: the input file to check.

    Returns:
        is_raster: true/false for whether the file is a raster.
    """
    try:
        ref = gdal.Open(convert_gcs_path(file_path), gdal.GA_ReadOnly)

        if ref is None:
            ref = None
            return False
        else:
            ref = None
            return True

    except RuntimeError:
        return False

is_vector(file_path)

Verifies whether a file is a valid vector dataset.

Parameters:

Name Type Description Default
file_path str

the input file to check.

required

Returns:

Name Type Description
is_vector bool

true/false for whethe the file is a vector.

Source code in titan/common.py
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
def is_vector(file_path: str) -> bool:
    """Verifies whether a file is a valid vector dataset.

    Args:
        file_path: the input file to check.

    Returns:
        is_vector: true/false for whethe the file is a vector.
    """
    try:
        ref = ogr.Open(file_path, gdal.GA_ReadOnly)
        if ref is None:
            ref = None
            return False
        else:
            ref = None
            return True
    except RuntimeError:
        return False

parse_crs_string(string)

Parses a string to determine the CRS/spatial projection format.

Parameters:

Name Type Description Default
string str

a string with CRS/projection data.

required

Returns:

Name Type Description
crs_type str

Str in ["wkt", "proj4", "epsg", "string"].

Source code in titan/common.py
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
def parse_crs_string(string: str) -> str:
    """Parses a string to determine the CRS/spatial projection format.

    Args:
        string: a string with CRS/projection data.

    Returns:
        crs_type: Str in ["wkt", "proj4", "epsg", "string"].
    """
    if "epsg:" in string.lower():
        return "epsg"
    elif "+proj" in string:
        return "proj4"
    elif "SPHEROID" in string:
        return "wkt"
    else:
        return "string"

string_to_crs(string)

Converts a crs/projection string to a pyproj-readable CRS object

Parameters:

Name Type Description Default
string str

a crs/projection string.

required

Returns:

Name Type Description
crs pyproj.CRS

a rasterio.crs.CRS object

Source code in titan/common.py
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
def string_to_crs(string: str) -> pyproj.CRS:
    """Converts a crs/projection string to a pyproj-readable CRS object

    Args:
        string: a crs/projection string.

    Returns:
        crs: a rasterio.crs.CRS object
    """
    crs_type = parse_crs_string(string)

    if crs_type == "epsg":
        auth, code = string.split(":")
        crs = rio.crs.CRS.from_epsg(int(code))
    elif crs_type == "proj4":
        crs = rio.crs.CRS.from_proj4(string)
    elif crs_type == "wkt":
        crs = rio.crs.CRS.from_wkt(string)
    else:
        crs = rio.crs.CRS.from_string(string)

    return crs