2d visualization

This commit is contained in:
qwjyh 2024-01-06 23:46:51 +09:00
parent 9996227460
commit d99771045e
5 changed files with 474 additions and 213 deletions

View file

@ -3,11 +3,13 @@ module CoordVisualize
using Dates
export CoordLog
export ColorMapFuncs
include("typedef.jl")
include("parser.jl")
include("edit.jl")
include("print.jl")
include("recipes.jl")
include("visualize.jl")
end # module CoordVisualize

View file

@ -11,10 +11,10 @@ If keyword argument `interactive` is set to `true`, prompts will be shown to
receive custom notes for each `CoordLog`.
Otherwise the note is set to ""(empty string).
"""
function parse_log(filepath::AbstractString; interactive=false)::Vector{CoordLog}
function parse_log(filepath::AbstractString; interactive=false)::Vector{CoordLog{Float64}}
istracing::Bool = false
coords_trace = Vector{Vector{Float64}}(undef, 0) # SVector ?
ret = Vector{CoordLog}()
ret = Vector{CoordLog{Float64}}()
log_date = DateTime(0)
for (i, l) in enumerate(readlines(filepath))
# skip logs not from tracecoord

264
src/recipes.jl Normal file
View file

@ -0,0 +1,264 @@
using GLMakie
using ColorTypes
using ColorSchemes
"""
Predefined color map functions.
# Types
[`ColorMapFunc`](@ref)
# Interface
Define these methods for the ColorMapFunc.
(AbstractVector{CoordLog}) -> Vector{ [0, 1]}, ticks
"""
module ColorMapFuncs
using ..CoordVisualize: CoordLog, n_coords
using ColorTypes
using Dates: DateTime, DateFormat, @dateformat_str, format
using Makie: wong_colors, Scene
"""
# Methods
(f::ColorMapFunc)(cmap, logs)
Helper method.
"""
abstract type ColorMapFunc end
function (f::ColorMapFunc)(cmap, logs, n)
steps, ticklabels = f(logs, n)
ticks = collect(LinRange(0, 1, n))
return get.(Ref(cmap), steps), (ticks, ticklabels)
end
"Use same color."
struct Constant <: ColorMapFunc
color::Colorant
end
Constant(c::Symbol) = Constant(parse(Colorant, c))
function (c::Constant)(map, logs, n)
# Iterators.repeated(c.color, length(logs))
fill(c.color, sum(n_coords, logs)), ([], [])
end
"Use colormap."
struct ColorMap <: ColorMapFunc
colormap::AbstractVector{<:Colorant}
end
ColorMap() = ColorMap(wong_colors())
ColorMap(cmap::Vector{Symbol}) = ColorMap(map(cmap) do s
parse(Colorant, s)
end)
function ColorMap(scene::Scene)
ColorMap(theme(scene, :linecolor))
end
function (cm::ColorMap)(map, logs, n)
cm = Iterators.cycle(cm.colormap)
nlog_s = Iterators.map(n_coords, logs)
colors =
Iterators.map(zip(cm, nlog_s)) do (color, count)
Iterators.repeated(color, count)
end |> Iterators.flatten |> collect
return colors, ([], [])
end
"Color depending on log date."
struct Date <: ColorMapFunc end
function (::Date)(logs::AbstractVector{CoordLog{T}}, n) where {T}
dformat = dateformat"yyyy-m-d"
logdates::Vector{DateTime} = map(logs) do log
fill(log.logdate, n_coords(log))
end |> (v -> vcat(v...))
fst, lst = extrema(logdates)
normeddate = (logdates .- fst) ./ (lst - fst)
diff = (lst - fst) / (n - 1)
ticklabels = format.(fst:diff:lst, dformat)
return normeddate, ticklabels
end
"Color depending on altitude."
struct Altitude <: ColorMapFunc end
function (f::Altitude)(logs::AbstractVector{CoordLog{T}}, n) where {T}
altitudes = map(logs) do log
Iterators.map(eachrow(log.coords)) do c
c[2]
end
end |> Iterators.flatten |> collect
low, high = extrema(altitudes)
normedalt = (altitudes .- low) ./ (high - low)
ticklabels = string.(round.(LinRange(low, high, n)))
return normedalt, ticklabels
end
end # module ColorMapFunc
# TODO: alpha?
"""
trace2ds(log::Vector{CoordLog})
# Arguments
TODO
"""
@recipe(Trace2Ds, log) do scene
Attributes(;
showmarker = false,
marker = theme(scene, :marker),
markercolormap = theme(scene, :colormap),
markersize = theme(scene, :markersize),
strokewidth = 0,
showline = true,
linecolormap = theme(scene, :colormap),
linestyle = theme(scene, :linestyle),
linewidth = theme(scene, :linewidth),
inspectable = theme(scene, :inspectable),
lcolormapfunc = ColorMapFuncs.ColorMap(), # or func like in ColorMapFunc
mcolormapfunc = ColorMapFuncs.ColorMap(),
lcolorticks = nothing,
nlcolorticks = 5,
mcolorticks = nothing,
nmcolorticks = 5,
)
end
function Makie.plot!(tr2d::Trace2Ds)
# @info "logs" tr2d
# @info "fieldnames" tr2d.log
# @info "" theme(tr2d, :colormap)
lcolormapfunc = tr2d.lcolormapfunc
ntraces = length(tr2d.log[]) # number of CoordLog
linesegs = Observable(Point2f[])
points = Observable(Point2f[])
altitudes = Observable(Float64[])
notes = Observable(String[])
if tr2d.markercolormap[] isa Symbol
tr2d.markercolormap[] = getproperty(ColorSchemes, tr2d.markercolormap[])
end
markercolors = Observable(
tr2d.mcolormapfunc[](tr2d.markercolormap[], tr2d.log[], tr2d.nmcolorticks[])[1],
)
mticks = tr2d.mcolorticks
if tr2d.linecolormap[] isa Symbol
tr2d.linecolormap[] = getproperty(ColorSchemes, tr2d.linecolormap[])
end
# @info "lcolormapfunc" lcolormapfunc
linecolors =
Observable(lcolormapfunc[](tr2d.linecolormap[], tr2d.log[], tr2d.nlcolorticks[])[1])
lticks = tr2d.lcolorticks
# helper function which mutates observables
function update_plot(
logs::AbstractVector{<:CoordLog{T}},
lcolormap,
mcolormap,
lcolormapfunc, #::Union{Symbol, Tuple{Symbol, Symbol}},
mcolormapfunc,
) where {T}
@info "update_plot"
markercolors[]
linecolors[]
# @info "logs on update_plot" logs
# init
empty!(linesegs[])
empty!(points[])
empty!(altitudes[])
empty!(markercolors[])
if linecolors[] isa AbstractVector
empty!(linecolors[])
else
linecolors[] = []
end
# update
colors_count = 1
lcolors, lticks[] = lcolormapfunc(lcolormap, logs, tr2d.nlcolorticks[])
mcolors, mticks[] = mcolormapfunc(mcolormap, logs, tr2d.nmcolorticks[])
for (i, log) in enumerate(logs)
first = true
for point in eachrow(log.coords)
push!(linesegs[], Point2f(point[1], point[3]))
push!(linesegs[], Point2f(point[1], point[3]))
push!(points[], Point2f(point[1], point[3]))
push!(altitudes[], point[2])
push!(linecolors[], lcolors[colors_count])
push!(linecolors[], lcolors[colors_count])
push!(markercolors[], mcolors[colors_count])
colors_count += 1
# # marker
# if !isnothing(mcolormapfunc)
# push!(markercolors[], mcolormapfunc(logs)[i])
# end
if first
pop!(linesegs[])
pop!(linecolors[])
first = false
else
# # colors
# if !isnothing(lcolormapfunc)
# push!(linecolors[], lcolormapfunc(logs)[i])
# end
end
end
pop!(linesegs[])
pop!(linecolors[])
push!(notes[], log.note)
end
markercolors[] = markercolors[]
linecolors[] = linecolors[]
end
Makie.Observables.onany(
update_plot,
tr2d.log,
tr2d.linecolormap,
tr2d.markercolormap,
lcolormapfunc,
tr2d.mcolormapfunc,
)
# init
update_plot(
tr2d.log[],
tr2d.linecolormap[],
tr2d.markercolormap[],
lcolormapfunc[],
tr2d.mcolormapfunc[],
)
linesegments!(
tr2d,
linesegs,
color = linecolors,
linewidth = tr2d.linewidth,
linestyle = tr2d.linestyle,
visible = tr2d.showline,
# inspector_label = (self, i, pos) ->
)
scatter!(
tr2d,
points,
color = markercolors,
markersize = tr2d.markersize,
strokewidth = tr2d.strokewidth,
visible = tr2d.showmarker,
)
# @info "dump" dump(tr2d, maxdepth = 1)
# @info "attributes" dump(tr2d.attributes, maxdepth = 3)
tr2d
end

View file

@ -10,7 +10,7 @@ function _plot_map!(ax, mappath::AbstractString)
heatmap!(
ax,
(1:width) .- width ÷ 2,
(1:heigh) .- width ÷ 2,
(1:heigh) .- heigh ÷ 2,
rotr90(map),
inspectable = false,
)
@ -25,213 +25,3 @@ function view_with_map(log::CoordLog; map = "map.png")
return fig
end
"""
Predefined color map functions.
# Interface
(map, AbstractVector{CoordLog}) -> Vector{<: Colorant}
"""
module ColorMapFunc
using ..CoordVisualize: CoordLog, n_coords
using ColorTypes
using Dates: DateTime
using Makie: wong_colors, Scene
"Use same color."
struct Constant
color::Colorant
end
Constant(c::Symbol) = Constant(parse(Colorant, c))
function (c::Constant)(map, logs)
# Iterators.repeated(c.color, length(logs))
fill(c.color, sum(n_coords, logs))
end
"Use colormap."
struct ColorMap
colormap::AbstractVector{<:Colorant}
end
ColorMap() = ColorMap(wong_colors())
ColorMap(cmap::Vector{Symbol}) = ColorMap(map(cmap) do s
parse(Colorant, s)
end)
function ColorMap(scene::Scene)
ColorMap(theme(scene, :linecolor))
end
function (cm::ColorMap)(map, logs)
cm = Iterators.cycle(cm.colormap)
nlog_s = Iterators.map(n_coords, logs)
Iterators.map(zip(cm, nlog_s)) do (color, count)
Iterators.repeated(color, count)
end |> Iterators.flatten |> collect
end
"Color depending on log date."
function date(cmap, logs::AbstractVector{CoordLog})
logdates::Vector{DateTime} = map(logs) do log
fill(log.logdate, n_coords(log))
end |> (v -> vcat(v...))
lst, fst = extrema(logdates)
normeddate = (logdates .- lst) ./ (fst - lst)
return get.(Ref(cmap), normeddate)
end
"Color depending on altitude."
function altitude(cmap, logs::AbstractVector{CoordLog})
altitudes = map(logs) do log
map(eachrow(log.coords)) do c
c[2]
end
end |> Iterators.flatten |> collect
low, high = extrema(altitudes)
normedalt = (altitudes .- low) ./ (high - low)
return get.(Ref(cmap), normedalt)
end
end # module ColorMapFunc
# TODO: alpha?
"""
trace2ds(log::Vector{CoordLog})
# Arguments
TODO
"""
@recipe(Trace2Ds, log) do scene
Attributes(;
marker = theme(scene, :marker),
markercolormap = theme(scene, :colormap),
markersize = theme(scene, :markersize),
strokewidth = theme(scene, :strokewidth),
linecolormap = theme(scene, :colormap),
linestyle = theme(scene, :linestyle),
linewidth = theme(scene, :linewidth),
inspectable = theme(scene, :inspectable),
lcolormapfunc = ColorMapFunc.ColorMap(), # or func like in ColorMapFunc
mcolormapfunc = ColorMapFunc.ColorMap(),
)
end
function Makie.plot!(tr2d::Trace2Ds)
# @info "logs" tr2d
# @info "fieldnames" tr2d.log
# @info "" theme(tr2d, :colormap)
lcolormapfunc = tr2d.lcolormapfunc
ntraces = length(tr2d.log[]) # number of CoordLog
linesegs = Observable(Point2f[])
points = Observable(Point2f[])
notes = Observable(String[])
if tr2d.markercolormap[] isa Symbol
tr2d.markercolormap[] = getproperty(ColorSchemes, tr2d.markercolormap[])
end
markercolors = Observable(tr2d.mcolormapfunc[](tr2d.markercolormap[], tr2d.log[]))
if tr2d.linecolormap[] isa Symbol
tr2d.linecolormap[] = getproperty(ColorSchemes, tr2d.linecolormap[])
end
# @info "lcolormapfunc" lcolormapfunc
linecolors = Observable(lcolormapfunc[](tr2d.linecolormap[], tr2d.log[]))
# helper function which mutates observables
function update_plot(
logs::AbstractVector{<:CoordLog},
lcolormap,
mcolormap,
lcolormapfunc, #::Union{Symbol, Tuple{Symbol, Symbol}},
mcolormapfunc,
)
@info "update_plot"
markercolors[]
linecolors[]
# @info "logs on update_plot" logs
# init
empty!(linesegs[])
empty!(points[])
empty!(markercolors[])
if linecolors[] isa AbstractVector
empty!(linecolors[])
else
linecolors[] = []
end
# update
colors_count = 1
for (i, log) in enumerate(logs)
first = true
for point in eachrow(log.coords)
push!(linesegs[], Point2f(point[1], point[3]))
push!(linesegs[], Point2f(point[1], point[3]))
push!(points[], Point2f(point[1], point[3]))
push!(linecolors[], lcolormapfunc(lcolormap, logs)[colors_count])
push!(linecolors[], lcolormapfunc(lcolormap, logs)[colors_count])
push!(markercolors[], mcolormapfunc(mcolormap, logs)[colors_count])
colors_count += 1
# # marker
# if !isnothing(mcolormapfunc)
# push!(markercolors[], mcolormapfunc(logs)[i])
# end
if first
pop!(linesegs[])
pop!(linecolors[])
first = false
else
# # colors
# if !isnothing(lcolormapfunc)
# push!(linecolors[], lcolormapfunc(logs)[i])
# end
end
end
pop!(linesegs[])
pop!(linecolors[])
push!(notes[], log.note)
end
markercolors[] = markercolors[]
linecolors[] = linecolors[]
end
Makie.Observables.onany(
update_plot,
tr2d.log,
tr2d.linecolormap,
tr2d.markercolormap,
lcolormapfunc,
tr2d.mcolormapfunc,
)
# init
update_plot(
tr2d.log[],
tr2d.linecolormap[],
tr2d.markercolormap[],
lcolormapfunc[],
tr2d.mcolormapfunc[],
)
linesegments!(
tr2d,
linesegs,
color = linecolors,
linewidth = tr2d.linewidth,
linestyle = tr2d.linestyle,
)
scatter!(
tr2d,
points,
color = markercolors,
markersize = tr2d.markersize,
strokewidth = tr2d.strokewidth,
)
# @info "dump" dump(tr2d, maxdepth = 1)
# @info "attributes" dump(tr2d.attributes, maxdepth = 3)
tr2d
end