easyidp.geotiff.GeoTiff¶
- class easyidp.geotiff.GeoTiff(file_path: str | Path | None = None, imarray: ndarray | None = None, header: dict | None = None, mask: ndarray | None = None)¶
A easy GeoTiff class warpped on rasterio
- __init__(file_path: str | Path | None = None, imarray: ndarray | None = None, header: dict | None = None, mask: ndarray | None = None)¶
The method to initialize the GeoTiff class
- Parameters:
tif_path (None| str | pathlib.Path, optional) – the path to geotiff file, by default None, specify if need to open existing geotiff file
imarray (np.ndarray) – The pixel data of GeoTIFF if format of (height, width, bands)
header (dict) – The profile / meta information of geotiff file, including size, bands, CRS, etc
Transparent_layer (None | int, optional) – the transparent or alpha layer of given GeoTiff file, by default None | None: no alpha layer | 0-x : specific alpha layer | -1 : the last layer
Example
>>> import easyidp as idp >>> test_data = idp.data.TestData() >>> dom = idp.GeoTiff(test_data.pix4d.lotus_dom)
Methods
__init__([file_path, imarray, header, mask])The method to initialize the GeoTiff class
convert_from_affine([target_bounds])Convert from affine rotation storage to standard storage.
Convert from standard storage to affine rotation storage.
crop_polygon(polygon_hv[, is_geo, ...])Crop a given polygon from geotiff
crop_rectangle(left, top, w, h[, is_geo, ...])Extract a rectangle regeion crop from a GeoTIFF image file.
crop_rois(roi[, is_geo, save_folder, ...])Crop several ROIs from the geotiff by given <ROI> object with several polygons and polygon names
crop_shapely_polygon(shapely_polygon[, ...])Crop a given polygon from geotiff, the base function of cropping geotiff
geo2pixel(polygon_hv[, return_index])Convert geo coordinate (lon, lat) to geotiff pixel coordinate (horizontal, vertical).
has_data()Check if the geotiff has data
open(tif_path)Open and get the meta information (header) from geotiff
pixel2geo(polygon_hv)Convert geotiff pixel coordinate or index (horizontal, vertical) to geo coordinate (x, y).
point_query(points_hv[, is_geo])Get the pixel value of given point(s)
polygon_math([polygon_hv, is_geo, kernel])Calculate the valus inside given polygon
save(save_path[, overwrite, apply_mask, ...])Save GeoTiff as tiff file with proper nodata/mask handling.
set_mask_polygon(polygon[, is_geo])Set mask from polygon coordinates.
Attributes
A quick access to
self.header['crs'], please access theheaderdict to change valueA quick access to
self.header['dim'], please access theheaderdict to change valueCheck if this GeoTiff has an alpha channel.
A quick access to
self.header['height'], please access theheaderdict to change valueAccess to the pixel values in the type of numpy ndarray
Boolean mask where True indicates valid (non-nodata) pixels.
Get the mask as polygon coordinates.
Get mask polygon in geo coordinates.
Get mask polygon in pixel coordinates.
A quick access to
self.header['nodata'], please access theheaderdict to change valueA quick access to
self.header['scale'], please access theheaderdict to change valueA quick access to
self.header['tie_point'], please access theheaderdict to change valueCheck if this GeoTiff uses affine rotation storage.
A quick access to
self.header['width'], please access theheaderdict to change valueThe file path of current GeoTiff, pathlib.Path object
The Geotiff meta infomation
- convert_from_affine(target_bounds: tuple | None = None) GeoTiff¶
Convert from affine rotation storage to standard storage.
Returns a NEW GeoTiff object with the rotated imarray transformed back to axis-aligned coordinates, with a mask for the valid region. The original object is not modified.
- Parameters:
target_bounds (tuple, optional) – (min_x, min_y, max_x, max_y) for the output image bounds. If None, uses the bounding box of mask_polygon.
- Returns:
New GeoTiff object in standard mode. Returns self if already standard.
- Return type:
- Raises:
ValueError – If no mask polygon is available for conversion.
Example
>>> gtiff = idp.GeoTiff('affine_file.tif') >>> gtiff.use_affine True >>> standard_gtiff = gtiff.convert_from_affine() >>> standard_gtiff.use_affine False >>> gtiff.use_affine # Original unchanged True
- convert_to_affine() GeoTiff¶
Convert from standard storage to affine rotation storage.
Returns a NEW GeoTiff object with imarray aligned to the mask polygon rectangle, and transform including rotation. The original object is not modified.
Requires mask_polygon to be a valid rectangle (4 vertices, 90° angles).
- Returns:
New GeoTiff object in affine mode. Returns self if already affine.
- Return type:
- Raises:
ValueError – If no mask polygon is set or polygon is not a valid rectangle.
Example
>>> gtiff = idp.GeoTiff('input.tif') >>> gtiff.set_mask_polygon(rect_coords, is_geo=True) >>> affine_gtiff = gtiff.convert_to_affine() >>> affine_gtiff.use_affine True >>> gtiff.use_affine # Original unchanged False
- crop_polygon(polygon_hv, is_geo=True, save_path: str | Path | None = None, return_geotiff: bool = False, use_affine: bool = False)¶
Crop a given polygon from geotiff
- Parameters:
polygon_hv (numpy nx2 array) – (horizontal, vertical) points
is_geo (bool, optional) – whether the given polygon is pixel coords on imarray or geo coords (default)
save_path (str | pathlib.Path, optional) – if given, will save the cropped as *.tif file to path, by default None
return_geotiff (bool, optional) – if specify to True, will return idp.GeoTiff object instead of ndarray
use_affine (bool, optional) – if True, use affine rotation storage for cropped results (only if ROI is a rectangle). Forces return_geotiff=True.
- Returns:
The cropped numpy pixels imarray
- Return type:
imarray_out
Example
Prepare data:
>>> import easyidp as idp >>> test_data = idp.data.TestData() # prepare geotiff >>> dom = idp.GeoTiff(test_data.pix4d.lotus_dom) # prepare polygon >>> roi = idp.ROI(test_data.shp.lotus_shp, name_field=0) >>> roi = roi[0] >>> roi.change_crs(dom.crs) >>> roi array([[ 368017.7565143 , 3955511.08102276], [ 368019.70190232, 3955511.49811902], [ 368020.11263046, 3955509.54636219], [ 368018.15769062, 3955509.13563382], [ 368017.7565143 , 3955511.08102276]])
Use this function:
>>> imarray = dom.crop_polygon(roi, is_geo=True) >>> imarray.shape (320, 319, 4)
If you want to save the previous as new GeoTiff:
>>> save_tiff = "path/to/save/cropped.tif" >>> imarray = obj.crop_polygon(polygon_hv, is_geo=True, save_path=save_tiff)
- crop_rectangle(left: int, top: int, w: int, h: int, is_geo: bool = True, save_path: str | Path | None = None, return_geotiff: bool = False, use_affine: bool = False)¶
Extract a rectangle regeion crop from a GeoTIFF image file.
(0,0) o-------------------------- | ^ | | top | v | <-------> o=============o ^ | left |<---- w ---->| | | | | h | | | | | o=============o v
- Parameters:
top (int) – Coordinates of the top left corner of the desired crop.
left (int) – Coordinates of the top left corner of the desired crop.
h (int) – Desired crop height.
w (int) – Desired crop width.
is_geo (bool, optional) – whether the given polygon is pixel coords on imarray or geo coords (default)
save_path (str | pathlib.Path, optional) – if given, will save the cropped as *.tif file to path
return_geotiff (bool, optional) – if specify to True, will return idp.GeoTiff object instead of ndarray
use_affine (bool, optional) – if True, use affine rotation storage for cropped results (only if ROI is a rectangle). Forces return_geotiff=True.
- Returns:
Extracted crop.
- Return type:
ndarray
Example
>>> import easyidp as idp >>> test_data = idp.data.TestData() >>> obj = idp.GeoTiff(test_data.pix4d.lotus_dom) >>> out = obj.crop_rectangle(left=434, top=918, w=320, h=321, is_geo=False) >>> out.shape (321, 320, 4)
Note
It is not recommended to use without specifying parameters like this:
crop_rectiange(434, 918, 320, 321)It is hard to know the exactly order
- crop_rois(roi, is_geo=True, save_folder=None, return_geotiff: bool = False, use_affine: bool = False)¶
Crop several ROIs from the geotiff by given <ROI> object with several polygons and polygon names
- Parameters:
roi (easyidp.ROI | dict) – the <ROI> object created by easyidp.ROI(), or dictionary with multiple polygons. If you just need crop single polygon with ndarray coordinates, please use GeoTiff.crop_polygon() instead.
is_geo (bool, optional) – whether the given polygon is pixel coords on imarray or geo coords (default)
save_folder (str, optional) – the folder to save cropped images, use ROI indices as file_names, by default “”, means not save.
return_geotiff (bool, optional) – if specify to True, will return idp.GeoTiff object in dictionary values instead of ndarray. Note that if use_affine=True, this will be forced to True.
use_affine (bool, optional) – if True, use affine rotation storage for cropped results (only if ROI is a rectangle). Forces return_geotiff=True.
- Returns:
The dictionary with key=id and value=ndarray data (or GeoTiff object)
- Return type:
dict,
Example
Prepare data:
>>> import easyidp as idp >>> test_data = idp.data.TestData() # prepare dom geotiff >>> dom = idp.GeoTiff(test_data.pix4d.lotus_dom) # prepare several ROIs >>> roi = idp.ROI(test_data.shp.lotus_shp, name_field=0) >>> roi = roi[0:3] # only use 3 for quick example >>> roi.change_crs(obj.crs) # transform to the same CRS like DOM {0: array([[ 368017.7565143 , 3955511.08102276], [ 368019.70190232, 3955511.49811902], [ 368020.11263046, 3955509.54636219], [ 368018.15769062, 3955509.13563382], [ 368017.7565143 , 3955511.08102276]]), 1: array([[ 368018.20042946, 3955508.96051697], [ 368020.14581791, 3955509.37761334], [ 368020.55654627, 3955507.42585654], [ 368018.601606 , 3955507.01512806], [ 368018.20042946, 3955508.96051697]]), 2: array([[ 368018.64801755, 3955506.84956301], [ 368020.59340644, 3955507.26665948], [ 368021.00413502, 3955505.31490271], [ 368019.04919431, 3955504.90417413], [ 368018.64801755, 3955506.84956301]])}
Use this function:
>>> out_dict = obj.crop_rois(roi) {"N1W1": array[...], "N1W3": array[...], ...} >>> out_dict["N1W1"].shape (320, 319, 4)
If you want automatically save geotiff results to specific folder:
>>> tif_out_folder = "./cropped_geotiff" >>> os.mkdir(tif_out_folder) >>> out_dict = obj.crop_rois(roi, save_folder=tif_out_folder)
- crop_shapely_polygon(shapely_polygon: Polygon, save_path: str | Path | None = None, return_geotiff: bool = False, use_affine: bool = False)¶
Crop a given polygon from geotiff, the base function of cropping geotiff
- Parameters:
shapely_polygon (shapely.geometry.Polygon) – The polygon to crop, in geo coordinate
save_path (str, optional) – if given, will save the cropped as *.tif file to path
return_geotiff (bool, optional) – if specify to True, will return idp.GeoTiff object instead of ndarray
use_affine (bool, optional) – if True, use affine rotation storage for cropped results (only if ROI is a rectangle). Forces return_geotiff=True.
- Return type:
idp.GeoTiff object
- property crs¶
A quick access to
self.header['crs'], please access theheaderdict to change value
- property dim¶
A quick access to
self.header['dim'], please access theheaderdict to change value
- file_path¶
The file path of current GeoTiff, pathlib.Path object
>>> dom.file_path PosixPath('/Users/<user>/Library/Application Support/easyidp.data/data_for_tests/pix4d/lotus_tanashi_full/hasu_tanashi_20170525_Ins1RGB_30m_transparent_mosaic_group1.tif')
- geo2pixel(polygon_hv: ndarray, return_index=False) ndarray¶
Convert geo coordinate (lon, lat) to geotiff pixel coordinate (horizontal, vertical). A warpper of rasterio.io.DatasetReader.transform() and rasterio.io.DatasetReader.index()
- Parameters:
points_hv (numpy nx2 array) – [horizontal, vertical] points
return_index (bool, default false) – if false: will get float coordinates -> (23.5, 27.8) if true: will get int pixel index -> (23, 27)
- Return type:
The ndarray pixel position of these points (horizontal, vertical)
Example
Prepare data:
>>> import easyidp as idp >>> test_data = idp.data.TestData() # prepare the roi data >>> roi = idp.ROI(test_data.shp.lotus_shp, name_field=0) >>> dom = idp.GeoTiff(test_data.pix4d.lotus_dom) >>> roi.change_crs(dom.crs) >>> roi_test = roi[111] array([[ 368051.75902187, 3955484.68169527], [ 368053.70441367, 3955485.09879908], [ 368054.11515079, 3955483.14704415], [ 368052.16020711, 3955482.73630818], [ 368051.75902187, 3955484.68169527]])
Use this function:
>>> roi_test_pixel = dom.geo2pixel(roi_test) array([[5043.01515811, 4551.90714551], [5306.6183839 , 4495.38901391], [5362.27381938, 4759.85445164], [5097.37630191, 4815.50973136], [5043.01515811, 4551.90714551]])
- property has_alpha: bool¶
Check if this GeoTiff has an alpha channel.
This property reads the colorinterp (color interpretation) from the GeoTiff header to determine if an alpha mask is present. The colorinterp field is extracted from the TIFF metadata by rasterio, which maps to the GDAL/TIFF PHOTOMETRIC and EXTRASAMPLES tags.
- Returns:
True if the GeoTiff has an alpha band, False otherwise.
- Return type:
bool
Example
>>> import easyidp as idp >>> test_data = idp.data.TestData() >>> dom = idp.GeoTiff(test_data.pix4d.lotus_dom) >>> dom.has_alpha True >>> dsm = idp.GeoTiff(test_data.pix4d.lotus_dsm) >>> dsm.has_alpha False
- has_data() bool¶
Check if the geotiff has data
- header¶
The Geotiff meta infomation
>>> dom.header {'height': 5752, 'width': 5490, 'dim': 4, 'nodata': 0, 'dtype': dtype('uint8'), 'scale': [0.00738, 0.00738], 'tie_point': [368014.54157, 3955518.2747700005], 'crs': <Derived Projected CRS: EPSG:32654> Name: WGS 84 / UTM zone 54N Axis Info [cartesian]: - E[east]: Easting (metre) - N[north]: Northing (metre) Area of Use: - name: Between 138°E and 144°E, northern hemisphere between equator and 84°N, onshore and offshore. Japan. Russian Federation. - bounds: (138.0, 0.0, 144.0, 84.0) Coordinate Operation: - name: UTM zone 54N - method: Transverse Mercator Datum: World Geodetic System 1984 ensemble - Ellipsoid: WGS 84 - Prime Meridian: Greenwich 'profile': <dict `rasterio.io.DatasetReader.profile`> } >>> dom.header["height"] 5752
Caution
Since v2.0.2, this function backend has been switched from tifffile to rasterio to improve the performance, Some of the key tags in header like ‘tags’, ‘photometric’, ‘planarconfig’, ‘compress’ has been deprecated.
- property height¶
A quick access to
self.header['height'], please access theheaderdict to change value
- property imarray¶
Access to the pixel values in the type of numpy ndarray
- property mask: ndarray | None¶
Boolean mask where True indicates valid (non-nodata) pixels.
Shape is (height, width). When mask_polygon is set, the binary mask is computed from the polygon. Otherwise, falls back to computing from alpha channel or nodata values.
This property is lazy-computed and cached for efficiency.
- Returns:
Boolean mask array with shape (height, width), or None if no data.
- Return type:
np.ndarray | None
Example
>>> import easyidp as idp >>> test_data = idp.data.TestData() >>> dsm = idp.GeoTiff(test_data.pix4d.lotus_dsm) >>> mask = dsm.mask >>> mask.shape (5752, 5490) >>> mask.dtype dtype('bool')
- property mask_polygon: ndarray | None¶
Get the mask as polygon coordinates.
Returns the polygon in its stored coordinate type (geo or pixel). Use mask_polygon_geo or mask_polygon_pixel for specific types.
- Returns:
(n, 2) polygon coordinates, or None if not set.
- Return type:
np.ndarray | None
- property mask_polygon_geo: ndarray | None¶
Get mask polygon in geo coordinates.
Converts from pixel coords if needed using the transform.
- Returns:
(n, 2) geo coordinates, or None if no polygon.
- Return type:
np.ndarray | None
- property mask_polygon_pixel: ndarray | None¶
Get mask polygon in pixel coordinates.
Converts from geo coords if needed using the transform.
- Returns:
(n, 2) pixel coordinates, or None if no polygon.
- Return type:
np.ndarray | None
- property nodata¶
A quick access to
self.header['nodata'], please access theheaderdict to change value
- open(tif_path: str | Path)¶
Open and get the meta information (header) from geotiff
- Parameters:
tif_path (str | pathlib.Path) – the path to geotiff file
Example
Though this function can be used by:
>>> import easyidp as idp >>> test_data = idp.data.TestData() >>> dom = idp.GeoTiff() >>> dom.read_geotiff(test_data.pix4d.lotus_dom)
It is highly recommended to specify the geotiff path when initializing the geotiff object:
>>> dom = idp.GeoTiff(test_data.pix4d.lotus_dom)
- pixel2geo(polygon_hv)¶
Convert geotiff pixel coordinate or index (horizontal, vertical) to geo coordinate (x, y). A warpper of rasterio.io.DatasetReader.xy() and rasterio.io.DatasetReader.transform()
- Parameters:
points_hv (numpy nx2 array) – [horizontal, vertical] points if dtype is np.floating, view as pixel coordinate if dtype is np.integer, view as pixel index (return left upper corner of pixel)
- Return type:
The ndarray pixel position of these points (horizontal, vertical)
Example
Prepare data:
>>> import easyidp as idp >>> test_data = idp.data.TestData() # prepare the roi data >>> roi = idp.ROI(test_data.shp.lotus_shp, name_field=0) >>> dom = idp.GeoTiff(test_data.pix4d.lotus_dom) >>> roi.change_crs(dom.crs) >>> roi_test = roi[111] >>> roi_test_pixel = dom.geo2pixel(roi_test) array([[5043.01515811, 4551.90714551], [5306.6183839 , 4495.38901391], [5362.27381938, 4759.85445164], [5097.37630191, 4815.50973136], [5043.01515811, 4551.90714551]])
Use this function:
>>> roi_test_back = dom.pixel2geo(roi_test_pixel) array([[ 368051.75902187, 3955484.68169527], [ 368053.70441367, 3955485.09879908], [ 368054.11515079, 3955483.14704415], [ 368052.16020711, 3955482.73630818], [ 368051.75902187, 3955484.68169527]])
- point_query(points_hv, is_geo=True)¶
Get the pixel value of given point(s)
- Parameters:
points_hv (tuple | list | nx2 ndarray) –
The coordinates of qurey points, in order (horizontal, vertical)is_geo (bool, optional) –
The given polygon is geo coords (True, default) or pixel coords (False) on imarray.
- Returns:
the obtained pixel value (RGB or height)
- Return type:
ndarray
Example
Prequirements
>>> import easyidp as idp >>> dsm = idp.GeoTiff(test_data.pix4d.lotus_dsm)
Query one point by tuple
>>> # one point tuple >>> pts = (368023.004, 3955500.669) >>> dsm.point_query(pts, is_geo=True) array([97.45558])
Query one point by list
>>> # one point list >>> pts = [368023.004, 3955500.669] >>> dsm.point_query(pts, is_geo=True) array([97.45558])
Query several points by list
>>> pts = [ ... [368022.581, 3955501.054], ... [368024.032, 3955500.465] ... ] >>> dsm.point_query(pts, is_geo=True) array([97.624344, 97.59617])
Query several points by numpy
>>> pts = np.array([ ... [368022.581, 3955501.054], ... [368024.032, 3955500.465] ... ]) >>> dsm.point_query(pts, is_geo=True) array([97.624344, 97.59617])
See also
easyidp.geotiff.point_query
- polygon_math(polygon_hv: ndarray | None = None, is_geo=True, kernel='mean')¶
Calculate the valus inside given polygon
- Parameters:
polygon_hv (numpy nx2 array | None, optional) – (horizontal, vertical) points. If None, the calculation will be performed on the entire image. Defaults to None.
is_geo (bool, optional) – whether the given polygon is pixel coords on imarray or geo coords (default)
kernel (str, optional) – The method to calculate polygon summary, options are: [“mean”, “min”, “max”, “pmin5”, “pmin10”, “pmax5”, “pmax10”], please check notes section for more details.
Notes
Option details for
kernelparameter:“mean”: the mean value inside polygon
“min”: the minimum value inside polygon
“max”: the maximum value inside polygon
“pmin5”: 5th [percentile mean]_ inside polygon
“pmin10”: 10th [percentile mean]_ inside polygon
“pmax5”: 95th [percentile mean]_ inside polygon
“pmax10”: 90th [percentile mean]_ inside polygon
Example
Prepare data:
>>> import easyidp as idp >>> test_data = idp.data.TestData() # prepare the roi data >>> roi = idp.ROI(test_data.shp.lotus_shp, name_field=0) >>> dsm = idp.GeoTiff(test_data.pix4d.lotus_dsm) >>> roi.change_crs(dsm.crs) >>> roi_test = roi[111] array([[ 368051.75902187, 3955484.68169527], [ 368053.70441367, 3955485.09879908], [ 368054.11515079, 3955483.14704415], [ 368052.16020711, 3955482.73630818], [ 368051.75902187, 3955484.68169527]])
Use this function:
>>> dsm.polygon_math(roi_test, is_geo=True, kernel="mean") 97.20491 >>> dsm.polygon_math(roi_test, is_geo=True, kernel="pmax10") 97.311844
Caution
This function is initially designed for doing some simple calculations one band (layer) geotiff.
If you applying this function on RGB color geotiff, it will return the calculated results of each layer
>>> dom.polygon_math(roi_test, is_geo=True, kernel="pmax10") array([139.97428808, 161.36439038, 122.30964888, 255. ])
The four values are RGBA four color channels.
- save(save_path: str | Path, overwrite: bool = False, apply_mask: bool = True, use_affine: bool = False) bool¶
Save GeoTiff as tiff file with proper nodata/mask handling.
Mask is only applied during save. Previous crop operations preserve full rectangular data, allowing further calculations on edge pixels.
The save strategy depends on data type: - DSM: uses nodata value (-32767.0) - RGB/RGBA: uses alpha channel - MS/MSA (multispectral): adds alpha channel to protect original data
When use_affine=True and mask_polygon is a rectangle, the image is saved with affine rotation in the transform (no mask needed). The current object is NOT modified (unless it was already in affine mode).
- Parameters:
save_path (str | Path) – The file path to save the geotiff
overwrite (bool, optional) – If True, overwrite existing file without prompting, by default False
apply_mask (bool, optional) – If True and mask exists, apply mask appropriately, by default True
use_affine (bool, optional) – “Convert and Save” mode. If True, try to use affine rotation storage for rectangular masks. If the object is ALREADY in affine mode (self.use_affine=True), this parameter is ignored (no double-conversion). Requires mask_polygon to be a valid rectangle (4 vertices, 90° angles). By default False.
- Returns:
True if save succeeded, False if cancelled
- Return type:
bool
Example
>>> import easyidp as idp >>> test_data = idp.data.TestData() >>> dsm = idp.GeoTiff(test_data.pix4d.lotus_dsm) >>> dsm.save('output_dsm.tif', overwrite=True) True
- property scale¶
A quick access to
self.header['scale'], please access theheaderdict to change value
- set_mask_polygon(polygon: ndarray, is_geo: bool = True) None¶
Set mask from polygon coordinates.
- Parameters:
polygon (np.ndarray) – (n, 2) polygon vertices. Auto-closes if first != last.
is_geo (bool, optional) – True if polygon is in geo coordinates, False for pixel. By default True.
Example
>>> gtiff = idp.GeoTiff(...) >>> roi_coords = np.array([[x1, y1], [x2, y2], ...]) >>> gtiff.set_mask_polygon(roi_coords, is_geo=True)
- property tie_point¶
A quick access to
self.header['tie_point'], please access theheaderdict to change value
- property use_affine: bool¶
Check if this GeoTiff uses affine rotation storage.
When True, the transform contains rotation and the entire image is valid (no mask needed). This is detected from the transform’s b and d coefficients being non-zero.
- Returns:
True if using affine rotation storage.
- Return type:
bool
- property width¶
A quick access to
self.header['width'], please access theheaderdict to change value