Skip to content

WIP: geometry lookup #854

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 58 commits into
base: main
Choose a base branch
from
Draft

WIP: geometry lookup #854

wants to merge 58 commits into from

Conversation

asinghvi17
Copy link
Collaborator

@asinghvi17 asinghvi17 commented Jan 8, 2025

TODOs:

  • CRS support
    • Make reproject reproject the geometry lookups too
    • In-memory resample using e.g zonal, to rasterize a VDC
  • Allow feature attributes to persist within geometry lookups
    • candidate 1: maybe have a metadata field?
    • candidate 2: table like feature collection structure, with metadata being a namedtuple of vectors...
  • Plot recipes - plottype on a dimvector with geometry lookup should check the trait and return poly. Then poly should have a recipe on a dimvector with geometry lookup.
  • Make everything return vectors, except when you ask for points
  • Add I/O support via CF conventions
    • Read: point, line, polygon
      • Actual reading given a dict
      • Hook up to CF
    • Write: point, line, polygon
      • [] Actual writing given a dict
      • Hook up to CF
  • Since we're depending on GeometryOps, use GeometryOps functions internally
  • Add test cases based on xvec tests
  • Translate all xvec examples

@felixcremer
Copy link
Contributor

How far is this? Is there anything I could help to push this through?

@asinghvi17
Copy link
Collaborator Author

asinghvi17 commented Apr 22, 2025

The big things we need are:

  • Zonal using geometrylookup to return a vector data cube
  • Zonal on VDC to give a view of all geoms that intersect the input geoms
  • resample from one geometrylookup to another

All of these are easy enough. I can push those today. What i really need is IO support via CF standards. I can probably figure out how to output but no idea how to parse input. We also need the dimarrays to tables thing to parse GeoJSON to a vdc, or some way to convert a GeoJSON wide table to a dimarray.

@rafaqz
Copy link
Owner

rafaqz commented Apr 22, 2025

Yeah really the big task we haven't started is having geometry writing for CF, maybe in CommonDataModel.jl

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rafaqz would like to get your opinion on this, not sure how breaking this will be to end users, who may now get an empty array where previously the function they provided wasn't even called.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from Slack this is extremely cool and we should do this

@asinghvi17
Copy link
Collaborator Author

I guess the place to do IO is:

  • _cdmlookup for read
  • _def_dim_var! for write
    ?

@rafaqz
Copy link
Owner

rafaqz commented Apr 22, 2025

Maybe we can just implement it all here first if that's easier

@asinghvi17
Copy link
Collaborator Author

Yeah that's what I'm trying to do with zonal for now, then I can just paste that file and tests to a new PR that does this for all zonal

@felixcremer
Copy link
Contributor

Near on a GeometryLookup should work but gives the following error:

