odc.geo
This is still work in progress.
This repository contains geometry related code extracted from Open Datacube.
For details and motivation see ODC-EP-06 enhancement proposal.
This is still work in progress.
This repository contains geometry related code extracted from Open Datacube.
For details and motivation see ODC-EP-06 enhancement proposal.
Introduce special state _epsg=0, that implies "epsg code is not known yet", _epsg=None
means "epsg code is known to be absent".
Force all CRS construction code paths through cache.
CRS.utm(some_geom)
picks appropriate utm zone for a given geometry and returns corresponding CRS objectcrs="utm|utm-n|utm-s"
as a valid destination crs, meaning pick "appropriate" utm CRS for an object being transformed.lon,lat
of the centroid of the geometry~some_geom
are located using pyproj
, then zone with biggest overlap is chosenAdds GeoBox.snap_to(other)
, this method adjust source geobox slightly by shifting footprint by a sub-pixel amount such that pixel edges align exactly with pixel edges of the other
geobox. Method returns adjusted copy of self. Method only works when it is possible to align pixels by pure translation only - same projection, same resolution, same orientation. ValueError
is raised when it's not possible.
Closes #59
Related to snap_to
somewhat. GeoBox construction method that creates a new "pixel grid compatible" geobox that minimally covers some area.
Overload geobox[roi]
to support geometry types on input, footprint in the world can be converted to pixel space, from which pixel slice is computed.
Closes #31
Adding library description to readme and docs
Adding badges to readme on github
Adding readthedocs integration
Bumping version and fixing description in the wheel
Fixing up add some more __repr__
methods
Reverting changes to multipolygon
constructor function
rename odc.geo._overlap
to just odc.geo.overlap
fix in compute_reproject_roi
when dst is an integer shrink of source
There were a bunch of places where it was not clear whether some tuple contains X,Y or Y,X data. It's confusing on the interface and confusing when reading code. This PR adds an XY[T]
type and a bunch of constructor methods that make it easy to understand what order x,y parameters are added.
Instead of using plain tuples those new types are used instead. On the input shape=
parameters are still allowed to be supplied as plain (int, int)
tuples and are equivalent to iyx_(y, x)
. Internally those properties are kept in XY[int]
form. Similarly a 2d index is also allowed to be supplied as (int, int)
, as this is what allows some_thing[ix, iy]
or some_thing[row, col]
syntax, order depends on use case, but should be clearly stated in the docs.
Resolution input parameters are allowed to be a single float, but plain tuples are not allowed as this causes order confusion. Single value resolution is equivalent to resyx_(-value, value)
.
Geobox(width, height, ...)
interface is problematic for several
reasons:
ndarray
, where shape is used insteadReplacing width, height
with a single parameter shape
. This could be a simple (int, int)
tuple for easier interop with ndarray
. But one can choose to use more typesafe XY[int]
.
Example of the change at call site
# before change:
GeoBox(512, 256, A, "espg:4326")
# becomes
## if array order is "native" at call-site
GeoBox((256, 512), A, "epsg:4326")
## if width/height orded is preferred
GeoBox(ixy_(512, 256), A, "epsg:4326")
this failed
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
/tmp/ipykernel_19782/44836221.py in <cell line: 1>()
----> 1 newcheckdask = odc.geo.xr.colorize(newcheck['test'], cmap=newcmp,vmin=1,vmax=240)
~/exploracornzarr/lib/python3.8/site-packages/odc/geo/_rgba.py in colorize(x, cmap, attrs, clip, vmin, vmax)
240
241 assert x.chunks is not None
--> 242 data = da.map_blocks(
243 _impl,
244 x.data,
~/exploracornzarr/lib/python3.8/site-packages/dask/array/core.py in map_blocks(func, name, token, dtype, chunks, drop_axis, new_axis, meta, *args, **kwargs)
732 adjust_chunks = None
733
--> 734 out = blockwise(
735 func,
736 out_ind,
~/exploracornzarr/lib/python3.8/site-packages/dask/array/blockwise.py in blockwise(func, out_ind, name, token, dtype, adjust_chunks, new_axes, align_arrays, concatenate, meta, *args, **kwargs)
274 from .utils import compute_meta
275
--> 276 meta = compute_meta(func, dtype, *args[::2], **kwargs)
277 return new_da_object(graph, out, chunks, meta=meta, dtype=dtype)
278
~/exploracornzarr/lib/python3.8/site-packages/dask/array/utils.py in compute_meta(func, _dtype, *args, **kwargs)
158 return None
159
--> 160 if _dtype and getattr(meta, "dtype", None) != _dtype:
161 with contextlib.suppress(AttributeError):
162 meta = meta.astype(_dtype)
~/exploracornzarr/lib/python3.8/site-packages/dask/delayed.py in __bool__(self)
588
589 def __bool__(self):
--> 590 raise TypeError("Truth of Delayed objects is not supported")
591
592 __nonzero__ = __bool__
TypeError: Truth of Delayed objects is not supported
this works - but as your docs suggest, gives strange results
newcheckdask = odc.geo.xr.colorize(newcheck['test'], cmap=newcmp,vmin=1,vmax=240)
Have I done something wrong, or is there a bug?
GeoBox.compat
to convert to datacube version of GeoBox
nan
as fill value for float32
outputs if output nodata
is not configured
colorize
handle nodata
values properly for non float inputs
colorize
drop nodata
attribute on the output arrayESRI
Geometry[Point] -> XY[float]
conversionGeometry.__iter__
interface to follow shapely
deprecation of that.odc.add_to
for maps with custom projectionsCode in xx.odc.geobox
assumes that xarray raster data is axis aligned. That is the off-diagonal terms of the affine matrix are zero and so entire row of pixels will have the same Y coordinate and similarly entire column same X coordinate:
|y\x|0|1|2|3| |-|-|-|-|-| |0|0,0|1,0|2,0|3,0| |1|0,1|1,1|2,1|3,1|
This limitation means one can not use .odc.geobox
on rotated images. For a lot of satellite data one swatch of observation happens on an angle, when this gets rendered into axis aligned image you end up with a large proportion of the mosaic image being empty. Supporting arbitrary affine transform could make it much easier to work with large scale mosaics for such data sources.
Xarray supports 2d coordinate, so one could simply compute X,Y coordinate for every pixel and store that in two 2d arrays, one for X and one for Y.
pix = img[r,c]
coord = (img.x[c], img.y[r]) # axis aligned representation
...
coord = (img.x[r,c], img.y[r,c]) # 2d-axis representation
.odc.geobox
could then detect that 2d axis are used and reconstruct affine matrix from coordinates similar to current implementation but it will need to be computed from 3 points (Affine matrix has 6 degrees of freedom).
In this scheme we keep 1-d X,Y axis, but they contain pixel coordinates and not real world coordinates, so x = [0.5, 1.5, 2.5... W-0.5]
, that sort of thing. We then also need to store actual affine matrix in some attributes attached to x,y
axis.
In this scenario .odc.geobox
needs to detect the fact that X,Y
axis contain pixel coordinates rather than real world coordinates and instead extract affine matrix from attributes/encoding. Slicing should still work as we will have access to the original image coordinates which is what we need to compute world coordinates from. Need to experiment with the location and representation for affine matrix data and how that works with things like .to_netcdf|to_zarr
.
affine format: 6 float values (string vs array vs dict)
attachment point: x,y
axis vs crs
axis vs band
:+1: Same memory requirements as axis aligned representation
:-1: xarray plotting won't display more than one dataset on the same axis properly
Currently testing transitioning from datacube to odc-geo in geocube: https://github.com/corteva/geocube/pull/95
Have some scenarios in the tests where the shape is off by one: odc-geo: (y: 100001, x: 11) datacube: (y: 100001, x: 12)
And the boundary differs: odc-geo: [1665945., 7018509., 1665478., 7018306.] datacube: [1665478., 7018306., 1665945., 7018509.]
Are these changes expected?
When fitting polynomial use lower rank if only few points available.
Closes #67
BoundingBox.boundary()
generate points along the perimiter{BoundingBox|GeoBox}.qr2sample
quasi-random 2d sampling using R2 sequenceGeometry.assign_crs
use different CRS without changing geometry itselfGeometry.transform(..., crs=)
can now optionally change CRS of the transformed geometryodc.geo.geom.triangulate
wrapper for for shapely methodMaybeCRS
now includes special Unset
case to differentiate from None
Geometry.geojson
(support composite and crs=None
geometries)rio_geobox
helper method in convertersto_grba
to use vmin=, vmax=
instead of non-standard clamp=
BoundingBox was an odd one out without CRS info attached, now it has that
Changes to GeoBox.from_bbox
Essentially if you take 0,0
in the real world, bring it to pixel space and locate pixel that contains it, then you want location within that pixel to be a certain point in [0, 1), [0, 1)
, so 0,0
is edge aligned, same as align=(0,0)
used to be, and to get center aligned pixel you pick (0.5, 0.5)
. Also you can just not align at all, in which case resulted geobox will start at left,top and might go a little bit over right, bottom.
Also adds some more convenience methods to bounding box and geobox.
Closes #25
I've been trying to use odc-geo
to define the target grid for regridding with xesmf
. This works great so far, but one of the features I've been missing so far is creating a Dataset
from the GeoBox
.
The naive version I've been using so far
def to_dataset(geobox):
def to_variable(dim, coord):
data = coord.values
units = coord.units
resolution = coord.resolution
if resolution < 0:
data = data[::-1]
resolution *= -1
return xr.Variable(
dims=dim, data=data, attrs={"units": units, "resolution": resolution}
)
coords = {
name: to_variable(name, odc_coord)
for name, odc_coord in geobox.coordinates.items()
}
return xr.Dataset(coords=coords)
which obviously lacks support for affine transformations, assumes 1D coordinates, and the resolution trick should probably configurable (and off by default?)
Did I miss anything? Do you know of a better way to do this? If not, would you be open to adding something like this to odc.geo.xr
?
Currently odc-geo
is tested in one single configuration: python 3.8 with optional dependencies installed to maximize test coverage. We should be testing things with minimal dependencies and with other versions of Python.
Related to: opendatacube/datacube-core#752
Currently the resolution
inside of the coordinate attributes are used:
https://github.com/opendatacube/datacube-core/blob/05093b75a8f15b643c7047502ae27b662af2d9b4/datacube/utils/xarray_geoextensions.py#L133-L139
GeoTransform
is a property used by GDAL to store this information (https://gdal.org/drivers/raster/netcdf.html#georeference)
Here is an example of reading in this property: https://github.com/corteva/rioxarray/blob/f658d5f829a88204b2ec7b239735aba31694ed0a/rioxarray/rioxarray.py#L540-L545
Thoughts about supporting this as well?
[DONE]
~Output folium
or ipyleaflet
maps when libraries are available when displaying geometries/geoboxes/bounding boxes.~Most of the low-level utilities needed for Dask-backed reprojection are already in odc-geo
. This is mostly
https://github.com/opendatacube/odc-geo/blob/11c59371d306e010e8cbc4b4b835a80005e3ec4e/odc/geo/geobox.py#L909
and also: https://github.com/opendatacube/odc-geo/blob/11c59371d306e010e8cbc4b4b835a80005e3ec4e/odc/geo/overlap.py#L130
Expected interface:
xx = dc.load(.., dask_chunks= {}) # or any other supported load backend
# automatically choose resolution and bounding box, align pixel edges to 0
# automatically choose chunk size
yy = xx.odc.to_crs("epsg:3857")
# fully defined destination pixel plane
# configurable destination chunking
yy = xx.odc.reproject(GeoBox.from_bbox(..), chunks={'x': 2048, 'y': 4096})
..., y, x[,band]
We are caching pyproj.CRS
objects here:
https://github.com/opendatacube/odc-geo/blob/202cd8aeb3b03a54d9e484c3514c19a8b57b1a7b/odc/geo/_crs.py#L31-L32
And pyproj
transformers here:
https://github.com/opendatacube/odc-geo/blob/202cd8aeb3b03a54d9e484c3514c19a8b57b1a7b/odc/geo/_crs.py#L47-L48
AnchorEnum
to the docs by @Kirill888 in https://github.com/opendatacube/odc-geo/pull/78Full Changelog: https://github.com/opendatacube/odc-geo/compare/v0.3.2...v0.3.3
Source code(tar.gz)Fixes import issues on Python 3.10 introduced in previous release
Full Changelog: https://github.com/opendatacube/odc-geo/compare/v0.3.1...v0.3.2
Source code(tar.gz)rasterio.crs.CRS -> odc.geo.crs.CRS
construction path.epsg
unless requested by the user. This is an expensive operation for CRSs that do now have EPSG code, https://github.com/opendatacube/datacube-core/issues/1321Full Changelog: https://github.com/opendatacube/odc-geo/compare/v0.3.0...v0.3.1
Source code(tar.gz)type=FeatureCollection
GeoBox
, can slice with any geometry, geobox or boundingboxGeoBox
that match the grid of other geobox
GeoBox.snap_to
, GeoBox.enclosing
.project
rio_geobox
converter method to read in geobox from rasterio file handle_FillValue
attribute populated by rioxarray
BoundingBox
class
.boundary
.aoi
.qr2sample(..)
Geometry.assign_crs(..)
and optional crs=
in Geometry.transform(op, crs=..)
.geojson(..)
generation from geometries.dropna()
to handle holes in polygonsFull Changelog: https://github.com/opendatacube/odc-geo/compare/v0.2.2...v0.3.0
Source code(tar.gz)src_nodata
kwarg in xr_reproject
by @valpesendorfer in https://github.com/opendatacube/odc-geo/pull/60nan
and inf
values in a more robust way than beforeGeometry.filter
and Geometry.dropna
operations__array_interface__
from Geometry
class
shapely
and is not used, and newer numpy warns about itnumpy.asarray(g.coords)
instead anywayGeometry
objectsFull Changelog: https://github.com/opendatacube/odc-geo/compare/v0.2.1...v0.2.2
Source code(tar.gz)colorize|add_to
when plotting data with missing pixels.odc.add_to
for maps with custom projections #50GeoBox.compat
to convert to datacube version of GeoBox
robust=True
in colorize|add_to
, user percentiles for clipping: vmin=2%, vmax=98%
#52Geometry[Point] -> XY[float]
conversionDeprecated iteration over Geometry
class to follow shapely
deprecation of that, use .geoms
property instead.
See this PR: https://github.com/opendatacube/odc-geo/pull/54
Full Changelog: https://github.com/opendatacube/odc-geo/compare/v0.2.0...v0.2.1
Source code(tar.gz).add_to(map)
.map_bounds
to interface with folium/ipyleaflet.to_rgba
.colorize
.compress
(jpeg,png,webp for map displays)GeoBox.to_crs
method (convenient access to output_geobox
method)Full Changelog: https://github.com/opendatacube/odc-geo/compare/v0.1.3...v0.2.0
Source code(tar.gz)xarray.DataArray
with GeoBoxes that are not aligned to X/Y axis (i.e. have rotation/shear components)chunks=
and time=
optional arguments to xr_zeros(..)
Bug fix release
BoundingBox
is now CRS awareGeoBox.from_
interfaces, anchor=
replaces align=
.from_polygon
still accepts old-style align=
for ease of datacube migration.odc.write_cog
, .odc.to_cog
methods.odc.reproject
(only supports non-Dask inputs currently).odc.output_geobox
- finds reasonable destination geobox when projecting to other CRSGeoBox
display code (was not handling empty cases well)First release
Source code(tar.gz)First release candidate.
Source code(tar.gz)EUROPE TRIVIA QUIZ IN PYTHON Project Outline Ask user if he / she knows more about Europe. If yes show the Trivia main screen, else show the end Trivi
h3-js The h3-js library provides a pure-JavaScript version of the H3 Core Library, a hexagon-based geographic grid system. It can be used either in No
odc.geo This is still work in progress. This repository contains geometry related code extracted from Open Datacube. For details and motivation see OD
Pandana Pandana is a Python library for network analysis that uses contraction hierarchies to calculate super-fast travel accessibility metrics and sh
rasterstats rasterstats is a Python module for summarizing geospatial raster datasets based on vector geometries. It includes functions for zonal stat
geojsonio.py Open GeoJSON data on geojson.io from Python. geojsonio.py also contains a command line utility that is a Python port of geojsonio-cli. Us
List of Land Cover datasets in the GEE Catalog A list of all the Land Cover (or discrete) datasets in Google Earth Engine. Values, Colors and Descript
pyTractive GPS Python module and script to interact with the Tractive GPS tracker. Requirements Python 3 geopy folium pandas pillow usage: main.py [-h
Geolocator A Geolocator made in python ✨ Features locates ur location using ur ip thats it! 💁♀️ How to use first download the locator.py file instal
xmastree2021 Code and coordinates for Matt's 2021 xmas tree This repository contains the code and coordinates used for Matt's 2021 Christmas tree, as
A small set of Python functions to draw pretty maps from OpenStreetMap data. Based on osmnx, matplotlib and shapely libraries.
gpdvega gpdvega is a bridge between GeoPandas a geospatial extension of Pandas and the declarative statistical visualization library Altair, which all
kaibu-utils A set of utility functions for working with Kaibu. Create a new repository Create a new repository and select imjoy-team/imjoy-python-temp
streamlit-folium This Streamlit Component is a work-in-progress to determine what functionality is desirable for a Folium and Streamlit integration. C
Morecantile +-------------+-------------+ ymax | | | | x: 0 | x: 1 | | y: 0 | y: 0
pypostal These are the official Python bindings to https://github.com/openvenues/libpostal, a fast statistical parser/normalizer for street addresses
Table of Contents What is GeoNode? Try out GeoNode Install Learn GeoNode Development Contributing Roadmap Showcase Most useful links Licensing What is
Emport Overview This repo provides a FDTD (Finite Differences Time Domain) simulator called emport for solving RF circuits. Emport outputs its simulat
Yet Another Timeseries Model (YATSM) master v0.6.x-maintenance Build Coverage Docs DOI | About Yet Another Timeseries Model (YATSM) is a Python packag
Geometry Sketcher Constraint-based sketcher addon for Blender that allows to create precise 2d shapes by defining a set of geometric constraints like