Skip to content

Using reproject in a threaded context makes Julia crash #287

Closed
@mathieu17g

Description

@mathieu17g

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

  1. 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)
  1. 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;
  1. 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)

Image

  1. 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

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions