Feature overview#
The following presents a descriptive example show-casing all core features of GeoUtils.
For more details, refer to the Fundamentals, Rasters or Vectors pages.
Tip
All pages of this documentation containing code cells can be run interactively online without the need of setting up your own environment. Simply click the top launch button! (MyBinder can be a bit capricious: you might have to be patient, or restart it after the build is done the first time 😅)
Alternatively, start your own notebook to test GeoUtils at .
The core Raster
and Vector
classes#
In GeoUtils, geospatial handling is object-based and revolves around Raster
and Vector
.
These link to either on-disk or in-memory datasets, opened by calling the object with a file path.
import geoutils as gu
# Examples files: infrared band of Landsat and glacier outlines
filename_rast = gu.examples.get_path("everest_landsat_b4")
filename_vect = gu.examples.get_path("everest_rgi_outlines")
# Open files by calling Raster and Vector
rast = gu.Raster(filename_rast)
vect = gu.Vector(filename_vect)
A Raster
is an object with four main attributes: a numpy.ma.MaskedArray
as data
, a
pyproj.crs.CRS
as crs
, an affine.Affine
as transform
, and a float
or int
as nodata
.
# The opened raster
rast
Raster(
data=not_loaded; shape on disk (1, 655, 800); will load (655, 800)
transform=| 30.00, 0.00, 478000.00|
| 0.00,-30.00, 3108140.00|
| 0.00, 0.00, 1.00|
crs=EPSG:32645
nodata=None)
Important
When a file exists on disk, Raster
is linked to a rasterio.io.DatasetReader
object for loading the metadata. The array will be
loaded in-memory implicitly when data
is required by an operation.
See Implicit lazy loading for more details.
A Vector
is an object with a single main attribute: a GeoDataFrame
as ds
, for which
most methods are wrapped directly into Vector
.
# The opened vector
vect
Vector(
ds= RGIId GLIMSId BgnDate EndDate CenLon CenLat \
0 RGI60-15.03410 G086932E27925N 20001030 -9999999 86.932083 27.924758
1 RGI60-15.03411 G086939E27924N 20001030 -9999999 86.939035 27.923919
2 RGI60-15.03412 G086852E27949N 20001030 -9999999 86.851810 27.948727
3 RGI60-15.03413 G086848E27952N 20001030 -9999999 86.848051 27.951937
4 RGI60-15.03414 G086849E27966N 20001030 -9999999 86.848799 27.966443
.. ... ... ... ... ... ...
81 RGI60-15.10070 G086956E28089N 20100409 -9999999 86.956000 28.089000
82 RGI60-15.10075 G086960E28045N 20100409 -9999999 86.960000 28.045000
83 RGI60-15.10077 G086963E28079N 20100409 -9999999 86.963000 28.079000
84 RGI60-15.10078 G086966E28051N 20100409 -9999999 86.966000 28.051000
85 RGI60-15.10079 G086969E28097N 20100409 -9999999 86.969000 28.097000
O1Region O2Region Area Zmin ... Aspect Lmax Status Connect Form \
0 15 2 0.980 5196 ... 310 1184 0 0 0
1 15 2 0.064 5831 ... 78 309 0 0 0
2 15 2 0.059 5509 ... 29 269 0 0 0
3 15 2 0.176 5400 ... 341 790 0 0 0
4 15 2 0.477 5280 ... 284 845 0 0 0
.. ... ... ... ... ... ... ... ... ... ...
81 15 2 12.646 5940 ... 266 5413 0 0 1
82 15 2 0.143 6530 ... 262 426 0 0 0
83 15 2 0.051 6420 ... 307 395 0 0 0
84 15 2 0.181 6559 ... 321 461 0 0 0
85 15 2 0.136 6538 ... 203 571 0 0 0
TermType Surging Linkages Name \
0 0 9 9 None
1 0 9 9 None
2 0 9 9 None
3 0 9 9 None
4 0 9 9 None
.. ... ... ... ...
81 0 9 9 CN5O193B0118 East Rongbuk Glacier
82 0 9 9 CN5O193B0118 East Rongbuk Glacier
83 0 9 9 CN5O193B0118 East Rongbuk Glacier
84 0 9 9 CN5O193B0118 East Rongbuk Glacier
85 0 9 9 CN5O193B0118 East Rongbuk Glacier
geometry
0 POLYGON ((86.92842 27.92877, 86.92866 27.92930...
1 POLYGON ((86.94077 27.92375, 86.94059 27.92295...
2 POLYGON ((86.85344 27.94752, 86.85340 27.94752...
3 POLYGON ((86.84776 27.94845, 86.84776 27.94849...
4 POLYGON ((86.84567 27.96906, 86.84567 27.96910...
.. ...
81 POLYGON ((86.94112 28.11455, 86.94129 28.11452...
82 POLYGON ((86.95861 28.04669, 86.95847 28.04701...
83 POLYGON ((86.96454 28.07843, 86.96445 28.07814...
84 POLYGON ((86.96302 28.04915, 86.96304 28.04920...
85 POLYGON ((86.97074 28.09496, 86.97039 28.09488...
[86 rows x 23 columns]
crs=EPSG:4326
bounds=BoundingBox(left=86.70393205700003, bottom=27.847778695000045, right=87.11409458600008, top=28.14549759700003))
All other attributes are derivatives of those main attributes, or of the filename on disk. Attributes of Raster
and
Vector
update with geospatial operations on themselves.
Handling and match-reference#
In GeoUtils, geospatial handling operations are based on class methods, such as crop()
or reproject()
.
For convenience and consistency, nearly all of these methods can be passed solely another Raster
or Vector
as a
reference to match during the operation. A reference Vector
enforces a matching of bounds
and/or
crs
, while a reference Raster
can also enforce a matching of res
, depending on the nature of the operation.
# Print initial bounds of the vector
print(vect.bounds)
# Crop vector to raster's extent, and add clipping option (otherwise keeps all intersecting features)
vect_cropped = vect.crop(rast, clip=True)
# Print bounds of cropped + clipped vector
print(vect_cropped.bounds)
BoundingBox(left=86.70393205700003, bottom=27.847778695000045, right=87.11409458600008, top=28.14549759700003)
BoundingBox(left=86.77604281922927, bottom=27.921168931302088, right=87.02035980673882, top=28.098736524870052)
Additionally, in GeoUtils, methods that apply to the same georeferencing attributes have consistent naming1 across Raster
and Vector
.
A reproject()
involves a change in crs
or transform
, while a crop()
only involves a change
in bounds
. Using polygonize()
allows to generate a Vector
from a Raster
,
and the other way around for rasterize()
.
Class |
||
---|---|---|
Warping/Reprojection |
||
Cropping/Clipping |
||
Rasterize/Polygonize |
All methods can also be passed any number of georeferencing arguments such as shape
or res
, and will
naturally deduce others from the input Raster
or Vector
, much as in GDAL’s command line.
Higher-level analysis tools#
GeoUtils also implements higher-level geospatial analysis tools for both Rasters
and Vectors
. For
example, one can compute the distance to a Vector
geometry, or to target pixels of a Raster
, using
proximity()
.
As with the geospatial handling functions previously listed, many analysis functions can take a Raster
or Vector
as a
reference to utilize during the operation. In the case of proximity()
, passing a Raster
serves as a reference
for the georeferenced grid on which to compute the distances.
# Compute proximity to vector on raster's grid
rast_proximity_to_vec = vect.proximity(rast)
Note
Right now, the array data
of rast
is still not loaded. Applying crop()
does not yet require loading,
and rast
’s metadata is sufficient to provide a georeferenced grid for proximity()
. The array will only be loaded when necessary.
Quick plotting#
To facilitate the analysis process, GeoUtils includes quick plotting tools that support multiple colorbars and implicitly add layers to the current axis.
Those are build on top of rasterio.plot.show()
and geopandas.GeoDataFrame.plot()
, and relay any argument passed.
See also
GeoUtils’ plotting tools only aim to smooth out the most common hassles when quickly plotting raster and vectors.
For advanced plotting tools to create “publication-quality” figures, see Cartopy or GeoPlot.
The plotting functionality is named plot()
everywhere, for consistency. Here again, a Raster
or
Vector
can be passed as a reference to match to ensure all data is displayed on the same grid and projection.
# Plot proximity to vector
rast_proximity_to_vec = vect.proximity(rast)
rast_proximity_to_vec.plot(cbar_title="Distance to glacier outline")
vect.plot(rast_proximity_to_vec, fc="none")
Tip
To quickly visualize a raster directly from a terminal, without opening a Python console/notebook, check out our tool geoviewer.py
in the Command line interface documentation.
Pythonic arithmetic and NumPy interface#
All Raster
objects support Python arithmetic (+
, -
, /
, //
, *
,
**
, %
) with any other Raster
, ndarray
or
number. With another Raster
, the georeferencing must match, while only the shape with a ndarray
.
# Add 1 to the raster array
rast += 1
Additionally, the Raster
object possesses a NumPy masked-array interface that allows to apply to it any NumPy universal function and
most other NumPy array functions, while logically casting dtype
and respecting nodata
values.
# Apply a normalization to the raster
import numpy as np
rast = (rast - np.min(rast)) / (np.max(rast) - np.min(rast))
Casting to Mask
, indexing and overload#
All Raster
objects also support Python logical comparison operators (==
, !=
, >=
, >
, <=
,
<
), or more complex NumPy logical functions. Those operations automatically casts them into a Mask
, a boolean raster that inherits all methods from Raster
.
# Get mask of an AOI: infrared index above 0.6, at least 200 m from glaciers
mask_aoi = np.logical_and(rast > 0.6, rast_proximity_to_vec > 200)
Masks can then be used for indexing a Raster
, which returns a MaskedArray
of indexed values.
# Index raster with mask to extract a 1-D array
values_aoi = rast[mask_aoi]
Masks also have simplified, overloaded Raster
methods due to their boolean dtype
. Using polygonize()
with a
Mask
is straightforward, for instance, to retrieve a Vector
of the area-of-interest:
# Polygonize areas where mask is True
vect_aoi = mask_aoi.polygonize()
# Plot result
rast.plot(cmap='Reds', cbar_title='Normalized infrared')
vect_aoi.plot(fc='none', ec='k', lw=0.75)
Saving to file#
Finally, for saving a Raster
or Vector
to file, simply call the save()
function.
# Save our AOI vector
vect_aoi.save("myaoi.gpkg")
Parsing metadata with SatelliteImage
#
In our case, rast
would be better opened using the Raster
object SatelliteImage
instead, which tentatively parses
metadata recognized from the filename or auxiliary files.
# Name of the image we used
import os
print(os.path.basename(filename_rast))
LE71400412000304SGS00_B4.tif
# Open while parsing metadata
rast = gu.SatelliteImage(filename_rast, silent=False)
From filename: setting satellite as Landsat 7
From filename: setting sensor as ETM+
From filename: setting tile_name as 140041
From filename: setting datetime as 2000-10-30 00:00:00
Wrap-up
In a few lines, we:
easily handled georeferencing operations on rasters and vectors,
performed numerical calculations inherently respecting invalid data,
casted to a mask implicitly from a logical operation on raster, and
vectorized a mask without need for any additional metadata, simply using the nature of the mask object!
Our result: a vector of high infrared absorption indexes at least 200 meters away from glaciers near Everest, which likely corresponds to perennial snowfields.
Otherwise, for more hands-on examples, explore GeoUtils’ gallery of examples!