JuliaGeo / GeometryOps.jl

GeoInterface-based geometry operations
https://juliageo.org/GeometryOps.jl/
MIT License
29 stars 4 forks source link

Quick way of rotating a polygon? #192

Open ConnectedSystems opened 3 months ago

ConnectedSystems commented 3 months ago

Before I get too stuck into writing my own methods, is there any way of rotating an arbitrary polygon by some degrees?

I'm hoping for something like rotate(poly, 10.0)

rafaqz commented 3 months ago

You can use any package like Rotations.jl in GeometryOps.transform. transformwill give you an SVector for each point.

It will work on any geometry or arrays/feature collections/dataframes of geometries.

asinghvi17 commented 3 months ago

If using Rotations.jl you will need to wrap it in a CoordinateTransformations.LinearMap or so.

ConnectedSystems commented 3 months ago

Can't get this to work though I feel as I am halfway there.

@asinghvi17 what do you mean by wrap "it" with a LinearMap. Wrap what exactly? The RotMatrix? Any further pointers would be appreciated.

asinghvi17 commented 3 months ago

GO.transform(transf, geom) works by calling transf(point) for each point in geom. Rotations.jl doesn't subscribe to the CoordinateTransformations.jl way of doing things (which is just making the struct callable) and is also purely 3D, so you need to adjust it for 2D anyway.

Consider the following:


julia> import GeometryOps as GO; import Rotations, CoordinateTransformations

julia> points = tuple.(rand(10), randn(10));

julia> GO.transform(Rotations.RotX(π/4), points)
ERROR: MethodError: objects of type RotX{Float64} are not callable
Use square brackets [] for indexing an Array.
Stacktrace:
[...]

julia> rotation_matrix = Rotations.RotX(π/4)[1:2, 1:2] |> CoordinateTransformations.LinearMap
LinearMap([1.0 0.0; 0.0 0.7071067811865476])

julia> GO.transform(rotation_matrix, points)
[...]
ConnectedSystems commented 3 months ago
rotation_matrix = Rotations.RotX(π/4)[1:2, 1:2] |> CoordinateTransformations.LinearMap

This was what I was missing!

I was playing around with the GO.transform(f, geom) method instead.

Thanks! Now I can replace my crude rotation function.

ConnectedSystems commented 3 months ago

Actually, this seems to only work for lines.

For other more complex geoms, it doesn't behave as I would expect.

using CoordinateTransformations, Rotations
using GLMakie, GeoMakie

pnts = [(1, 3), (3, 3), (3, 1), (1, 1), (1,3)];
rotation_matrix = RotX(0.0)[1:2, 1:2] |> LinearMap
poly(pnts, alpha=0.2)
for deg in [45.0, 90.0, 180.0]
    rotation_matrix = RotX(deg2rad(deg))[1:2, 1:2] |> LinearMap
    rot_geom = Tuple.(GO.transform(rotation_matrix, pnts))
    poly!(rot_geom, alpha=0.3)
end

image

asinghvi17 commented 3 months ago

My bad, it should have been RotZ.

using CoordinateTransformations, Rotations
using GLMakie, GeoMakie
import GeometryOps as GO

pnts = [(1, 3), (3, 3), (3, 1), (1, 1), (1,3)];
rotation_matrix = RotZ(0.0)[1:2, 1:2] |> LinearMap
poly(pnts, alpha=0.2)
for deg in [45.0, 90.0, 180.0]
    rotation_matrix = RotZ(deg2rad(deg))[1:2, 1:2] |> LinearMap
    rot_geom = Tuple.(GO.transform(rotation_matrix, pnts))
    poly!(rot_geom, alpha=0.3)
end

iTerm2 yePz4z

You can also create your own matrix fairly easily:

using StaticArrays # so we don't allocate
rotmatrix2d(a #= angle =#) = StaticArrays.Mat2(cos(a), sin(a), -sin(a), cos(a))

poly(pnts, alpha=0.2)
for deg in [45.0, 90.0, 180.0]
    rotation_matrix = rotmatrix2d(deg2rad(deg)) |> LinearMap
    rot_geom = Tuple.(GO.transform(rotation_matrix, pnts))
    poly!(rot_geom, alpha=0.3)
end