From 3fab2a42691f6097bf811278a2e372a4584071b4 Mon Sep 17 00:00:00 2001 From: Anshul Singhvi Date: Fri, 5 Apr 2024 22:57:34 -0400 Subject: [PATCH 01/16] Add benchmarks for GeometryOps.jl (a Julia package) --- geometryops/Project.toml | 9 +++ geometryops/distance.jl | 20 +++++++ geometryops/intersects.jl | 23 ++++++++ geometryops/load.jl | 0 geometryops/plots.jl | 114 ++++++++++++++++++++++++++++++++++++++ geometryops/sample.jl | 47 ++++++++++++++++ geometryops/transform.jl | 22 ++++++++ geometryops/utils.jl | 25 +++++++++ geometryops/write.jl | 0 run_benchmarks.sh | 11 ++++ 10 files changed, 271 insertions(+) create mode 100644 geometryops/Project.toml create mode 100644 geometryops/distance.jl create mode 100644 geometryops/intersects.jl create mode 100644 geometryops/load.jl create mode 100644 geometryops/plots.jl create mode 100644 geometryops/sample.jl create mode 100644 geometryops/transform.jl create mode 100644 geometryops/utils.jl create mode 100644 geometryops/write.jl mode change 100644 => 100755 run_benchmarks.sh diff --git a/geometryops/Project.toml b/geometryops/Project.toml new file mode 100644 index 0000000..0b6611c --- /dev/null +++ b/geometryops/Project.toml @@ -0,0 +1,9 @@ +[deps] +CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0" +Chairmarks = "0ca39b1e-fe0b-4e98-acfc-b1656634c4de" +DelimitedFiles = "8bb1440f-4735-579b-a4ab-409b98df4dab" +GMT = "5752ebe1-31b9-557e-87aa-f909b540aa54" +GeoInterface = "cf35fbd7-0cd7-5166-be24-54bfbe79505f" +GeometryOps = "3251bfac-6a57-4b6d-aa61-ac1fef2975ab" +Proj = "c94c279d-25a6-4763-9509-64d165bea63e" +SwarmMakie = "0b1c068e-6a84-4e66-8136-5c95cafa83ed" diff --git a/geometryops/distance.jl b/geometryops/distance.jl new file mode 100644 index 0000000..96436b5 --- /dev/null +++ b/geometryops/distance.jl @@ -0,0 +1,20 @@ +import GeometryOps as GO, GeoInterface as GI +using CairoMakie +import GMT # easiest package to use to load Geopackage files +using Chairmarks + +include("utils.jl") + +# Load the data +data_path = joinpath(dirname(@__DIR__), "data") +points_gpkg = GMT.gmtread(joinpath(data_path, "points.gpkg")) +point_set = map(view(points_gpkg, 1:4000)) do row + Point2d(row[1], row[2]) +end +# Benchmark the distance function +# This uses the `Chairmarks.jl` package to benchmark the `GO.distance` function. +# The benchmark will run for 15 seconds. +distance_benchmark = @be [GO.distance(point1, point2) for point1 in $point_set, point2 in $point_set] seconds=15 + +# Write the results to a CSV file +write_benchmark_as_csv(distance_benchmark; task = "distance") \ No newline at end of file diff --git a/geometryops/intersects.jl b/geometryops/intersects.jl new file mode 100644 index 0000000..d682aff --- /dev/null +++ b/geometryops/intersects.jl @@ -0,0 +1,23 @@ +import GeometryOps as GO, GeoInterface as GI +using CairoMakie +import GMT # easiest package to use to load Geopackage files +using Chairmarks + +include("utils.jl") + +# Load the data +data_path = joinpath(dirname(@__DIR__), "data") +points_gpkg = GMT.gmtread(joinpath(data_path, "points.gpkg")) +polygon_gpkg = GMT.gmtread(joinpath(data_path, "polygon.gpkg")) +# Process it into a Julia form +point_set = map(points_gpkg) do row + Point2d(row[1], row[2]) +end +polygon = GI.Polygon([GI.LinearRing(Point2d.(eachrow(polygon_gpkg)))]) +# Benchmark the distance function +# This uses the `Chairmarks.jl` package to benchmark the `GO.distance` function. +# The benchmark will run for 15 seconds. +benchmark = @be GO.intersects.(point_set, (polygon,)) seconds=15 + +# Write the results to a CSV file +write_benchmark_as_csv(benchmark; task = "intersects") \ No newline at end of file diff --git a/geometryops/load.jl b/geometryops/load.jl new file mode 100644 index 0000000..e69de29 diff --git a/geometryops/plots.jl b/geometryops/plots.jl new file mode 100644 index 0000000..60a1981 --- /dev/null +++ b/geometryops/plots.jl @@ -0,0 +1,114 @@ +using CairoMakie, AlgebraOfGraphics, DelimitedFiles, Statistics + +results_path = joinpath(dirname(@__DIR__), "results") +results_files = readdir(results_path; join = false) +regex = r"(\w+)-(\w+).csv" +regex_matches = match.(regex, results_files) +results = map(regex_matches) do match + task, package = match.captures + data = DelimitedFiles.readdlm(joinpath(results_path, "$task-$package.csv"), ';', header = true) + (task, package, parse.(Float64, replace.(data[1][:, end], ("," => ".",)))) +end + +m1 = regex_matches[1] +task, package = m1.captures +data = DelimitedFiles.readdlm(joinpath(results_path, "$task-$package.csv"), ';', header = true) +data[1][:, end] + + +f = Figure() + +for (idx, task) in enumerate(unique(first.(results))) + records = filter(r -> r[1] == task, results) + colors = fill(Makie.wong_colors()[1], length(records)) + records_ind = findfirst(==("geometryops"), getindex.(records, 2)) + if !isnothing(records_ind) + println("Found GeometryOps for $task, coloring it") + colors[findfirst(==("geometryops"), getindex.(records, 2))] = Makie.wong_colors()[3] + end + a, p = barplot(f[idx, 1], + 1:length(records), + Statistics.median.(last.(records)); + color = colors, + direction = :x, + axis = (; + title = task, + yticks = (1:length(records), getindex.(records, 2)), + ylabel = "Package", + xlabel = "Median time (s)", + ) + ) + display(f) +end + +resize!(f, 800, 1500) +f + +# summary plot + +marker_map = Dict( + # R packages + "sf" => :utriangle, + "s2" => :utriangle, + "terra" => :utriangle, + "geos" => :utriangle, + # Python package + "geopandas" => :circle, + # Julia package + "geometryops" => :rect, +) + +color_map = Dict( + # R packages + "sf" => Makie.wong_colors()[1], + "s2" => Makie.wong_colors()[2], + "terra" => Makie.wong_colors()[3], + "geos" => Makie.wong_colors()[4], + # Python package + "geopandas" => Makie.wong_colors()[5], + # Julia package + "geometryops" => Makie.wong_colors()[6], +) + +result_tasks = getindex.(results, 1) .|> string +result_pkgs = getindex.(results, 2) .|> string +result_times = Statistics.median.(getindex.(results, 3)) + +using SwarmMakie + +using CategoricalArrays + +ca = CategoricalArray(result_tasks) +f, a, p = beeswarm( + ca.refs, result_times; + marker = getindex.((marker_map,), result_pkgs), + color = getindex.((color_map,), result_pkgs), + axis = (; + xticks = (1:length(ca.pool.levels), ca.pool.levels), + xlabel = "Task", + ylabel = "Median time (s)", + ) +) + + + +Legend( + f[1, 2], + +) + +aog_data = AlgebraOfGraphics.data( + (; + tasks = result_tasks, times = result_times, + markers = , + colors = +) +) * mapping( + :tasks, :times; + marker = :markers => nonnumeric, + color = :colors => nonnumeric +) * visual(Beeswarm) |> draw + + + +beeswarm \ No newline at end of file diff --git a/geometryops/sample.jl b/geometryops/sample.jl new file mode 100644 index 0000000..79d30aa --- /dev/null +++ b/geometryops/sample.jl @@ -0,0 +1,47 @@ +import GeometryOps as GO, GeoInterface as GI +using CairoMakie +import GMT # easiest package to use to load Geopackage files +using Chairmarks +using Proj # activate GeometryOps reprojection +include("utils.jl") # include saving utilities as common code + +# Load the data +data_path = joinpath(dirname(@__DIR__), "data") +points_gpkg = GMT.gmtread(joinpath(data_path, "points.gpkg")) +polygon_gpkg = GMT.gmtread(joinpath(data_path, "polygon.gpkg")) +# Process it into a Julia form +point_set = map(points_gpkg) do row + Point2d(row[1], row[2]) +end +polygon = GI.Polygon([GI.LinearRing(Point2d.(eachrow(polygon_gpkg)))]) +# Create a `sample` function in the vein of the Python function + +function _sample(polygon, size) + ext = GI.extent(polygon) + xmin, xmax = ext.X + ymin, ymax = ext.Y + + count = 0 + points = Point2d[] + + while count < size + x = xmin + (xmax - xmin) * rand() + y = ymin + (ymax - ymin) * rand() + point = Point2d(x, y) + if GO.contains(polygon, point) + push!(points, point) + count += 1 + end + end + + return points + +end + +# Benchmark the `_sample` function +# This uses the `Chairmarks.jl` package to benchmark the `GO.distance` function. +# The benchmark will run for 15 seconds. +benchmark = @be _sample($polygon, 100_000) seconds=15 + +# Write the results to a CSV file +write_benchmark_as_csv(benchmark; task = "sample") \ No newline at end of file diff --git a/geometryops/transform.jl b/geometryops/transform.jl new file mode 100644 index 0000000..ecc262b --- /dev/null +++ b/geometryops/transform.jl @@ -0,0 +1,22 @@ +import GeometryOps as GO, GeoInterface as GI +using CairoMakie +import GMT # easiest package to use to load Geopackage files +using Chairmarks + +using Proj # activate GeometryOps reprojection + +include("utils.jl") + +# Load the data +data_path = joinpath(dirname(@__DIR__), "data") +points_gpkg = GMT.gmtread(joinpath(data_path, "points.gpkg")) +point_set = map(points_gpkg) do row + Point2d(row[1], row[2]) +end +# Benchmark the reproject function +# This uses the `Chairmarks.jl` package to benchmark the `GO.distance` function. +# The benchmark will run for 15 seconds. +benchmark = @be GO.reproject($point_set, $(points_gpkg[1].proj4), $("EPSG:4326"); always_xy = false) seconds=15 + +# Write the results to a CSV file +write_benchmark_as_csv(benchmark; task = "transform") \ No newline at end of file diff --git a/geometryops/utils.jl b/geometryops/utils.jl new file mode 100644 index 0000000..c74b257 --- /dev/null +++ b/geometryops/utils.jl @@ -0,0 +1,25 @@ +import DelimitedFiles, Chairmarks + +""" + write_benchmark_as_csv(benchmark::Chairmarks.Benchmark; package = "geometryops", task = "distance") + +Write the results of a Chairmarks benchmark to a CSV file. The CSV file will be written to the +""" +function write_benchmark_as_csv(benchmark::Chairmarks.Benchmark; package = "geometryops", task = "distance") + # Extract the time results from the Chairmarks benchmark. + # These are in the form of `Chairmarks.Sample` objects which have various + # properties like `time`, `memory`, etc. + times = getproperty.(benchmark.samples, :time) + # Now, we generate each row of the CSV file as a vector of strings. + time_rows = map(times) do time + [package, task, replace(string(round(time, digits=4)), "." => ",")] + end + # Write the results to a CSV file + header_vec = ["task", "package", "time"] + + DelimitedFiles.writedlm( + joinpath(dirname(@__DIR__), "results", "$task-$package.csv"), + permutedims(hcat(header_vec, time_rows...)), + ';' + ) +end diff --git a/geometryops/write.jl b/geometryops/write.jl new file mode 100644 index 0000000..e69de29 diff --git a/run_benchmarks.sh b/run_benchmarks.sh old mode 100644 new mode 100755 index 190ca96..bfd2db0 --- a/run_benchmarks.sh +++ b/run_benchmarks.sh @@ -2,6 +2,7 @@ R_packages=(sf terra s2 geos) Python_packages=(geopandas) +Julia_packages=(geometryops) mkdir results @@ -24,3 +25,13 @@ do python3 "$path" done done + +# run Julia benchmarks +for i in ${Python_packages[*]} +do + for path in "${i}"/*.jl + do + echo "$path" + julia --project=./geometryops "$path" + done +done \ No newline at end of file From b95c67766d9837f0f5eab025a899f89746d14008 Mon Sep 17 00:00:00 2001 From: Anshul Singhvi Date: Fri, 5 Apr 2024 22:59:51 -0400 Subject: [PATCH 02/16] ensure that Julia benchmarks can be run without setup --- run_benchmarks.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/run_benchmarks.sh b/run_benchmarks.sh index bfd2db0..aacc3bd 100755 --- a/run_benchmarks.sh +++ b/run_benchmarks.sh @@ -27,6 +27,7 @@ do done # run Julia benchmarks +julia --project=./geometryops -e 'using Pkg; Pkg.instantiate()' for i in ${Python_packages[*]} do for path in "${i}"/*.jl From 77dc9f1a86313a2c60fd30dd5964b89f30e7490e Mon Sep 17 00:00:00 2001 From: Anshul Singhvi Date: Fri, 5 Apr 2024 23:05:16 -0400 Subject: [PATCH 03/16] Fix CSV writing --- geometryops/utils.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geometryops/utils.jl b/geometryops/utils.jl index c74b257..8ffdb2a 100644 --- a/geometryops/utils.jl +++ b/geometryops/utils.jl @@ -12,7 +12,7 @@ function write_benchmark_as_csv(benchmark::Chairmarks.Benchmark; package = "geom times = getproperty.(benchmark.samples, :time) # Now, we generate each row of the CSV file as a vector of strings. time_rows = map(times) do time - [package, task, replace(string(round(time, digits=4)), "." => ",")] + [task, package, replace(string(round(time, digits=4)), "." => ",")] end # Write the results to a CSV file header_vec = ["task", "package", "time"] From 2c5af28e09fa3df9eaec853fcda0c03e50a81af2 Mon Sep 17 00:00:00 2001 From: Anshul Singhvi Date: Fri, 5 Apr 2024 23:05:41 -0400 Subject: [PATCH 04/16] Update timings.csv --- timings.csv | 57 +++++++++++++++++++++++++++-------------------------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/timings.csv b/timings.csv index 1f19176..420806a 100644 --- a/timings.csv +++ b/timings.csv @@ -1,29 +1,30 @@ task,package,median -buffer,geopandas,10.46 -distance,geopandas,10.17 -intersects,geopandas,0.09 -load,geopandas,17.4 -sample,geopandas,0.8 -transform,geopandas,0.51 -write,geopandas,42.08 -buffer,geos,9.07 -distance,geos,11.17 -intersects,geos,0.12 -buffer,sf,19.65 -distance,sf,1.31 -intersects,sf,2.33 -load,sf,1.23 -sample,sf,2.28 -write,sf,8.73 -buffer,terra,20.62 -distance,terra,1.32 -intersects,terra,0.3 -load,terra,0.64 -sample,terra,5.64 -transform,terra,0.82 -write,terra,7.61 -distance,s2,24.79 -intersects,s2,0.76 -transform,s2,2.38 -transform,sf-project,0.21 -transform,sf-transform,1.93 +buffer,geopandas,6.09 +distance,geopandas,9.62 +intersects,geopandas,0.13 +load,geopandas,15.81 +sample,geopandas,0.43 +transform,geopandas,0.31 +write,geopandas,20.24 +buffer,sf,5.35 +distance,sf,0.36 +intersects,sf,0.64 +load,sf,0.45 +sample,sf,0.64 +write,sf,2.81 +buffer,terra,6.17 +distance,terra,0.38 +intersects,terra,0.16 +load,terra,0.34 +sample,terra,1.47 +transform,terra,0.37 +write,terra,2.53 +distance,geometryops,0.02 +distance,s2,10.29 +intersects,s2,0.37 +transform,s2,0.77 +intersects,geometryops,0.01 +sample,geometryops,0.09 +transform,geometryops,0.05 +transform,sf-project,0.05 +transform,sf-transform,0.5 From a844ed6639621ec1e8cc73de7be6712e86588d50 Mon Sep 17 00:00:00 2001 From: Anshul Singhvi Date: Sat, 6 Apr 2024 10:58:29 -0400 Subject: [PATCH 05/16] Update all functions to use GeoDataFrames better reading --- geometryops/Project.toml | 1 + geometryops/distance.jl | 11 +++++++---- geometryops/intersects.jl | 12 ++++++------ geometryops/load.jl | 26 +++++++++++++++++++++++++ geometryops/plots.jl | 41 ++++++++++++++++++++------------------- geometryops/sample.jl | 10 +++++----- geometryops/transform.jl | 9 ++++++--- geometryops/write.jl | 27 ++++++++++++++++++++++++++ 8 files changed, 99 insertions(+), 38 deletions(-) diff --git a/geometryops/Project.toml b/geometryops/Project.toml index 0b6611c..bdc084f 100644 --- a/geometryops/Project.toml +++ b/geometryops/Project.toml @@ -3,6 +3,7 @@ CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0" Chairmarks = "0ca39b1e-fe0b-4e98-acfc-b1656634c4de" DelimitedFiles = "8bb1440f-4735-579b-a4ab-409b98df4dab" GMT = "5752ebe1-31b9-557e-87aa-f909b540aa54" +GeoDataFrames = "62cb38b5-d8d2-4862-a48e-6a340996859f" GeoInterface = "cf35fbd7-0cd7-5166-be24-54bfbe79505f" GeometryOps = "3251bfac-6a57-4b6d-aa61-ac1fef2975ab" Proj = "c94c279d-25a6-4763-9509-64d165bea63e" diff --git a/geometryops/distance.jl b/geometryops/distance.jl index 96436b5..38252df 100644 --- a/geometryops/distance.jl +++ b/geometryops/distance.jl @@ -1,16 +1,19 @@ import GeometryOps as GO, GeoInterface as GI using CairoMakie -import GMT # easiest package to use to load Geopackage files +import GeoDataFrames # easiest package to use to load Geopackage files using Chairmarks include("utils.jl") # Load the data data_path = joinpath(dirname(@__DIR__), "data") -points_gpkg = GMT.gmtread(joinpath(data_path, "points.gpkg")) -point_set = map(view(points_gpkg, 1:4000)) do row - Point2d(row[1], row[2]) +points_gpkg = GeoDataFrames.read(joinpath(data_path, "points.gpkg")) +polygon_gpkg = GeoDataFrames.read(joinpath(data_path, "polygon.gpkg")) +# Process it into a Julia form +point_set = map(points_gpkg.geom) do row + Point2d(GI.x(row), GI.y(row)) end +polygon = GO.apply(p -> Point2d(GI.x(p), GI.y(p)), GI.PointTrait(), only(polygon_gpkg.geom)) # Benchmark the distance function # This uses the `Chairmarks.jl` package to benchmark the `GO.distance` function. # The benchmark will run for 15 seconds. diff --git a/geometryops/intersects.jl b/geometryops/intersects.jl index d682aff..356c61c 100644 --- a/geometryops/intersects.jl +++ b/geometryops/intersects.jl @@ -1,19 +1,19 @@ import GeometryOps as GO, GeoInterface as GI using CairoMakie -import GMT # easiest package to use to load Geopackage files +import GeoDataFrames # easiest package to use to load Geopackage files using Chairmarks include("utils.jl") # Load the data data_path = joinpath(dirname(@__DIR__), "data") -points_gpkg = GMT.gmtread(joinpath(data_path, "points.gpkg")) -polygon_gpkg = GMT.gmtread(joinpath(data_path, "polygon.gpkg")) +points_gpkg = GeoDataFrames.read(joinpath(data_path, "points.gpkg")) +polygon_gpkg = GeoDataFrames.read(joinpath(data_path, "polygon.gpkg")) # Process it into a Julia form -point_set = map(points_gpkg) do row - Point2d(row[1], row[2]) +point_set = map(points_gpkg.geom) do row + Point2d(GI.x(row), GI.y(row)) end -polygon = GI.Polygon([GI.LinearRing(Point2d.(eachrow(polygon_gpkg)))]) +polygon = GO.apply(p -> Point2d(GI.x(p), GI.y(p)), GI.PointTrait(), only(polygon_gpkg.geom)) # Benchmark the distance function # This uses the `Chairmarks.jl` package to benchmark the `GO.distance` function. # The benchmark will run for 15 seconds. diff --git a/geometryops/load.jl b/geometryops/load.jl index e69de29..f2a0009 100644 --- a/geometryops/load.jl +++ b/geometryops/load.jl @@ -0,0 +1,26 @@ +import GeometryOps as GO, GeoInterface as GI +using CairoMakie +import GeoDataFrames # easiest package to use to load Geopackage files +using Chairmarks + +include("utils.jl") + +# Load the data +data_path = joinpath(dirname(@__DIR__), "data") +points_gpkg = GeoDataFrames.read(joinpath(data_path, "points.gpkg")) +polygon_gpkg = GeoDataFrames.read(joinpath(data_path, "polygon.gpkg")) +# Process it into a Julia form +point_set = map(points_gpkg.geom) do row + Point2d(GI.x(row), GI.y(row)) +end +polygon = GO.apply(p -> Point2d(GI.x(p), GI.y(p)), GI.PointTrait(), only(polygon_gpkg.geom)) +function _read(path) + GeoDataFrames.read(path) +end +# Benchmark the reading/loading function +# This uses the `Chairmarks.jl` package to benchmark the `GeoDataFrames.read` function. +# The benchmark will run for 15 seconds. +benchmark = @be GeoDataFrames.read($(joinpath(data_path, "points.gpkg"))) seconds=15 + +# Write the results to a CSV file +write_benchmark_as_csv(benchmark; task = "load") \ No newline at end of file diff --git a/geometryops/plots.jl b/geometryops/plots.jl index 60a1981..70d55c1 100644 --- a/geometryops/plots.jl +++ b/geometryops/plots.jl @@ -79,36 +79,37 @@ using SwarmMakie using CategoricalArrays ca = CategoricalArray(result_tasks) + +group_marker = [MarkerElement(; color = color_map[package], marker = marker_map[package], markersize = 12) for package in keys(marker_map)] +names_marker = collect(keys(marker_map)) +lang_markers = ["R" => :utriangle, "Python" => :circle, "Julia" => :rect] +group_package = [MarkerElement(; marker, markersize = 12) for (lang, marker) in lang_markers] +names_package = first.(lang_markers) + f, a, p = beeswarm( ca.refs, result_times; marker = getindex.((marker_map,), result_pkgs), color = getindex.((color_map,), result_pkgs), + markersize = 10, axis = (; xticks = (1:length(ca.pool.levels), ca.pool.levels), xlabel = "Task", ylabel = "Median time (s)", + yscale = log10, + title = "Benchmark vector operations", ) ) - - - -Legend( +leg = Legend( f[1, 2], - + [group_marker, group_package], + [names_marker, names_package], + ["Package", "Language"], + tellheight = false, + tellwidth = true, + gridshalign = :left, ) +resize!(f, 600, 450) +f -aog_data = AlgebraOfGraphics.data( - (; - tasks = result_tasks, times = result_times, - markers = , - colors = -) -) * mapping( - :tasks, :times; - marker = :markers => nonnumeric, - color = :colors => nonnumeric -) * visual(Beeswarm) |> draw - - - -beeswarm \ No newline at end of file +a.yscale = log10 +f \ No newline at end of file diff --git a/geometryops/sample.jl b/geometryops/sample.jl index 79d30aa..c3b15c2 100644 --- a/geometryops/sample.jl +++ b/geometryops/sample.jl @@ -7,13 +7,13 @@ include("utils.jl") # include saving utilities as common code # Load the data data_path = joinpath(dirname(@__DIR__), "data") -points_gpkg = GMT.gmtread(joinpath(data_path, "points.gpkg")) -polygon_gpkg = GMT.gmtread(joinpath(data_path, "polygon.gpkg")) +points_gpkg = GeoDataFrames.read(joinpath(data_path, "points.gpkg")) +polygon_gpkg = GeoDataFrames.read(joinpath(data_path, "polygon.gpkg")) # Process it into a Julia form -point_set = map(points_gpkg) do row - Point2d(row[1], row[2]) +point_set = map(points_gpkg.geom) do row + Point2d(GI.x(row), GI.y(row)) end -polygon = GI.Polygon([GI.LinearRing(Point2d.(eachrow(polygon_gpkg)))]) +polygon = GO.apply(p -> Point2d(GI.x(p), GI.y(p)), GI.PointTrait(), only(polygon_gpkg.geom)) # Create a `sample` function in the vein of the Python function function _sample(polygon, size) diff --git a/geometryops/transform.jl b/geometryops/transform.jl index ecc262b..336eaad 100644 --- a/geometryops/transform.jl +++ b/geometryops/transform.jl @@ -9,10 +9,13 @@ include("utils.jl") # Load the data data_path = joinpath(dirname(@__DIR__), "data") -points_gpkg = GMT.gmtread(joinpath(data_path, "points.gpkg")) -point_set = map(points_gpkg) do row - Point2d(row[1], row[2]) +points_gpkg = GeoDataFrames.read(joinpath(data_path, "points.gpkg")) +polygon_gpkg = GeoDataFrames.read(joinpath(data_path, "polygon.gpkg")) +# Process it into a Julia form +point_set = map(points_gpkg.geom) do row + Point2d(GI.x(row), GI.y(row)) end +polygon = GO.apply(p -> Point2d(GI.x(p), GI.y(p)), GI.PointTrait(), only(polygon_gpkg.geom)) # Benchmark the reproject function # This uses the `Chairmarks.jl` package to benchmark the `GO.distance` function. # The benchmark will run for 15 seconds. diff --git a/geometryops/write.jl b/geometryops/write.jl index e69de29..24ccd76 100644 --- a/geometryops/write.jl +++ b/geometryops/write.jl @@ -0,0 +1,27 @@ +import GeometryOps as GO, GeoInterface as GI +using CairoMakie +import GeoDataFrames # easiest package to use to load Geopackage files +using Chairmarks + +include("utils.jl") + +# Load the data +data_path = joinpath(dirname(@__DIR__), "data") +points_gpkg = GeoDataFrames.read(joinpath(data_path, "points.gpkg")) +polygon_gpkg = GeoDataFrames.read(joinpath(data_path, "polygon.gpkg")) +# Process it into a Julia form +point_set = map(points_gpkg.geom) do row + Point2d(GI.x(row), GI.y(row)) +end +polygon = GO.apply(p -> Point2d(GI.x(p), GI.y(p)), GI.PointTrait(), only(polygon_gpkg.geom)) +function _write(gdf) + GeoDataFrames.write(joinpath(tempdir(), "temp.gpkg"), gdf; crs = GI.crs(first(gdf.geometry))) +end +# Benchmark the writing function +# This uses the `Chairmarks.jl` package to benchmark the `GeoDataFrames.write` function. +# The benchmark will run for 15 seconds. +gdf = GeoDataFrames.DataFrame(:geometry => point_set) +benchmark = @be _write($gdf) seconds=20 + +# Write the results to a CSV file +write_benchmark_as_csv(benchmark; task = "write") \ No newline at end of file From be11dfc619d3ce8cf95848d54d546ee3e29e5287 Mon Sep 17 00:00:00 2001 From: Anshul Singhvi Date: Sun, 7 Apr 2024 13:03:47 -0400 Subject: [PATCH 06/16] Fix docs in `transform` --- geometryops/transform.jl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/geometryops/transform.jl b/geometryops/transform.jl index 336eaad..5be1024 100644 --- a/geometryops/transform.jl +++ b/geometryops/transform.jl @@ -17,7 +17,9 @@ point_set = map(points_gpkg.geom) do row end polygon = GO.apply(p -> Point2d(GI.x(p), GI.y(p)), GI.PointTrait(), only(polygon_gpkg.geom)) # Benchmark the reproject function -# This uses the `Chairmarks.jl` package to benchmark the `GO.distance` function. +# This uses the `Chairmarks.jl` package to benchmark the +# `GO.reproject` function, which calls the Proj C-API. +# In future it will also support native Julia transformations. # The benchmark will run for 15 seconds. benchmark = @be GO.reproject($point_set, $(points_gpkg[1].proj4), $("EPSG:4326"); always_xy = false) seconds=15 From 815a94c5fdc8bb0b26aae205570f97e5569b4efc Mon Sep 17 00:00:00 2001 From: Anshul Singhvi Date: Sun, 7 Apr 2024 13:04:17 -0400 Subject: [PATCH 07/16] Implement `buffer` via LibGEOS integration in GeometryOps This is currently an active PR on GeometryOps but will be merged this week. --- geometryops/buffer.jl | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 geometryops/buffer.jl diff --git a/geometryops/buffer.jl b/geometryops/buffer.jl new file mode 100644 index 0000000..2b3336d --- /dev/null +++ b/geometryops/buffer.jl @@ -0,0 +1,25 @@ +import GeometryOps as GO, GeoInterface as GI +using CairoMakie +import GeoDataFrames # easiest package to use to load Geopackage files +using Chairmarks + +using LibGEOS # activate buffer capabilities in GeometryOps + +include("utils.jl") + +# Load the data +data_path = joinpath(dirname(@__DIR__), "data") +points_gpkg = GeoDataFrames.read(joinpath(data_path, "points.gpkg")) +polygon_gpkg = GeoDataFrames.read(joinpath(data_path, "polygon.gpkg")) +# Process it into a Julia form +point_set = map(points_gpkg.geom) do row + Point2d(GI.x(row), GI.y(row)) +end +polygon = GO.apply(p -> Point2d(GI.x(p), GI.y(p)), GI.PointTrait(), only(polygon_gpkg.geom)) +# Benchmark the distance function +# This uses the `Chairmarks.jl` package to benchmark the `GO.distance` function. +# The benchmark will run for 15 seconds. +benchmark = @be GO.buffer.((GO.GEOS(),), point_set, 100) seconds=15 + +# Write the results to a CSV file +write_benchmark_as_csv(benchmark; task = "buffer") \ No newline at end of file From 76b36839e49fdea53b67976249362b5c1dbfebfd Mon Sep 17 00:00:00 2001 From: Anshul Singhvi Date: Sun, 7 Apr 2024 13:04:40 -0400 Subject: [PATCH 08/16] Fix Julia package usage in `run_benchmarks.sh` --- run_benchmarks.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/run_benchmarks.sh b/run_benchmarks.sh index aacc3bd..f313cd7 100755 --- a/run_benchmarks.sh +++ b/run_benchmarks.sh @@ -28,7 +28,7 @@ done # run Julia benchmarks julia --project=./geometryops -e 'using Pkg; Pkg.instantiate()' -for i in ${Python_packages[*]} +for i in ${Julia_packages[*]} do for path in "${i}"/*.jl do From 0ec504941413b2e541d3e14274dc93b0fe6da4b1 Mon Sep 17 00:00:00 2001 From: Anshul Singhvi Date: Sun, 7 Apr 2024 13:04:44 -0400 Subject: [PATCH 09/16] New timings --- timings.csv | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/timings.csv b/timings.csv index 420806a..205d6f3 100644 --- a/timings.csv +++ b/timings.csv @@ -1,4 +1,7 @@ task,package,median +buffer,geometryops,2.41 +load,geometryops,0.2 +write,geometryops,3.53 buffer,geopandas,6.09 distance,geopandas,9.62 intersects,geopandas,0.13 @@ -6,6 +9,9 @@ load,geopandas,15.81 sample,geopandas,0.43 transform,geopandas,0.31 write,geopandas,20.24 +buffer,geos,2.82 +distance,geos,5.08 +intersects,geos,0.04 buffer,sf,5.35 distance,sf,0.36 intersects,sf,0.64 @@ -19,12 +25,12 @@ load,terra,0.34 sample,terra,1.47 transform,terra,0.37 write,terra,2.53 -distance,geometryops,0.02 +geometryops,distance,0.02 distance,s2,10.29 intersects,s2,0.37 transform,s2,0.77 -intersects,geometryops,0.01 -sample,geometryops,0.09 -transform,geometryops,0.05 +geometryops,intersects,0.01 +geometryops,sample,0.09 +geometryops,transform,0.05 transform,sf-project,0.05 transform,sf-transform,0.5 From deb416431d94297de7bcbeab3091054e064d0565 Mon Sep 17 00:00:00 2001 From: Anshul Singhvi Date: Sun, 7 Apr 2024 13:05:36 -0400 Subject: [PATCH 10/16] Add LibGEOS to project.toml --- geometryops/Project.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/geometryops/Project.toml b/geometryops/Project.toml index bdc084f..1e77f9c 100644 --- a/geometryops/Project.toml +++ b/geometryops/Project.toml @@ -6,5 +6,6 @@ GMT = "5752ebe1-31b9-557e-87aa-f909b540aa54" GeoDataFrames = "62cb38b5-d8d2-4862-a48e-6a340996859f" GeoInterface = "cf35fbd7-0cd7-5166-be24-54bfbe79505f" GeometryOps = "3251bfac-6a57-4b6d-aa61-ac1fef2975ab" +LibGEOS = "a90b1aa1-3769-5649-ba7e-abc5a9d163eb" Proj = "c94c279d-25a6-4763-9509-64d165bea63e" SwarmMakie = "0b1c068e-6a84-4e66-8136-5c95cafa83ed" From 6abcef1cf3b6a2e7e55f8f9cf845e02e0ed040d2 Mon Sep 17 00:00:00 2001 From: Anshul Singhvi Date: Sun, 7 Apr 2024 22:09:39 -0400 Subject: [PATCH 11/16] Minor plot improvements --- geometryops/plots.jl | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/geometryops/plots.jl b/geometryops/plots.jl index 70d55c1..5074c56 100644 --- a/geometryops/plots.jl +++ b/geometryops/plots.jl @@ -38,7 +38,6 @@ for (idx, task) in enumerate(unique(first.(results))) xlabel = "Median time (s)", ) ) - display(f) end resize!(f, 800, 1500) @@ -108,8 +107,5 @@ leg = Legend( tellwidth = true, gridshalign = :left, ) -resize!(f, 600, 450) -f - -a.yscale = log10 +resize!(f, 650, 450) f \ No newline at end of file From cb82f0da8edd0832442465bc87c8f86cea2df286 Mon Sep 17 00:00:00 2001 From: Anshul Singhvi Date: Fri, 12 Apr 2024 15:24:34 -0400 Subject: [PATCH 12/16] Fix timings, distance and plots --- geometryops/distance.jl | 10 ++++++---- geometryops/plots.jl | 7 ++++++- timings.csv | 10 +++++----- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/geometryops/distance.jl b/geometryops/distance.jl index 38252df..4b74f0d 100644 --- a/geometryops/distance.jl +++ b/geometryops/distance.jl @@ -9,15 +9,17 @@ include("utils.jl") data_path = joinpath(dirname(@__DIR__), "data") points_gpkg = GeoDataFrames.read(joinpath(data_path, "points.gpkg")) polygon_gpkg = GeoDataFrames.read(joinpath(data_path, "polygon.gpkg")) -# Process it into a Julia form -point_set = map(points_gpkg.geom) do row +# Process it into a Julia form. +# In this particular case, we only compute +# distance between the first 4000 points - otherwise it would get excessive. +point_set = map(points_gpkg.geom[1:4000]) do row Point2d(GI.x(row), GI.y(row)) end polygon = GO.apply(p -> Point2d(GI.x(p), GI.y(p)), GI.PointTrait(), only(polygon_gpkg.geom)) # Benchmark the distance function # This uses the `Chairmarks.jl` package to benchmark the `GO.distance` function. # The benchmark will run for 15 seconds. -distance_benchmark = @be [GO.distance(point1, point2) for point1 in $point_set, point2 in $point_set] seconds=15 +distance_benchmark = @be [GO.distance(point1, point2) for point1 in $point_set, point2 in $point_set] seconds=15 gc=false # Write the results to a CSV file -write_benchmark_as_csv(distance_benchmark; task = "distance") \ No newline at end of file +write_benchmark_as_csv(distance_benchmark; task = "distance") diff --git a/geometryops/plots.jl b/geometryops/plots.jl index 5074c56..2ba80f7 100644 --- a/geometryops/plots.jl +++ b/geometryops/plots.jl @@ -1,4 +1,4 @@ -using CairoMakie, AlgebraOfGraphics, DelimitedFiles, Statistics +using CairoMakie, DelimitedFiles, Statistics results_path = joinpath(dirname(@__DIR__), "results") results_files = readdir(results_path; join = false) @@ -96,6 +96,11 @@ f, a, p = beeswarm( ylabel = "Median time (s)", yscale = log10, title = "Benchmark vector operations", + xgridvisible = false, + xminorgridvisible = true, + yminorgridvisible = true, + yminorticks = IntervalsBetween(5), + ygridcolor = RGBA{Float32}(0.0f0,0.0f0,0.0f0,0.05f0), ) ) leg = Legend( diff --git a/timings.csv b/timings.csv index 205d6f3..6ab256d 100644 --- a/timings.csv +++ b/timings.csv @@ -1,6 +1,10 @@ task,package,median -buffer,geometryops,2.41 +buffer,geometryops,2.38 +distance,geometryops,0.04 +intersects,geometryops,0.01 load,geometryops,0.2 +sample,geometryops,0.09 +transform,geometryops,0.05 write,geometryops,3.53 buffer,geopandas,6.09 distance,geopandas,9.62 @@ -25,12 +29,8 @@ load,terra,0.34 sample,terra,1.47 transform,terra,0.37 write,terra,2.53 -geometryops,distance,0.02 distance,s2,10.29 intersects,s2,0.37 transform,s2,0.77 -geometryops,intersects,0.01 -geometryops,sample,0.09 -geometryops,transform,0.05 transform,sf-project,0.05 transform,sf-transform,0.5 From 69729bca4287e7f76a049d3df0e1b0cf6123a52a Mon Sep 17 00:00:00 2001 From: Anshul Singhvi Date: Sat, 8 Jun 2024 23:49:20 -0400 Subject: [PATCH 13/16] Bump the Project.toml compat in preparation for a new release This ensures that LibGEOS functionality like buffer is available to GeometryOps directly. --- geometryops/Project.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/geometryops/Project.toml b/geometryops/Project.toml index 1e77f9c..4145e05 100644 --- a/geometryops/Project.toml +++ b/geometryops/Project.toml @@ -9,3 +9,6 @@ GeometryOps = "3251bfac-6a57-4b6d-aa61-ac1fef2975ab" LibGEOS = "a90b1aa1-3769-5649-ba7e-abc5a9d163eb" Proj = "c94c279d-25a6-4763-9509-64d165bea63e" SwarmMakie = "0b1c068e-6a84-4e66-8136-5c95cafa83ed" + +[compat] +GeometryOps = "0.1.7" From c8119b31d75b7e516a3cfdfc6a6318d47f9d2f88 Mon Sep 17 00:00:00 2001 From: Anshul Singhvi Date: Thu, 11 Jul 2024 12:01:13 +0200 Subject: [PATCH 14/16] Update all conversions to use `GO.tuples` --- geometryops/buffer.jl | 7 +++---- geometryops/distance.jl | 7 +++---- geometryops/intersects.jl | 11 +++++------ geometryops/load.jl | 10 +++------- geometryops/sample.jl | 6 ++---- geometryops/transform.jl | 5 ++--- geometryops/write.jl | 6 ++---- 7 files changed, 20 insertions(+), 32 deletions(-) diff --git a/geometryops/buffer.jl b/geometryops/buffer.jl index 2b3336d..47011ab 100644 --- a/geometryops/buffer.jl +++ b/geometryops/buffer.jl @@ -12,10 +12,9 @@ data_path = joinpath(dirname(@__DIR__), "data") points_gpkg = GeoDataFrames.read(joinpath(data_path, "points.gpkg")) polygon_gpkg = GeoDataFrames.read(joinpath(data_path, "polygon.gpkg")) # Process it into a Julia form -point_set = map(points_gpkg.geom) do row - Point2d(GI.x(row), GI.y(row)) -end -polygon = GO.apply(p -> Point2d(GI.x(p), GI.y(p)), GI.PointTrait(), only(polygon_gpkg.geom)) +point_set = GO.tuples(points_gpkg.geom) +polygon = GO.tuples(only(polygon_gpkg.geom)) + # Benchmark the distance function # This uses the `Chairmarks.jl` package to benchmark the `GO.distance` function. # The benchmark will run for 15 seconds. diff --git a/geometryops/distance.jl b/geometryops/distance.jl index 4b74f0d..f7086af 100644 --- a/geometryops/distance.jl +++ b/geometryops/distance.jl @@ -12,10 +12,9 @@ polygon_gpkg = GeoDataFrames.read(joinpath(data_path, "polygon.gpkg")) # Process it into a Julia form. # In this particular case, we only compute # distance between the first 4000 points - otherwise it would get excessive. -point_set = map(points_gpkg.geom[1:4000]) do row - Point2d(GI.x(row), GI.y(row)) -end -polygon = GO.apply(p -> Point2d(GI.x(p), GI.y(p)), GI.PointTrait(), only(polygon_gpkg.geom)) +point_set = GO.tuples(points_gpkg.geom[1:4000]) +polygon = GO.tuples(only(polygon_gpkg.geom)) + # Benchmark the distance function # This uses the `Chairmarks.jl` package to benchmark the `GO.distance` function. # The benchmark will run for 15 seconds. diff --git a/geometryops/intersects.jl b/geometryops/intersects.jl index 356c61c..ad35bc3 100644 --- a/geometryops/intersects.jl +++ b/geometryops/intersects.jl @@ -10,12 +10,11 @@ data_path = joinpath(dirname(@__DIR__), "data") points_gpkg = GeoDataFrames.read(joinpath(data_path, "points.gpkg")) polygon_gpkg = GeoDataFrames.read(joinpath(data_path, "polygon.gpkg")) # Process it into a Julia form -point_set = map(points_gpkg.geom) do row - Point2d(GI.x(row), GI.y(row)) -end -polygon = GO.apply(p -> Point2d(GI.x(p), GI.y(p)), GI.PointTrait(), only(polygon_gpkg.geom)) -# Benchmark the distance function -# This uses the `Chairmarks.jl` package to benchmark the `GO.distance` function. +point_set = GO.tuples(points_gpkg.geom) +polygon = GO.tuples(only(polygon_gpkg.geom)) + +# Benchmark the intersects function +# This uses the `Chairmarks.jl` package to benchmark the `GO.intersects` function. # The benchmark will run for 15 seconds. benchmark = @be GO.intersects.(point_set, (polygon,)) seconds=15 diff --git a/geometryops/load.jl b/geometryops/load.jl index f2a0009..aa4894e 100644 --- a/geometryops/load.jl +++ b/geometryops/load.jl @@ -10,13 +10,9 @@ data_path = joinpath(dirname(@__DIR__), "data") points_gpkg = GeoDataFrames.read(joinpath(data_path, "points.gpkg")) polygon_gpkg = GeoDataFrames.read(joinpath(data_path, "polygon.gpkg")) # Process it into a Julia form -point_set = map(points_gpkg.geom) do row - Point2d(GI.x(row), GI.y(row)) -end -polygon = GO.apply(p -> Point2d(GI.x(p), GI.y(p)), GI.PointTrait(), only(polygon_gpkg.geom)) -function _read(path) - GeoDataFrames.read(path) -end +point_set = GO.tuples(points_gpkg.geom) +polygon = GO.tuples(only(polygon_gpkg.geom)) + # Benchmark the reading/loading function # This uses the `Chairmarks.jl` package to benchmark the `GeoDataFrames.read` function. # The benchmark will run for 15 seconds. diff --git a/geometryops/sample.jl b/geometryops/sample.jl index c3b15c2..4f97fae 100644 --- a/geometryops/sample.jl +++ b/geometryops/sample.jl @@ -10,10 +10,8 @@ data_path = joinpath(dirname(@__DIR__), "data") points_gpkg = GeoDataFrames.read(joinpath(data_path, "points.gpkg")) polygon_gpkg = GeoDataFrames.read(joinpath(data_path, "polygon.gpkg")) # Process it into a Julia form -point_set = map(points_gpkg.geom) do row - Point2d(GI.x(row), GI.y(row)) -end -polygon = GO.apply(p -> Point2d(GI.x(p), GI.y(p)), GI.PointTrait(), only(polygon_gpkg.geom)) +point_set = GO.tuples(points_gpkg.geom) +polygon = GO.tuples(only(polygon_gpkg.geom)) # Create a `sample` function in the vein of the Python function function _sample(polygon, size) diff --git a/geometryops/transform.jl b/geometryops/transform.jl index 5be1024..91e0f4f 100644 --- a/geometryops/transform.jl +++ b/geometryops/transform.jl @@ -12,9 +12,8 @@ data_path = joinpath(dirname(@__DIR__), "data") points_gpkg = GeoDataFrames.read(joinpath(data_path, "points.gpkg")) polygon_gpkg = GeoDataFrames.read(joinpath(data_path, "polygon.gpkg")) # Process it into a Julia form -point_set = map(points_gpkg.geom) do row - Point2d(GI.x(row), GI.y(row)) -end +point_set = GO.tuples(points_gpkg.geom) +polygon = GO.tuples(polygon_gpkg.geom) polygon = GO.apply(p -> Point2d(GI.x(p), GI.y(p)), GI.PointTrait(), only(polygon_gpkg.geom)) # Benchmark the reproject function # This uses the `Chairmarks.jl` package to benchmark the diff --git a/geometryops/write.jl b/geometryops/write.jl index 24ccd76..a33e0ab 100644 --- a/geometryops/write.jl +++ b/geometryops/write.jl @@ -10,10 +10,8 @@ data_path = joinpath(dirname(@__DIR__), "data") points_gpkg = GeoDataFrames.read(joinpath(data_path, "points.gpkg")) polygon_gpkg = GeoDataFrames.read(joinpath(data_path, "polygon.gpkg")) # Process it into a Julia form -point_set = map(points_gpkg.geom) do row - Point2d(GI.x(row), GI.y(row)) -end -polygon = GO.apply(p -> Point2d(GI.x(p), GI.y(p)), GI.PointTrait(), only(polygon_gpkg.geom)) +point_set = GO.tuples(points_gpkg.geom) +polygon = GO.tuples(polygon_gpkg.geom) function _write(gdf) GeoDataFrames.write(joinpath(tempdir(), "temp.gpkg"), gdf; crs = GI.crs(first(gdf.geometry))) end From fe4d4cd0718c22f2d724f40cfc1cf13673fcdec4 Mon Sep 17 00:00:00 2001 From: Anshul Singhvi Date: Thu, 11 Jul 2024 12:02:33 +0200 Subject: [PATCH 15/16] Update `plots.jl` to replicate the PR plot Add MakieTeX for plotting --- geometryops/Project.toml | 1 + geometryops/plots.jl | 52 +++++++++++++++++++++++++++------------- 2 files changed, 37 insertions(+), 16 deletions(-) diff --git a/geometryops/Project.toml b/geometryops/Project.toml index 4145e05..35a039e 100644 --- a/geometryops/Project.toml +++ b/geometryops/Project.toml @@ -7,6 +7,7 @@ GeoDataFrames = "62cb38b5-d8d2-4862-a48e-6a340996859f" GeoInterface = "cf35fbd7-0cd7-5166-be24-54bfbe79505f" GeometryOps = "3251bfac-6a57-4b6d-aa61-ac1fef2975ab" LibGEOS = "a90b1aa1-3769-5649-ba7e-abc5a9d163eb" +MakieTeX = "6d554a22-29e7-47bd-aee5-0c5f06619414" Proj = "c94c279d-25a6-4763-9509-64d165bea63e" SwarmMakie = "0b1c068e-6a84-4e66-8136-5c95cafa83ed" diff --git a/geometryops/plots.jl b/geometryops/plots.jl index 2ba80f7..b8fe6db 100644 --- a/geometryops/plots.jl +++ b/geometryops/plots.jl @@ -45,44 +45,58 @@ f # summary plot + +using MakieTeX # to render SVG +# get language logo SVGs +language_logo_url(lang::String) = "https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/$(lowercase(lang))/$(lowercase(lang))-plain.svg" +language_marker_dict = Dict( + [ + key => MakieTeX.SVGDocument(read(download(language_logo_url(key)), String)) + for key in ("C", "Go", "javascript", "julia", "python", "r") + ] +) +language_marker_dict["r"] = MakieTeX.SVGDocument(read(download("https://raw.githubusercontent.com/file-icons/icons/master/svg/R.svg"), String)); + +# create a map from package name to marker marker_map = Dict( # R packages - "sf" => :utriangle, - "s2" => :utriangle, - "terra" => :utriangle, - "geos" => :utriangle, + "sf" => language_marker_dict["r"], + "s2" => language_marker_dict["r"], + "terra" => language_marker_dict["r"], + "geos" => language_marker_dict["r"], # Python package - "geopandas" => :circle, + "geopandas" => language_marker_dict["python"], # Julia package - "geometryops" => :rect, + "geometryops" => language_marker_dict["julia"], ) - +# package name to color color_map = Dict( # R packages - "sf" => Makie.wong_colors()[1], - "s2" => Makie.wong_colors()[2], - "terra" => Makie.wong_colors()[3], + "sf" => Makie.wong_colors()[2], + "s2" => Makie.wong_colors()[6], + "terra" => Makie.wong_colors()[1], "geos" => Makie.wong_colors()[4], # Python package "geopandas" => Makie.wong_colors()[5], # Julia package - "geometryops" => Makie.wong_colors()[6], + "geometryops" => Makie.wong_colors()[3], ) result_tasks = getindex.(results, 1) .|> string result_pkgs = getindex.(results, 2) .|> string result_times = Statistics.median.(getindex.(results, 3)) -using SwarmMakie +using SwarmMakie # for beeswarm plots and dodging using CategoricalArrays +using Makie: RGBA ca = CategoricalArray(result_tasks) group_marker = [MarkerElement(; color = color_map[package], marker = marker_map[package], markersize = 12) for package in keys(marker_map)] names_marker = collect(keys(marker_map)) -lang_markers = ["R" => :utriangle, "Python" => :circle, "Julia" => :rect] -group_package = [MarkerElement(; marker, markersize = 12) for (lang, marker) in lang_markers] +lang_markers = ["R" => language_marker_dict["r"], "Python" => language_marker_dict["python"], "Julia" => language_marker_dict["julia"]] +group_package = [MarkerElement(; marker, markersize = 12, color = :black) for (lang, marker) in lang_markers] names_package = first.(lang_markers) f, a, p = beeswarm( @@ -101,7 +115,8 @@ f, a, p = beeswarm( yminorgridvisible = true, yminorticks = IntervalsBetween(5), ygridcolor = RGBA{Float32}(0.0f0,0.0f0,0.0f0,0.05f0), - ) + ), + figure = (; backgroundcolor = RGBAf(1, 1, 1, 0)) ) leg = Legend( f[1, 2], @@ -113,4 +128,9 @@ leg = Legend( gridshalign = :left, ) resize!(f, 650, 450) -f \ No newline at end of file +a.backgroundcolor[] = RGBAf(1, 1, 1, 0) +leg.backgroundcolor[] = RGBAf(1, 1, 1, 0) +p.markersize[] = 13 +f + + From acb192724116a3623771fdaccd2ea586117d2f29 Mon Sep 17 00:00:00 2001 From: Anshul Singhvi Date: Thu, 11 Jul 2024 12:05:37 +0200 Subject: [PATCH 16/16] Fix the Python logo as well --- geometryops/plots.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/geometryops/plots.jl b/geometryops/plots.jl index b8fe6db..bdc3bad 100644 --- a/geometryops/plots.jl +++ b/geometryops/plots.jl @@ -56,6 +56,7 @@ language_marker_dict = Dict( ] ) language_marker_dict["r"] = MakieTeX.SVGDocument(read(download("https://raw.githubusercontent.com/file-icons/icons/master/svg/R.svg"), String)); +language_marker_dict["python"] = MakieTeX.SVGDocument(read(download("https://raw.githubusercontent.com/file-icons/MFixx/master/svg/python.svg"), String)); # create a map from package name to marker marker_map = Dict(