Skip to content
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

Suggested feature: area_intersection(poly1, poly2) #246

Open
milankl opened this issue Jan 8, 2025 · 6 comments
Open

Suggested feature: area_intersection(poly1, poly2) #246

milankl opened this issue Jan 8, 2025 · 6 comments

Comments

@milankl
Copy link

milankl commented Jan 8, 2025

Talking to @asinghvi17, for SpeedyWeather and Oceananigans (@simone-silvestri) we need (towards things like conservative interpolation between two grids on the sphere) we will need

GeometryOps.area(GeometryOps.intersection(GeometryOps.GEOS(), polygon1, polygon2))

to be as fast as possible as polygon1 and 2 will each come from large vectors of polygons (10k to 1mio) and all combinations need to be computed. See also SpeedyWeather/SpeedyWeather.jl#648. Our polygons are all simple and convex, having 4 vertices each, all much smaller than the radius of the sphere. However, we basically never need the actual intersection polygon materialized, we just need its area.

This is an issue to discuss how to get there, maybe an area_intersection(poly1, poly2) function? Or maybe other shortcuts that we could take.

@rafaqz
Copy link
Member

rafaqz commented Jan 8, 2025

It's probably much faster already not using GEOS

@milankl
Copy link
Author

milankl commented Jan 8, 2025

You mean just leave that argument out?

@milankl
Copy link
Author

milankl commented Jan 8, 2025

Throws an error

MethodError: no method matching TraitTarget(::Nothing)
The type `TraitTarget` exists, but no method is defined for this combination of argument types when trying to construct it.

Closest candidates are:
  TraitTarget(::T) where T<:GeoInterface.AbstractTrait
   @ GeometryOps ~/.julia/packages/GeometryOps/xlUi5/src/types.jl:41
  TraitTarget(::GeoInterface.AbstractTrait...)
   @ GeometryOps ~/.julia/packages/GeometryOps/xlUi5/src/types.jl:44
  TraitTarget(::Type{<:TraitTarget{T}}) where T
   @ GeometryOps ~/.julia/packages/GeometryOps/xlUi5/src/types.jl:43
  ...


Stacktrace:
 [1] intersection(geom_a::GeoInterface.Wrappers.Polygon{false, false, Vector{GeoInterface.Wrappers.LinearRing{false, false, Vector{Point{2, Float64}}, Nothing, Nothing}}, Nothing, Nothing}, geom_b::GeoInterface.Wrappers.Polygon{false, false, Vector{GeoInterface.Wrappers.LinearRing{false, false, Vector{Point{2, Float64}}, Nothing, Nothing}}, Nothing, Nothing}, ::Type{Float64}; target::Nothing, kwargs::@Kwargs{})
   @ GeometryOps ~/.julia/packages/GeometryOps/xlUi5/src/methods/clipping/intersection.jl:45
 [2] intersection(geom_a::GeoInterface.Wrappers.Polygon{false, false, Vector{GeoInterface.Wrappers.LinearRing{false, false, Vector{Point{2, Float64}}, Nothing, Nothing}}, Nothing, Nothing}, geom_b::GeoInterface.Wrappers.Polygon{false, false, Vector{GeoInterface.Wrappers.LinearRing{false, false, Vector{Point{2, Float64}}, Nothing, Nothing}}, Nothing, Nothing}, ::Type{Float64})
   @ GeometryOps ~/.julia/packages/GeometryOps/xlUi5/src/methods/clipping/intersection.jl:42
 [3] fraction(polygon::GeoInterface.Wrappers.Polygon{false, false, Vector{GeoInterface.Wrapper

@milankl
Copy link
Author

milankl commented Jan 16, 2025

@simone-silvestri and I have put our grids on top of each other

Image

to formulate our problem, we have two vectors of polygons on the sphere

grid1 = [poly11, poly12, poly13, ...]
grid2 = [poly21, poly22, poly23, ...]

now we need a matrix of size length(grid1) x length(grid2) where every element is the area of the intersect between a polygon from grid1 with a polygon from grid2. If we get this right, then summing the rows or columns would then yield the area of every polygon in grid1 or grid2. This matrix we can then use to interpolate conservatively from one grid to the other by simply "redistributing" values from one grid cell via the intersects into the overlapping grid cells from the other grid.

Let us consider the following example with a blue grid (4x 1x1 cells) and an orange grid (4 triangles and 1 diamond in the middle) numbered as denoted

Image

the areas would be (calculating by hand)

areas_blue_grid = [1, 1, 1, 1]
areas_orange_grid = [1/2, 1/2, 2, 1/2, 1/2]

and the matrix of the intersect areas would be (calculating by hand!)

julia> using SparseArrays
julia> SparseMatrixCSC([0 0 1/2 1/2 0; 0 0 1/2 0 1/2; 1/2 0 1/2 0 0; 0 1/2 1/2 0 0])
4×5 SparseMatrixCSC{Float64, Int64} with 8 stored entries:
         0.5  0.5   
         0.5      0.5
 0.5      0.5       
     0.5  0.5       

and summing rows/columns would yield the area vectors from above. So for any two grids this matrix is what we need for the conservative interpolation. If we can work together to calculate reasonably fast on the sphere that would be fantastic!!

@milankl
Copy link
Author

milankl commented Jan 16, 2025

To illlustrate how to do conservative interpolation between these two grids

  1. define the areas
julia> area1 = vec(sum(A, dims=2))
4-element Vector{Float64}:
 1.0
 1.0
 1.0
 1.0

julia> area2 = vec(sum(A, dims=1))
5-element Vector{Float64}:
 0.5
 0.5
 2.0
 0.5
 0.5
  1. start with some data on grid2 (the orange one)
julia> v2 = [5, 0, 0, 0, 0]
5-element Vector{Int64}:
 5
 0
 0
 0
 0

global integral is 2.5

julia> sum(v2 .* area2)
2.5
  1. interpolate onto other grid via multiplication with A and normalize by area on that grid
julia> v1 = (A*v2) ./ area1
4-element Vector{Float64}:
 0.0
 0.0
 2.5
 0.0

global integral is 2.5 again

julia> sum(v1 .* area1)
2.5
  1. interpolate back to old grid (this is diffusive mean variance not conserved, but mean should be!) via the transpose A' (no new interpolation operator needed!)
julia> v2back = (A'*v1) ./ area2
5-element Vector{Float64}:
 2.5
 0.0
 0.625
 0.0
 0.0

you can see how half of the 5 we started with diffused into another grid cell (as expected when they overlap). But the integral is still 2.5 🥳

julia> sum(v2back .* area2)
2.5

@simone-silvestri
Copy link

Probably interesting also for @gaelforget

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

No branches or pull requests

3 participants