Stacktrace:
  [1] _selnotfound(l::GeometryLookup{…}, selval::Float64)
    @ DimensionalData.Dimensions.Lookups ~/Documents/VectorDataCubes_Tutorial_EGU_2025/dev/DimensionalData/src/Lookups/selector.jl:233
  [2] _selnotfound_or_nothing(err::DimensionalData.Dimensions.Lookups._True, lookup::GeometryLookup{…}, selval::Float64)
    @ DimensionalData.Dimensions.Lookups ~/Documents/VectorDataCubes_Tutorial_EGU_2025/dev/DimensionalData/src/Lookups/selector.jl:231
  [3] at(::DimensionalData.Dimensions.Lookups.Unordered, ::DimensionalData.Dimensions.Lookups.NoSpan, lookup::GeometryLookup{…}, selval::Float64, atol::Nothing, rtol::Nothing; err::DimensionalData.Dimensions.Lookups._True)
    @ DimensionalData.Dimensions.Lookups ~/Documents/VectorDataCubes_Tutorial_EGU_2025/dev/DimensionalData/src/Lookups/selector.jl:216
  [4] at(::DimensionalData.Dimensions.Lookups.Unordered, ::DimensionalData.Dimensions.Lookups.NoSpan, lookup::GeometryLookup{…}, selval::Float64, atol::Nothing, rtol::Nothing)
    @ DimensionalData.Dimensions.Lookups ~/Documents/VectorDataCubes_Tutorial_EGU_2025/dev/DimensionalData/src/Lookups/selector.jl:213
  [5] at(lookup::GeometryLookup{…}, sel::At{…}; kw::@Kwargs{})
    @ DimensionalData.Dimensions.Lookups ~/Documents/VectorDataCubes_Tutorial_EGU_2025/dev/DimensionalData/src/Lookups/selector.jl:158
  [6] at(lookup::GeometryLookup{…}, sel::At{…})
    @ DimensionalData.Dimensions.Lookups ~/Documents/VectorDataCubes_Tutorial_EGU_2025/dev/DimensionalData/src/Lookups/selector.jl:157
  [7] near(order::DimensionalData.Dimensions.Lookups.Unordered, ::DimensionalData.Dimensions.Lookups.NoSampling, lookup::GeometryLookup{…}, sel::Near{…}; kw::@Kwargs{})
    @ DimensionalData.Dimensions.Lookups ~/Documents/VectorDataCubes_Tutorial_EGU_2025/dev/DimensionalData/src/Lookups/selector.jl:285
  [8] near(lookup::GeometryLookup{…}, sel::Near{…}; kw::@Kwargs{})
    @ DimensionalData.Dimensions.Lookups ~/Documents/VectorDataCubes_Tutorial_EGU_2025/dev/DimensionalData/src/Lookups/selector.jl:283
  [9] near(lookup::GeometryLookup{…}, sel::Near{…})
    @ DimensionalData.Dimensions.Lookups ~/Documents/VectorDataCubes_Tutorial_EGU_2025/dev/DimensionalData/src/Lookups/selector.jl:277
 [10] _selectindices
    @ ~/Documents/VectorDataCubes_Tutorial_EGU_2025/dev/DimensionalData/src/Lookups/selector.jl:1098 [inlined]
 [11] selectindices(l::GeometryLookup{…}, sel::Near{…}; kw::@Kwargs{})
    @ DimensionalData.Dimensions.Lookups ~/Documents/VectorDataCubes_Tutorial_EGU_2025/dev/DimensionalData/src/Lookups/selector.jl:1075
 [12] #_selecttuple#70
    @ ~/Documents/VectorDataCubes_Tutorial_EGU_2025/dev/DimensionalData/src/Lookups/selector.jl:370 [inlined]
 [13] _selecttuple
    @ ~/Documents/VectorDataCubes_Tutorial_EGU_2025/dev/DimensionalData/src/Lookups/selector.jl:368 [inlined]
 [14] selectindices
    @ ~/Documents/VectorDataCubes_Tutorial_EGU_2025/dev/DimensionalData/src/Lookups/selector.jl:1069 [inlined]
 [15] _dims2indices
    @ ~/Documents/VectorDataCubes_Tutorial_EGU_2025/dev/DimensionalData/src/Dimensions/indexing.jl:146 [inlined]
 [16] _dims2indices
    @ ~/Documents/VectorDataCubes_Tutorial_EGU_2025/dev/DimensionalData/src/Dimensions/indexing.jl:138 [inlined]
 [17] dims2indices
    @ ~/Documents/VectorDataCubes_Tutorial_EGU_2025/dev/DimensionalData/src/Dimensions/indexing.jl:31 [inlined]
 [18] macro expansion
    @ ~/Documents/VectorDataCubes_Tutorial_EGU_2025/dev/DimensionalData/src/Dimensions/indexing.jl:91 [inlined]
 [19] split_alignments(fa::typeof(DimensionalData.Dimensions.dims2indices), fu::typeof(DimensionalData.Dimensions.unalligned_dims2indices), lookups::Tuple{…}, dims::Tuple{…}, I::Tuple{…})
    @ DimensionalData.Dimensions ~/Documents/VectorDataCubes_Tutorial_EGU_2025/dev/DimensionalData/src/Dimensions/indexing.jl:91
 [20] split_alignments(fa::Function, fu::Function, dims::Tuple{…}, I::Tuple{…})
    @ DimensionalData.Dimensions ~/Documents/VectorDataCubes_Tutorial_EGU_2025/dev/DimensionalData/src/Dimensions/indexing.jl:89
 [21] dims2indices
    @ ~/Documents/VectorDataCubes_Tutorial_EGU_2025/dev/DimensionalData/src/Dimensions/indexing.jl:56 [inlined]
 [22] dims2indices
    @ ~/Documents/VectorDataCubes_Tutorial_EGU_2025/dev/DimensionalData/src/Dimensions/indexing.jl:32 [inlined]
 [23] _dim_getindex
    @ ~/Documents/VectorDataCubes_Tutorial_EGU_2025/dev/DimensionalData/src/array/indexing.jl:106 [inlined]
 [24] #getindex#176
    @ ~/Documents/VectorDataCubes_Tutorial_EGU_2025/dev/DimensionalData/src/array/indexing.jl:75 [inlined]
 [25] top-level scope
    @ REPL[69]:1
