Closed
Description
This issue may be linked to #88
Using reproject
in a threaded context makes Julia crash.
julia> versioninfo()
Julia Version 1.11.4
Commit 8561cc3d68 (2025-03-10 11:36 UTC)
Build Info:
Official https://julialang.org/ release
Platform Info:
OS: Windows (x86_64-w64-mingw32)
CPU: 20 × 13th Gen Intel(R) Core(TM) i7-13700H
WORD_SIZE: 64
LLVM: libLLVM-16.0.6 (ORCJIT, goldmont)
Threads: 20 default, 0 interactive, 10 GC (on 20 virtual cores)
Environment:
JULIA_NUM_THREADS = 20
JULIA_PKG_USE_CLI_GIT = false
JULIA_SSL_CA_ROOTS_PATH =
JULIA_EDITOR = code
(jl_tbk2nN) pkg> st
Status `C:\...\AppData\Local\Temp\jl_tbk2nN\Project.toml`
[150eb455] CoordinateTransformations v0.6.4
[e9467ef8] GLMakie v0.11.3
[68eda718] GeoFormatTypes v0.4.4
[cf35fbd7] GeoInterface v1.4.1
[db073c08] GeoMakie v0.7.11
[3251bfac] GeometryOps v0.1.16
[c94c279d] Proj v1.8.1
Below is some code to reproduce the crash
- Create a vector of MuliPolygon
import GeoInterface as GI
import CoordinateTransformations as CT
import GeometryOps as GO
# Lambert-93 (EPSG:2154) center (https://epsg.io/2154)
center_l93 = (489_353.59, 6_587_552.2)
# MultiPolygon pattern creation
exterior = GI.LinearRing(GI.Point.([(0, 0), (40_000, 0), (40_000, 40_000), (0, 40_000), (0, 0)]))
hole = GI.LinearRing(GI.Point.([(10_000, 10_000), (20_000, 10_000), (20_000, 20_000), (10_000, 20_000), (10_000, 10_000)]))
poly1 = GI.Polygon([exterior, hole])
poly2 = GO.transform(CT.Translation(50_000, 0), poly1) # Translate the polygon to the right by 50km
mpoly = GI.MultiPolygon([poly1, poly2])
# Create a 50x50 grid of MultiPolygons
N = 50
mpolys = [GO.transform(CT.Translation(((i - 1) % N) * 100_000, ((i - 1) ÷ N) * 50_000), mpoly) for i = 1:N^2]
# Translate the grid to the center of Lambert-93
mpolys = GO.transform.(Ref(CT.Translation(center_l93...)), mpolys)
- Define CRS and PROJ String for reprojection
# EPSG:2154 (Lambert-93) to EPSG:3857 (Web Mercator)
import Proj
import GeoFormatTypes as GFT
src_crs = Proj.CRS("EPSG:2154")
src_proj_string = GFT.ProjString(src_crs).val
dst_crs = Proj.CRS("EPSG:3857")
dst_proj_string = GFT.ProjString(dst_crs).val;
- Broadcasted
GeometryOps.reproject
works
# Define a plotting function to visualize the MultiPolygons
using GLMakie, GeoMakie
function show_mpolys(mpolys, src_proj_string, rmpolys, dst_proj_string)
fig = Figure(; size = (1000, 300))
ga1 = GeoAxis(fig[1, 1], source = src_proj_string; title = "Lambert-93 data source")
ga2 = GeoAxis(fig[1, 2], source = dst_proj_string; title = "Data source converted to Web Mercator")
for i = 1:N^2
plot!(ga1, mpolys[i])
plot!(ga2, rmpolys[i])
end
resize_to_layout!(fig)
return fig
end
GLMakie.activate!()
rmpolys1 = GO.reproject.(mpolys, Ref(src_crs), Ref(dst_crs))
fig1 = show_mpolys(mpolys, src_proj_string, rmpolys1, dst_proj_string)
display(GLMakie.Screen(title="GeometryOps.jl reproject"), fig1)
- Threaded
GeometryOps.reproject
on MultiPolygons leads to a Julia crash
rmpolys2 = fetch.([Threads.@spawn(GO.reproject(mpoly, src_proj_string, dst_proj_string)) for mpoly in mpolys])
Side issue, maybe worth a dedicated issue
While looking for a workaround to speed up my pipeline, I found that the performance of the broadcasted version of GeometryOps.reproject
could be enhanced when compared to a broadcasted MultiPolygon conversion with a Proj transformation
using BenchmarkTools
reproject_mp(T::Proj.Transformation, mp::GI.MultiPolygon) =
GI.MultiPolygon([[[[T(pt)...] for pt in pts] for pts in subgeom] for subgeom in GI.coordinates(mp)])
trans = Proj.Transformation(src_proj_string, dst_proj_string)
julia> @btime GO.reproject.($mpolys, Ref($src_crs), Ref($dst_crs));
489.643 ms (7662 allocations: 334.42 KiB)
julia> @btime reproject_mp.(Ref($trans), $mpolys);
2.640 ms (45460 allocations: 1.48 MiB)
Metadata
Metadata
Assignees
Labels
No labels