Skip to content

Commit 8af0936

Browse files
author
Max Freudenberg
committed
introduce rgbplot
1 parent 889e8b7 commit 8af0936

File tree

4 files changed

+88
-5
lines changed

4 files changed

+88
-5
lines changed

Project.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ Flatten = "4c728ea3-d9ee-5c9a-9642-b6f7d7dc04fa"
1818
GeoFormatTypes = "68eda718-8dee-11e9-39e7-89f7f65f511f"
1919
GeoInterface = "cf35fbd7-0cd7-5166-be24-54bfbe79505f"
2020
HDF5 = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f"
21+
ImageCore = "a09fc81d-aa75-5fe9-8630-4744c3626534"
2122
Missings = "e1d29d7a-bbdc-5cf2-9ac0-f12de2c33e28"
2223
Mmap = "a63ad114-7e13-5084-954f-fe012c677804"
2324
NCDatasets = "85f8d34a-cbdd-5861-8df4-14fed0d494ab"
@@ -28,6 +29,7 @@ RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01"
2829
Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
2930
Requires = "ae029012-a4dd-5104-9daa-d747884805df"
3031
Setfield = "efcf1570-3423-57d1-acb7-fd33fddbac46"
32+
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
3133

3234
[compat]
3335
Adapt = "2, 3.0"

src/Rasters.jl

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,16 @@ import Adapt,
2020
Flatten,
2121
GeoInterface,
2222
HDF5,
23+
ImageCore,
2324
PolygonInbounds,
2425
ProgressMeter,
2526
Missings,
2627
Mmap,
2728
NCDatasets,
2829
RecipesBase,
2930
Reexport,
30-
Setfield
31+
Setfield,
32+
Statistics
3133

3234
Reexport.@reexport using DimensionalData, GeoFormatTypes, RasterDataSources
3335

@@ -40,7 +42,8 @@ using DimensionalData: Name, NoName
4042
using .Dimensions: StandardIndices, DimTuple
4143
using .LookupArrays: LookupArrayTuple
4244

43-
using RecipesBase: @recipe, @series
45+
using RecipesBase: @recipe, @series, @userplot
46+
using Statistics: quantile
4447
using Base: tail, @propagate_inbounds
4548

4649
using Setfield: @set, @set!

src/plotrecipes.jl

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,58 @@ end
187187
end
188188
end
189189

190+
# plot multispectral images as rgb
191+
@userplot RGBPlot
192+
193+
@recipe function f(p::RGBPlot; bands=1:3, low=0.02, high=0.98)
194+
r = first(p.args)
195+
if !(typeof(r) <: AbstractRaster)
196+
error("Plotting RGB images is only implemented for AbstractRasters.")
197+
end
198+
if !hasdim(r, Band)
199+
error("Raster must have a band dimension.")
200+
end
201+
if !(0 <= low <= 1) || !(0 <= high <= 1)
202+
error("'low' and 'high' have to be between 0 and 1 (inclusive).")
203+
end
204+
img = Float32.(copy(r[Band([bands...])]))
205+
normalize!(img, low, high)
206+
img = permutedims(img, (Band, X, Y))
207+
img = DD.reorder(img, X=>DD.ForwardOrdered, Y=>DD.ForwardOrdered)
208+
plot_image = ImageCore.colorview(RGB, img)
209+
210+
yguide, xguide = DD.label(dims(img, (Y,X)))
211+
212+
y, x = map(Rasters._prepare, dims(img, (Y,X)))
213+
214+
rdt = DD.refdims_title(img; issingle=true)
215+
:title --> (rdt === "" ? Rasters._maybename(img) : Rasters._maybename(img) * "\n" * rdt)
216+
:xguide --> xguide
217+
:xrotation --> 45
218+
:yguide --> yguide
219+
:tick_direction --> :out
220+
:framestyle --> :box
221+
222+
if all(d -> lookup(d) isa Mapped, (x, y))
223+
:xlims --> mappedbounds(x)
224+
:ylims --> mappedbounds(y)
225+
:aspect_ratio --> :equal
226+
else
227+
:xlims --> bounds(img, x)
228+
:ylims --> bounds(img, y)
229+
bnds = bounds(img, (X, Y))
230+
# # TODO: Rethink this....
231+
s1, s2 = map(((l, u),) -> (u - l), bnds) ./ (size(img, X), size(img, Y))
232+
square_pixels = s2 / s1
233+
:aspect_ratio --> square_pixels
234+
end
235+
236+
:seriestype --> :image
237+
:yflip --> false
238+
DD.index(x), DD.index(y), plot_image'
239+
end
240+
241+
190242
# Plots.jl heatmaps pixels are centered.
191243
# So we should center the index, and use the projected value.
192244
_prepare(d::Dimension) = d |> _maybe_shift |> _maybe_mapped
@@ -243,3 +295,26 @@ function DD.refdims_title(refdim::Band; issingle=false)
243295
end
244296
end
245297

298+
function eachband_view(r::Raster)
299+
nbands = size(r, Band)
300+
return (view(r, Band(n)) for n in 1:nbands)
301+
end
302+
303+
function normalize!(raster, low=0.1, high=0.9)
304+
if !hasdim(raster, Band)
305+
l = quantile(skipmissing(raster), low)
306+
h = quantile(skipmissing(raster), high)
307+
raster .-= l
308+
raster ./= h - l + eps(float(eltype(raster)))
309+
raster .= clamp.(raster, zero(eltype(raster)), one(eltype(raster)))
310+
else
311+
for band in eachband_view(raster)
312+
l = quantile(skipmissing(band), low)
313+
h = quantile(skipmissing(band), high)
314+
band .-= l
315+
band ./= h - l + eps(float(eltype(raster)))
316+
band .= clamp.(band, zero(eltype(raster)), one(eltype(raster)))
317+
end
318+
end
319+
return raster
320+
end

src/skipmissing.jl

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,13 @@ end
3535

3636
_missing(x, itr) = ismissing(x) || x == missingval(itr) # mind the order, as comparison with missing returns missing
3737
function _missing(x::AbstractFloat, itr)
38-
if isnan(missingval(itr))
39-
return ismissing(x) || isnan(x)
38+
# x is an AbstractFloat here and hence cannot be nothing or missing
39+
if isnothing(missingval(itr))
40+
return false
41+
elseif isnan(missingval(itr))
42+
return isnan(x)
4043
else
41-
return ismissing(x) || x == missingval(itr)
44+
return x == missingval(itr)
4245
end
4346
end
4447

0 commit comments

Comments
 (0)