Some type information was truncated. Use `show(err)` to see complete types.

@asinghvi17
Copy link
Collaborator Author

Fixed that.

I was thinking about feature collections a bit more, and I think we can do this:

  • If the input is a feature collection, decompose to a vector of features
  • If the input is a table and has a geometry column, then collect the output of Tables.rows into a vector of GeoInterface features
  • Implement GI.getgeom(geomlookup, i) which we can use everywhere

Then this is automatically able to subset as well. And getindex etc just work.

@rafaqz
Copy link
Owner

rafaqz commented Apr 24, 2025

Sounds perfect.

The one downside is it will materialise a column table into rows, so allocates a lot. But users will learn to load the column if they only want the geoms

@asinghvi17
Copy link
Collaborator Author

asinghvi17 commented Apr 24, 2025

Yeah I don't see a way around rowtableizing unfortunately...maybe some lazy wrapper struct but that can DEFINITELY come later :D

@rafaqz
Copy link
Owner

rafaqz commented Apr 24, 2025

Yeah had the EXACT same thought

@rafaqz
Copy link
Owner

rafaqz commented Apr 24, 2025

We can also expand these back into columns in DimTable

@asinghvi17
Copy link
Collaborator Author

asinghvi17 commented Apr 24, 2025

Hm that would be interesting to do! Like an implicit dimstack

@asinghvi17
Copy link
Collaborator Author

actually yeah, I like the dimstack idea more now that I think about it, it seems way more natural than this but also harder to do

@felixcremer
Copy link
Contributor

Zonal on list of Tuples fails

Running zonal on the output of centroid I would have expected to get a Vector data cube with point entries.
Or is there a better way to get a point bsed vector data cube?

julia> zonal(identity, lai_da, of=Geometry(centroids))
ERROR: MethodError: no method matching npoint(::GeoInterface.PointTrait, ::Tuple{Float64, Float64})
The function `npoint` exists, but no method is defined for this combination of argument types.

Closest candidates are:
  npoint(::GeoInterface.PentagonTrait, ::Any)
   @ GeoInterface ~/.julia/packages/GeoInterface/4tyIo/src/fallbacks.jl:83
  npoint(::GeoInterface.QuadTrait, ::Any)
   @ GeoInterface ~/.julia/packages/GeoInterface/4tyIo/src/fallbacks.jl:81
  npoint(::GeoInterface.TriangleTrait, ::Any)
   @ GeoInterface ~/.julia/packages/GeoInterface/4tyIo/src/fallbacks.jl:77
  ...

