From 704b16a078025d149e9946434b9030b0f7b77be4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Poisot?= Date: Thu, 14 Oct 2021 12:34:48 -0400 Subject: [PATCH 1/6] =?UTF-8?q?=E2=9C=A8=20bivariate=20maps=20now=20use=20?= =?UTF-8?q?transparency?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/recipes/recipes.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/recipes/recipes.jl b/src/recipes/recipes.jl index 848380e9..f1847836 100644 --- a/src/recipes/recipes.jl +++ b/src/recipes/recipes.jl @@ -24,7 +24,7 @@ end """ test 2 """ -@recipe function plot(l1::FT, l2::ST; classes::Int=3, p0=colorant"#e8e8e8", p1=colorant"#64acbe", p2=colorant"#c85a5a") where {FT <: SimpleSDMLayer, ST <: SimpleSDMLayer} +@recipe function plot(l1::FT, l2::ST; classes::Int=3, p0=colorant"#e8e8e8ff", p1=colorant"#64acbeff", p2=colorant"#c85a5aff") where {FT <: SimpleSDMLayer, ST <: SimpleSDMLayer} eltype(l1) <: Number || throw(ArgumentError("Plotting is only supported for layers with number values ($(eltype(l1)))")) eltype(l2) <: Number || throw(ArgumentError("Plotting is only supported for layers with number values ($(eltype(l2)))")) seriestype --> :scatter From 6f125641cb94d4bc33f4c5bf31d5d2518aa5057a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Poisot?= Date: Thu, 14 Oct 2021 15:28:00 -0400 Subject: [PATCH 2/6] trivariate plot almost there --- src/recipes/recipes.jl | 102 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 98 insertions(+), 4 deletions(-) diff --git a/src/recipes/recipes.jl b/src/recipes/recipes.jl index f1847836..7028035b 100644 --- a/src/recipes/recipes.jl +++ b/src/recipes/recipes.jl @@ -1,6 +1,7 @@ @shorthands bivariate @shorthands bivariatelegend - +@shorthands trivariate +@shorthands trivariatelegend """ test 1 @@ -24,7 +25,7 @@ end """ test 2 """ -@recipe function plot(l1::FT, l2::ST; classes::Int=3, p0=colorant"#e8e8e8ff", p1=colorant"#64acbeff", p2=colorant"#c85a5aff") where {FT <: SimpleSDMLayer, ST <: SimpleSDMLayer} +@recipe function plot(l1::FT, l2::ST; classes::Int=3, p0=colorant"#e8e8e8ff", p1=colorant"#64acbeff", p2=colorant"#c85a5aff", quantiles=true) where {FT <: SimpleSDMLayer, ST <: SimpleSDMLayer} eltype(l1) <: Number || throw(ArgumentError("Plotting is only supported for layers with number values ($(eltype(l1)))")) eltype(l2) <: Number || throw(ArgumentError("Plotting is only supported for layers with number values ($(eltype(l2)))")) seriestype --> :scatter @@ -37,8 +38,13 @@ test 2 c1 = LinRange(p0, p1, classes) c2 = LinRange(p0, p2, classes) breakpoints = LinRange(0.0, 1.0, classes+1) - q1 = rescale(l1, collect(LinRange(0.0, 1.0, 10classes))) - q2 = rescale(l2, collect(LinRange(0.0, 1.0, 10classes))) + if quantiles + q1 = rescale(l1, collect(LinRange(0.0, 1.0, 10classes))) + q2 = rescale(l2, collect(LinRange(0.0, 1.0, 10classes))) + else + q1 = rescale(l1, (0., 1.)) + q2 = rescale(l2, (0., 1.)) + end classified = similar(l1, Int) cols = typeof(p0)[] for i in 1:classes @@ -99,3 +105,91 @@ test 2 end end end + + +""" +test 2 +""" +@recipe function plot(x::FT, y::ST, z::TT; quantiles=true, simplex=false, red="", green="", blue="") where {FT <: SimpleSDMLayer, ST <: SimpleSDMLayer, TT <: SimpleSDMLayer} + eltype(x) <: Number || throw(ArgumentError("Plotting is only supported for layers with number values ($(eltype(x)))")) + eltype(y) <: Number || throw(ArgumentError("Plotting is only supported for layers with number values ($(eltype(y)))")) + eltype(z) <: Number || throw(ArgumentError("Plotting is only supported for layers with number values ($(eltype(z)))")) + SimpleSDMLayers._layers_are_compatible(x, y) + SimpleSDMLayers._layers_are_compatible(x, z) + SimpleSDMLayers._layers_are_compatible(y, z) + seriestype --> :scatter + if get(plotattributes, :seriestype, :trivariate) in [:trivariate] + void = colorant"#ffffff00" + if quantiles + X = rescale(x, collect(LinRange(0.0, 1.0, 256))) + Y = rescale(y, collect(LinRange(0.0, 1.0, 256))) + Z = rescale(z, collect(LinRange(0.0, 1.0, 256))) + else + X = rescale(x, (0., 1.)) + Y = rescale(y, (0., 1.)) + Z = rescale(z, (0., 1.)) + end + trip = fill(void, size(X)) + for i in CartesianIndices(trip) + if !isnothing(X[i]) + r = X[i] + g = Y[i] + b = Z[i] + if simplex + S = r+g+b + if S > zero(typeof(S)) + r = r / S + g = g / S + b = b / S + end + end + trip[i] = RGBA(r, g, b, 1.0) + end + end + @series begin + seriestype := :heatmap + subplot := 1 + legend --> false + longitudes(X), latitudes(X), reverse(trip; dims=1) + end + end +end + + +function _sunflower(n, α) + b = round(α*sqrt(n)) + ϕ = (sqrt(5)+1)/2 + r = zeros(Float64, n) + θ = zeros(Float64, n) + for k in 1:n + r[k] = k>(n-b) ? 1.0 : sqrt(k-1/2)/sqrt(n-(b+1)/2) + θ[k] = 2π*k/ϕ^2 + end + x = r.*cos.(θ) + y = r.*sin.(θ) + return (x, y) +end + + +#= +saturation = 0.9 +rad = atan.(x0, y0) +deg = rad * (180. / π) + +c = HSV.(deg, saturation, 0.8.*sqrt.(x0.*x0 .+ y0.*y0)) +nr, ng, nb = layernames(WorldClim, BioClim, [1,3,12]) + +plot(; aspectratio=1, xlim=, ylim=(-1.3,1.3), grid=false, leg=false, frame=:none) +scatter!(x0, y0, c=c, aspectratio=1, msw=1.0, ms=4) + + + +ax, ay = R * cos(π/2), R * sin(π/2) +annotate!(ax, ay, Plots.text(nr, 10, :dark, rotation = 0)) + +ax, ay = R * cos(3π/2+π/3), R * sin(3π/2+π/3) +annotate!(ax, ay, Plots.text(ng, 10, :dark, rotation = 60)) + +ax, ay = R * cos(π/2+2π/3), R * sin(π/2+2π/3) +annotate!(ax, ay, Plots.text(nb, 10, rotation = -60)) +=# \ No newline at end of file From 0385e38ee0ce56a449582000a48d5d2448c54579 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Poisot?= Date: Thu, 14 Oct 2021 19:09:12 -0400 Subject: [PATCH 3/6] trivariate legend --- src/recipes/recipes.jl | 170 ++++++++++++++++++++++++++--------------- 1 file changed, 107 insertions(+), 63 deletions(-) diff --git a/src/recipes/recipes.jl b/src/recipes/recipes.jl index 7028035b..33c44da1 100644 --- a/src/recipes/recipes.jl +++ b/src/recipes/recipes.jl @@ -6,10 +6,14 @@ """ test 1 """ -@recipe function plot(layer::T) where {T <: SimpleSDMLayer} +@recipe function plot(layer::T) where {T<:SimpleSDMLayer} seriestype --> :heatmap if get(plotattributes, :seriestype, :heatmap) in [:heatmap, :contour] - eltype(layer) <: AbstractFloat || throw(ArgumentError("This plot is only supported for layers with number values ($(eltype(layer)))")) + eltype(layer) <: AbstractFloat || throw( + ArgumentError( + "This plot is only supported for layers with number values ($(eltype(layer)))", + ), + ) aspect_ratio --> 1 xlims --> extrema(longitudes(layer)) ylims --> extrema(latitudes(layer)) @@ -25,9 +29,25 @@ end """ test 2 """ -@recipe function plot(l1::FT, l2::ST; classes::Int=3, p0=colorant"#e8e8e8ff", p1=colorant"#64acbeff", p2=colorant"#c85a5aff", quantiles=true) where {FT <: SimpleSDMLayer, ST <: SimpleSDMLayer} - eltype(l1) <: Number || throw(ArgumentError("Plotting is only supported for layers with number values ($(eltype(l1)))")) - eltype(l2) <: Number || throw(ArgumentError("Plotting is only supported for layers with number values ($(eltype(l2)))")) +@recipe function plot( + l1::FT, + l2::ST; + classes::Int=3, + p0=colorant"#e8e8e8ff", + p1=colorant"#64acbeff", + p2=colorant"#c85a5aff", + quantiles=true, +) where {FT<:SimpleSDMLayer,ST<:SimpleSDMLayer} + eltype(l1) <: Number || throw( + ArgumentError( + "Plotting is only supported for layers with number values ($(eltype(l1)))" + ), + ) + eltype(l2) <: Number || throw( + ArgumentError( + "Plotting is only supported for layers with number values ($(eltype(l2)))" + ), + ) seriestype --> :scatter if get(plotattributes, :seriestype, :scatter) in [:scatter, :histogram2d] SimpleSDMLayers._layers_are_compatible(l1, l2) @@ -37,28 +57,28 @@ test 2 SimpleSDMLayers._layers_are_compatible(l1, l2) c1 = LinRange(p0, p1, classes) c2 = LinRange(p0, p2, classes) - breakpoints = LinRange(0.0, 1.0, classes+1) + breakpoints = LinRange(0.0, 1.0, classes + 1) if quantiles q1 = rescale(l1, collect(LinRange(0.0, 1.0, 10classes))) q2 = rescale(l2, collect(LinRange(0.0, 1.0, 10classes))) else - q1 = rescale(l1, (0., 1.)) - q2 = rescale(l2, (0., 1.)) + q1 = rescale(l1, (0.0, 1.0)) + q2 = rescale(l2, (0.0, 1.0)) end classified = similar(l1, Int) cols = typeof(p0)[] for i in 1:classes if isequal(classes)(i) - fi = (v) -> breakpoints[i] < v <= breakpoints[i+1] + fi = (v) -> breakpoints[i] < v <= breakpoints[i + 1] else - fi = (v) -> breakpoints[i] <= v < breakpoints[i+1] + fi = (v) -> breakpoints[i] <= v < breakpoints[i + 1] end m1 = broadcast(fi, q1) for j in 1:classes if isequal(classes)(j) - fj = (v) -> breakpoints[j] < v <= breakpoints[j+1] + fj = (v) -> breakpoints[j] < v <= breakpoints[j + 1] else - fj = (v) -> breakpoints[j] <= v < breakpoints[j+1] + fj = (v) -> breakpoints[j] <= v < breakpoints[j + 1] end m2 = broadcast(fj, q2) push!(cols, ColorBlendModes.BlendMultiply(c1[i], c2[j])) @@ -85,15 +105,15 @@ test 2 ticks --> :none legend --> false frametype --> :none - xlims --> (1-0.5, classes+0.5) - ylims --> (1-0.5, classes+0.5) + xlims --> (1 - 0.5, classes + 0.5) + ylims --> (1 - 0.5, classes + 0.5) aspect_ratio --> 1 cols = Vector{typeof(p0)}(undef, classes^2) class = 1 m = zeros(Float64, classes, classes) for i in 1:classes for j in 1:classes - m[j,i] = class + m[j, i] = class cols[class] = ColorBlendModes.BlendMultiply(c1[i], c2[j]) class += 1 end @@ -106,14 +126,27 @@ test 2 end end - """ test 2 """ -@recipe function plot(x::FT, y::ST, z::TT; quantiles=true, simplex=false, red="", green="", blue="") where {FT <: SimpleSDMLayer, ST <: SimpleSDMLayer, TT <: SimpleSDMLayer} - eltype(x) <: Number || throw(ArgumentError("Plotting is only supported for layers with number values ($(eltype(x)))")) - eltype(y) <: Number || throw(ArgumentError("Plotting is only supported for layers with number values ($(eltype(y)))")) - eltype(z) <: Number || throw(ArgumentError("Plotting is only supported for layers with number values ($(eltype(z)))")) +@recipe function plot( + x::FT, y::ST, z::TT; quantiles=true, simplex=false, red="", green="", blue="" +) where {FT<:SimpleSDMLayer,ST<:SimpleSDMLayer,TT<:SimpleSDMLayer} + eltype(x) <: Number || throw( + ArgumentError( + "Plotting is only supported for layers with number values ($(eltype(x)))" + ), + ) + eltype(y) <: Number || throw( + ArgumentError( + "Plotting is only supported for layers with number values ($(eltype(y)))" + ), + ) + eltype(z) <: Number || throw( + ArgumentError( + "Plotting is only supported for layers with number values ($(eltype(z)))" + ), + ) SimpleSDMLayers._layers_are_compatible(x, y) SimpleSDMLayers._layers_are_compatible(x, z) SimpleSDMLayers._layers_are_compatible(y, z) @@ -121,13 +154,13 @@ test 2 if get(plotattributes, :seriestype, :trivariate) in [:trivariate] void = colorant"#ffffff00" if quantiles - X = rescale(x, collect(LinRange(0.0, 1.0, 256))) - Y = rescale(y, collect(LinRange(0.0, 1.0, 256))) - Z = rescale(z, collect(LinRange(0.0, 1.0, 256))) + X = rescale(x, collect(LinRange(0.0, 1.0, 100))) + Y = rescale(y, collect(LinRange(0.0, 1.0, 100))) + Z = rescale(z, collect(LinRange(0.0, 1.0, 100))) else - X = rescale(x, (0., 1.)) - Y = rescale(y, (0., 1.)) - Z = rescale(z, (0., 1.)) + X = rescale(x, (0.0, 1.0)) + Y = rescale(y, (0.0, 1.0)) + Z = rescale(z, (0.0, 1.0)) end trip = fill(void, size(X)) for i in CartesianIndices(trip) @@ -136,7 +169,7 @@ test 2 g = Y[i] b = Z[i] if simplex - S = r+g+b + S = r + g + b if S > zero(typeof(S)) r = r / S g = g / S @@ -152,44 +185,55 @@ test 2 legend --> false longitudes(X), latitudes(X), reverse(trip; dims=1) end + elseif get(plotattributes, :seriestype, :trivariatelegend) in [:trivariatelegend] + # Legend + steps = 6 + a = 1 / steps + h = a * sqrt(3) / 2 + aspect_ratio --> 1 + xlims --> (0, 1) + ylims --> (-0.1, sqrt(3) / 2 + 0.1) + legend --> false + ticks --> :none + framestyle --> :none + for (row_number, triangles_to_do) in enumerate(reverse(1:steps)) + for triangle_number in 1:triangles_to_do + x0 = (triangle_number - 1) * a + (row_number - 1) * (a / 2) + y0 = (row_number - 1) * h + @series begin + subplot := 1 + seriestype := :shape + seriescolor := rgb_from_xy(x0 + a / 2, y0 + h / 2; simplex=simplex) + [x0, x0 + a / 2, x0 + a], [y0, y0 + h, y0] + end + if row_number > 1 + @series begin + subplot := 1 + seriestype := :shape + seriescolor := rgb_from_xy(x0 + a / 2, y0 - h / 2; simplex=simplex) + [x0, x0 + a / 2, x0 + a], [y0, y0 - h, y0] + end + end + end + end + @series begin + seriestype := :scatter + annotations := [(0.0, -0.05, red, :center), (1.0, -0.05, green, :center), (0.5, sqrt(3)/2+0.05, blue, :center)] + markersize := 0 + [0.0, 0.0] + end end end - -function _sunflower(n, α) - b = round(α*sqrt(n)) - ϕ = (sqrt(5)+1)/2 - r = zeros(Float64, n) - θ = zeros(Float64, n) - for k in 1:n - r[k] = k>(n-b) ? 1.0 : sqrt(k-1/2)/sqrt(n-(b+1)/2) - θ[k] = 2π*k/ϕ^2 +function rgb_from_xy(x, y; simplex=false) + b = 2y / sqrt(3) + g = (2x - b) / 2 + r = 1 - (b + g) + if !simplex + m = maximum([r, g, b]) + b = b * (1 / m) + g = g * (1 / m) + r = r * (1 / m) end - x = r.*cos.(θ) - y = r.*sin.(θ) - return (x, y) + return RGB(r, g, b) end - - -#= -saturation = 0.9 -rad = atan.(x0, y0) -deg = rad * (180. / π) - -c = HSV.(deg, saturation, 0.8.*sqrt.(x0.*x0 .+ y0.*y0)) -nr, ng, nb = layernames(WorldClim, BioClim, [1,3,12]) - -plot(; aspectratio=1, xlim=, ylim=(-1.3,1.3), grid=false, leg=false, frame=:none) -scatter!(x0, y0, c=c, aspectratio=1, msw=1.0, ms=4) - - - -ax, ay = R * cos(π/2), R * sin(π/2) -annotate!(ax, ay, Plots.text(nr, 10, :dark, rotation = 0)) - -ax, ay = R * cos(3π/2+π/3), R * sin(3π/2+π/3) -annotate!(ax, ay, Plots.text(ng, 10, :dark, rotation = 60)) - -ax, ay = R * cos(π/2+2π/3), R * sin(π/2+2π/3) -annotate!(ax, ay, Plots.text(nb, 10, rotation = -60)) -=# \ No newline at end of file From cab68c9fdb6de16168facfa6301ee1b3f0c8b0df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Poisot?= Date: Fri, 15 Oct 2021 10:05:26 -0400 Subject: [PATCH 4/6] up --- Project.toml | 2 +- docs/make.jl | 2 +- docs/src/examples/bivariate.jl | 75 ------------------- docs/src/examples/mutivariate.jl | 120 +++++++++++++++++++++++++++++++ 4 files changed, 122 insertions(+), 77 deletions(-) delete mode 100644 docs/src/examples/bivariate.jl create mode 100644 docs/src/examples/mutivariate.jl diff --git a/Project.toml b/Project.toml index e089c801..e3dd89ce 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "SimpleSDMLayers" uuid = "2c645270-77db-11e9-22c3-0f302a89c64c" authors = ["Timothée Poisot ", "Gabriel Dansereau "] -version = "0.8.1" +version = "0.8.2" [deps] ArchGDAL = "c9ce4bd3-c3d5-55b8-8973-c0e20141b8c3" diff --git a/docs/make.jl b/docs/make.jl index b429e838..c32f7084 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -40,7 +40,7 @@ makedocs( "Geometry for clipping" => "examples/geometry.md", "Sliding window analysis" => "examples/slidingwindow.md", "Landcover data" => "examples/landcover.md", - "Bivariate mapping" => "examples/bivariate.md" + "Multivariate mapping" => "examples/multivariate.md" ], "SDM case studies" => [ "GBIF integration" => "sdm/gbif.md", diff --git a/docs/src/examples/bivariate.jl b/docs/src/examples/bivariate.jl deleted file mode 100644 index 347e4bc7..00000000 --- a/docs/src/examples/bivariate.jl +++ /dev/null @@ -1,75 +0,0 @@ -# # Bivariate maps - -using SimpleSDMLayers -using StatsPlots -using StatsPlots.PlotMeasures - -# **Justification for this use case:** We can show more than one (specifically, -# two) variables on a single map, using a bivariate color scale. In order to -# illustrate bivariate mapping, we will look at the joint distribution of two -# measures: eveness of land use, and terrain roughness. - -boundaries = (left=-12.0, right=30.0, bottom=36.0, top=72.0) -layer1 = convert( - Float16, - SimpleSDMPredictor(EarthEnv, HabitatHeterogeneity, 2; resolution=5, boundaries...), -) -layer2 = convert( - Float16, SimpleSDMPredictor(EarthEnv, Topography, 7; resolution=5, boundaries...) -) -layer2 = mask(layer1, layer2); - -# Note that bivariate maps usually work best when used with 9 classes in total -# (so 3 for each side). The next decision is to take a bivaraite color palette, -# and the combinations below are [commonly -# used](https://www.joshuastevens.net/cartography/make-a-bivariate-choropleth-map/). -# Note that you can definitely use [diverging -# colors](https://www.personal.psu.edu/cab38/ColorSch/Schemes.html) if you want. - -p0 = colorant"#e8e8e8" -bv_pal_1 = (p0=p0, p1=colorant"#64acbe", p2=colorant"#c85a5a") -bv_pal_2 = (p0=p0, p1=colorant"#73ae80", p2=colorant"#6c83b5") -bv_pal_3 = (p0=p0, p1=colorant"#9972af", p2=colorant"#c8b35a") -bv_pal_4 = (p0=p0, p1=colorant"#be64ac", p2=colorant"#5ac8c8") - -# The bivariate map itself is a call to plot. Internally, this will transform -# the layers into quantiles (determined by the `classes` keyword, defaults to -# 3): - -plot(layer1, layer2; st=:bivariate, bv_pal_3...) - -# Note that you can use the `bivariate` shorthand as well: - -pl1 = bivariate(layer1, layer2; classes=3, frame=:box, bv_pal_4...) -xaxis!(pl1, "Longitude") -yaxis!(pl1, "Latitude") - -# We can repeat essentially the same process for the legend: - -pl2 = bivariatelegend(layer1, layer2; classes=3, bv_pal_4...) -xaxis!(pl2, layernames(EarthEnv, HabitatHeterogeneity, 2)) -yaxis!(pl2, layernames(EarthEnv, Topography, 7)) - -# And now, we can plot the legend next to the map - future releases of the -# package will hopefully offer this in a far more user friendly way. - -plot(pl1, pl2; layout=@layout [a{0.75w} b]) - -# Using the `subplot` and `inset` arguments of Plots.jl, we can have the legend -# within the figure. Note how in this example we expand the limits on the x axis -# to make the legend fit, but also use more classes in the map to have a -# smoother result. - -p1 = bivariate(layer1, layer2; classes=6, bv_pal_2..., frame=:box, xlim=(-24, maximum(longitudes(layer1)))) -xaxis!(p1, "Longitude") -yaxis!(p1, "Latitude") -p2 = bivariatelegend!( - layer1, - layer2; - bv_pal_2..., - inset=(1, bbox(0.04, 0.05, 0.28, 0.28, :top, :left)), - subplot=2, - xlab=layernames(EarthEnv, HabitatHeterogeneity)[2], - ylab=layernames(EarthEnv, Topography)[7], - guidefontsize=7, -) diff --git a/docs/src/examples/mutivariate.jl b/docs/src/examples/mutivariate.jl new file mode 100644 index 00000000..8a22f9e8 --- /dev/null +++ b/docs/src/examples/mutivariate.jl @@ -0,0 +1,120 @@ +# # Multivariate maps + +using SimpleSDMLayers +using StatsPlots +using StatsPlots.PlotMeasures +using Statistics + +# **Justification for this use case:** We can show more than one (specifically, +# two or three) variables on a single map, using a bivariate or trivariate color +# scale. In order to illustrate these mappings, we will look at the joint +# distribution of three measures: eveness of land use, terrain roughness, and +# proportion of urbanized land. + +boundaries = (left=-12.0, right=30.0, bottom=36.0, top=72.0) +layer1 = convert( + Float16, + SimpleSDMPredictor(EarthEnv, HabitatHeterogeneity, 2; resolution=5, boundaries...), +) +layer2 = convert( + Float16, SimpleSDMPredictor(EarthEnv, Topography, 7; resolution=5, boundaries...) +) + +# The landcover data are finer than the other layers, so we will coarsen them. +# Because of rounding issues, the left and right cordinates need to be rounded. + +layer3 = coarsen(convert( + Float16, SimpleSDMPredictor(EarthEnv, LandCover, 9; boundaries...) +), mean, (5, 5)) +layer3.left = round(layer3.left; digits=0) +layer3.right = round(layer3.right; digits=0) + +# We finally mask everything according to the first layer + +layer2 = mask(layer1, layer2); +layer3 = mask(layer1, layer3); + +# Note that bivariate maps usually work best when used with 9 classes in total +# (so 3 for each side). The next decision is to take a bivaraite color palette, +# and the combinations below are [commonly +# used](https://www.joshuastevens.net/cartography/make-a-bivariate-choropleth-map/). +# Note that you can definitely use [diverging +# colors](https://www.personal.psu.edu/cab38/ColorSch/Schemes.html) if you want. +# If you use colors in the RGBA format (*e.g.* `colorant"#ef0ce8c4"`), the color +# map will account for transparency. + +p0 = colorant"#e8e8e8" +bv_pal_1 = (p0=p0, p1=colorant"#64acbe", p2=colorant"#c85a5a") +bv_pal_2 = (p0=p0, p1=colorant"#73ae80", p2=colorant"#6c83b5") +bv_pal_3 = (p0=p0, p1=colorant"#9972af", p2=colorant"#c8b35a") +bv_pal_4 = (p0=p0, p1=colorant"#be64ac", p2=colorant"#5ac8c8") + +# The bivariate map itself is a call to plot. Internally, this will transform +# the layers into quantiles (determined by the `classes` keyword, defaults to +# 3): + +plot(layer1, layer3; st=:bivariate, bv_pal_3...) + +# Note that you can use the `bivariate` shorthand as well: + +pl1 = bivariate(layer1, layer3; classes=3, frame=:box, bv_pal_4...) +xaxis!(pl1, "Longitude") +yaxis!(pl1, "Latitude") + +# We can repeat essentially the same process for the legend: + +pl2 = bivariatelegend(layer1, layer3; classes=3, bv_pal_4...) +xaxis!(pl2, layernames(EarthEnv, HabitatHeterogeneity, 2)) +yaxis!(pl2, layernames(EarthEnv, LandCover, 9)) + +# And now, we can plot the legend next to the map - future releases of the +# package will hopefully offer this in a far more user friendly way. + +plot(pl1, pl2; layout=@layout [a{0.75w} b]) + +# Using the `subplot` and `inset` arguments of Plots.jl, we can have the legend +# within the figure. Note how in this example we expand the limits on the x axis +# to make the legend fit, but also use more classes in the map to have a +# smoother result. + +p1 = bivariate(layer1, layer3; classes=6, bv_pal_2..., frame=:box, xlim=(-24, maximum(longitudes(layer1)))) +xaxis!(p1, "Longitude") +yaxis!(p1, "Latitude") +p2 = bivariatelegend!( + layer1, + layer3; + bv_pal_2..., + inset=(1, bbox(0.04, 0.05, 0.28, 0.28, :top, :left)), + subplot=2, + xlab=layernames(EarthEnv, HabitatHeterogeneity, 2), + ylab=layernames(EarthEnv, LandCover, 9), + guidefontsize=7, +) + +# Using a trivariate mapping follows the same process, with layers representing +# the red, green, and blue channel respectively. + +plot(layer1, layer2, layer3; st=:trivariate) + +# There are two options for this type of plots. The first is `quantiles=true` +# (which maps quantiles rather than raw values), and the second is +# `simplex=false`, which makes all values sum to 1 within a pixel. For example: + +trivariate(layer1, layer2, layer3; quantiles=true, simplex=true) + +# It is a good idea to question whether using `simplex` is appropriate. The +# legend can also be plotted using `trivariatelegend`: + +trivariatelegend(layer1, layer2, layer3; quantiles=true, simplex=true) + +# The legend function admits three additional arguments for the names of the +# `red`, `green`, and `blue` channels: + +trivariatelegend(layer1, layer2, layer3; quantiles=true, simplex=true, red="Heterogeneity", green="Roughness", blue="Urban") + +# We can also combine the two elements: + +trivariate(layer1, layer2, layer3; xlim=(-24, maximum(longitudes(layer1)))) +xaxis!(p1, "Longitude") +yaxis!(p1, "Latitude") +p2 = trivariatelegend!(layer1, layer2, layer3; inset=(1, bbox(0.04, 0.05, 0.28, 0.28, :top, :left)), subplot=2, red="Heterogeneity", green="Roughness", blue="Urban") \ No newline at end of file From c8b5715d3edc1e3621fdecf6de8c54911585b4d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Poisot?= Date: Fri, 15 Oct 2021 10:45:26 -0400 Subject: [PATCH 5/6] =?UTF-8?q?=F0=9F=A7=91=E2=80=8D=F0=9F=8F=AB=20rename?= =?UTF-8?q?=20mv=20vignette?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/src/examples/{mutivariate.jl => multivariate.jl} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/src/examples/{mutivariate.jl => multivariate.jl} (100%) diff --git a/docs/src/examples/mutivariate.jl b/docs/src/examples/multivariate.jl similarity index 100% rename from docs/src/examples/mutivariate.jl rename to docs/src/examples/multivariate.jl From 0221b556de5650ff7b327cef371ec5484a11f59f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Poisot?= Date: Fri, 15 Oct 2021 11:30:24 -0400 Subject: [PATCH 6/6] fix triv plot --- docs/src/examples/multivariate.jl | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/docs/src/examples/multivariate.jl b/docs/src/examples/multivariate.jl index 8a22f9e8..4e0a2f54 100644 --- a/docs/src/examples/multivariate.jl +++ b/docs/src/examples/multivariate.jl @@ -94,13 +94,13 @@ p2 = bivariatelegend!( # Using a trivariate mapping follows the same process, with layers representing # the red, green, and blue channel respectively. -plot(layer1, layer2, layer3; st=:trivariate) +plot(layer1, layer2, layer3; st=:trivariate, frame=:grid) # There are two options for this type of plots. The first is `quantiles=true` # (which maps quantiles rather than raw values), and the second is # `simplex=false`, which makes all values sum to 1 within a pixel. For example: -trivariate(layer1, layer2, layer3; quantiles=true, simplex=true) +trivariate(layer1, layer2, layer3; quantiles=true, simplex=true, frame=:grid) # It is a good idea to question whether using `simplex` is appropriate. The # legend can also be plotted using `trivariatelegend`: @@ -110,11 +110,16 @@ trivariatelegend(layer1, layer2, layer3; quantiles=true, simplex=true) # The legend function admits three additional arguments for the names of the # `red`, `green`, and `blue` channels: -trivariatelegend(layer1, layer2, layer3; quantiles=true, simplex=true, red="Heterogeneity", green="Roughness", blue="Urban") +trivariatelegend(layer1, layer2, layer3; quantiles=true, simplex=true, red="Heterogeneous", green="Rough", blue="Urbanized") -# We can also combine the two elements: +# We can also combine the two elements. For reasons that are not completely +# clear, the `trivariatelegend!` method makes the whole script hang, so the best +# we can currently do is to put the legend next to the plot. This will be fixed +# in a future release. -trivariate(layer1, layer2, layer3; xlim=(-24, maximum(longitudes(layer1)))) -xaxis!(p1, "Longitude") -yaxis!(p1, "Latitude") -p2 = trivariatelegend!(layer1, layer2, layer3; inset=(1, bbox(0.04, 0.05, 0.28, 0.28, :top, :left)), subplot=2, red="Heterogeneity", green="Roughness", blue="Urban") \ No newline at end of file +tri1 = trivariate(layer1, layer2, layer3; xlim=(-24, maximum(longitudes(layer1)))) +xaxis!(tri1, "Longitude") +yaxis!(tri1, "Latitude") +tri2 = trivariatelegend(layer1, layer2, layer3; inset=(1, bbox(0.04, 0.05, 0.28, 0.28, :top, :left)), subplot=2, red="Heterogeneity", green="Roughness", blue="Urban") + +plot(tri1, tri2; layout=@layout [a{0.75w} b])