Stacktrace:
  [1] npoint(geom::Tuple{Float64, Float64})
    @ GeoInterface ~/.julia/packages/GeoInterface/4tyIo/src/interface.jl:175
  [2] _burn_geometry!(B::Raster{…}, ::GeoInterface.PointTrait, geom::Tuple{…}; shape::Nothing, verbose::Bool, boundary::Symbol, allocs::Rasters.Allocs{…}, fill::Bool, kw::@Kwargs{})
    @ Rasters ~/Documents/VectorDataCubes_Tutorial_EGU_2025/dev/Rasters/src/methods/burning/geometry.jl:85
  [3] burn_geometry!(B::Raster{…}, data::Tuple{…}; kw::@Kwargs{})
    @ Rasters ~/Documents/VectorDataCubes_Tutorial_EGU_2025/dev/Rasters/src/methods/burning/geometry.jl:4
  [4] boolmask!(dest::Raster{…}, data::Tuple{…}; invert::Bool, lock::Nothing, progress::Bool, threaded::Bool, burncheck_metadata::DimensionalData.Dimensions.Lookups.Metadata{…}, allocs::Rasters.Allocs{…}, geometrycolumn::Nothing, collapse::Rasters.NoKW, kw::@Kwargs{})
    @ Rasters ~/Documents/VectorDataCubes_Tutorial_EGU_2025/dev/Rasters/src/methods/mask.jl:354
  [5] boolmask(x::Tuple{Float64, Float64}; to::Tuple{X{…}, Y{…}}, invert::Bool, geometrycolumn::Nothing, kw::@Kwargs{})
    @ Rasters ~/Documents/VectorDataCubes_Tutorial_EGU_2025/dev/Rasters/src/methods/mask.jl:317
  [6] _mask(x::Raster{…}, with::Tuple{…}; kw::@Kwargs{})
    @ Rasters ~/Documents/VectorDataCubes_Tutorial_EGU_2025/dev/Rasters/src/methods/mask.jl:85
  [7] _mask(x::Raster{…}, with::Tuple{…})
    @ Rasters ~/Documents/VectorDataCubes_Tutorial_EGU_2025/dev/Rasters/src/methods/mask.jl:83
  [8] mask(x::Raster{…}; with::Tuple{…}, kw::@Kwargs{})
    @ Rasters ~/Documents/VectorDataCubes_Tutorial_EGU_2025/dev/Rasters/src/methods/mask.jl:76
  [9] _zonal(f::Function, x::Raster{…}, ::GeoInterface.PointTrait, geom::Tuple{…}; skipmissing::Bool, spatialslices::DimensionalData.Dimensions.Lookups._True, missingval::Missing, kw::@Kwargs{})
    @ Rasters ~/Documents/VectorDataCubes_Tutorial_EGU_2025/dev/Rasters/src/methods/zonal.jl:112
 [10] _zonal
    @ ~/Documents/VectorDataCubes_Tutorial_EGU_2025/dev/Rasters/src/methods/zonal.jl:103 [inlined]
 [11] _zonal
    @ ~/Documents/VectorDataCubes_Tutorial_EGU_2025/dev/Rasters/src/methods/zonal.jl:97 [inlined]
 [12] _alloc_zonal(f::Function, x::Raster{…}, geoms::DimVector{…}, n::Int64; spatialslices::DimensionalData.Dimensions.Lookups._True, missingval::Missing, kw::@Kwargs{})
    @ Rasters ~/Documents/VectorDataCubes_Tutorial_EGU_2025/dev/Rasters/src/methods/zonal.jl:183
 [13] _zonal(f::Function, x::Raster{…}, ::Nothing, data::Geometry{…}; progress::Bool, threaded::Bool, geometrycolumn::Nothing, missingval::Missing, kw::@Kwargs{})
    @ Rasters ~/Documents/VectorDataCubes_Tutorial_EGU_2025/dev/Rasters/src/methods/zonal.jl:141
 [14] _zonal
    @ ~/Documents/VectorDataCubes_Tutorial_EGU_2025/dev/Rasters/src/methods/zonal.jl:136 [inlined]
 [15] _zonal(f::Function, x::Raster{…}, of::Geometry{…}; kw::@Kwargs{})
    @ Rasters ~/Documents/VectorDataCubes_Tutorial_EGU_2025/dev/Rasters/src/methods/zonal.jl:96
 [16] (::Rasters.var"#1047#1048"{})(xo::Raster{…})
    @ Rasters ~/Documents/VectorDataCubes_Tutorial_EGU_2025/dev/Rasters/src/methods/zonal.jl:77
 [17] open(f::Rasters.var"#1047#1048"{}, A::Raster{…}; kw::@Kwargs{})
    @ Rasters ~/Documents/VectorDataCubes_Tutorial_EGU_2025/dev/Rasters/src/array.jl:154
 [18] open(f::Function, A::Raster{…})
    @ Rasters ~/Documents/VectorDataCubes_Tutorial_EGU_2025/dev/Rasters/src/array.jl:149
 [19] #zonal#1046
    @ ~/Documents/VectorDataCubes_Tutorial_EGU_2025/dev/Rasters/src/methods/zonal.jl:76 [inlined]
 [20] top-level scope
    @ REPL[157]:1
Some type information was truncated. Use `show(err)` to see complete types.

@asinghvi17
Copy link
Collaborator Author

Extract will do it but it won't give you a VDC. Guess we have to get that next :)

But zonal probably doesn't support point geoms because it tries to burn them.

@rafaqz
Copy link
Owner

rafaqz commented Apr 25, 2025

Yeah extract is better for points, zonal doesn't have the point optimisations

@asinghvi17
Copy link
Collaborator Author

@rafaqz extract is now also multidimensional (but VERY inefficient)!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants