ncss-tech / aqp

Algorithms for Quantitative Pedology
http://ncss-tech.github.io/aqp/
54 stars 14 forks source link

plotSPC with small number of profiles #128

Closed brownag closed 4 years ago

brownag commented 4 years ago

See new buffer.spc argument in help file.

Default value is 0 to reproduce historic functionality -- and preserve use of plotSPC in higher level functions/examples. However, a buffer.spc of 1 would work well for nearly all plots I've seen, and I think lessens the challenge of getting "reasonable" soil profile sketch margins set up.

Example 1 -- a n=2 profile profile plot

library(aqp)
library(soilDB)

data("loafercreek")

par(mar=c(0,0,0,0))
plotSPC(filter(loafercreek, clay > 50), 
        axis.line.offset = -8,
        buffer.spc = 1, 
        cex.names = 0.7,
        max.depth = 100)

Example 2 use of buffer.spc=1 for the oh-so-difficult single profile plot. BEFORE (buffer.spc=0) default_plotSPC

AFTER (buffer.spc=1) new_plotSPC

I don't know that this really addresses the dangling issues from #62 ... it is an alternate approach that perhaps requires less understanding of the many arguments to this function.

The above images were made without offset of axis or adjustment of label size. It follows easily that you can find an argument (axis.line.offset cex.names) to do each of those things.

However, the current code we have for doing single profile or two profile plots -- involving partial shifts along the x axis, etc, is unlikely to be something a user would come up with on their own.

That's not to say this is a particularly elegant solution -- i first noticed it worked by padding empty SPCs on the margins of x -- but can replicate this behavior simply by manipulating n plot.order relative.position pLabels pID etc within plotSPC based on the buffer.spc argument

Perhaps when #59 is complete none of this wizardry will be needed.

dylanbeaudette commented 4 years ago

Interesting, and thanks for the contribution. I'm not sure that more wizardry is required. I agree that the solutions in #62 are fine for power users but not general solutions. What we really need is a better heuristic for setting up the plotting region without messing around with indexing. After all, the integer indexing along the x-axis is what makes it possible to quickly and intuitively annotate these sketches. Adding invisible profiles seems like it will create very complex code, especially when using the plot.order argument, in fact, it appears plot.order is ignored:

plotSPC(loafercreek[1:3,], buffer.spc=1, cex.names=0.8, axis.line.offset = -5, plot.order=3:1)

Also note that the magical plot metadata includes the fake profiles:

get('last_spc_plot', envir = aqp.env)
$width
[1] 0.2

$plot.order
[1] 1 2 3 4 5

$x0
[1] 1 2 3 4 5

$pIDs
[1] NA       "64505"  "115595" "115819" NA   

I'm all in favor of making plotSPC work with any number of profiles, but not quite like this.

dylanbeaudette commented 4 years ago

Much of this problem comes down to sloppy creation (e.g. I did most testing with n > 5 profiles) of the plotting frame via xlim argument to the initial call to plot().

I've added some additional heuristics (9dfbfab) along with a more reasonable xlim calculation. A continuous correction would be ideal, as the current approach deals with the major changes in plot geometry at 1, 2, 5, 20+ profiles.

Seems to scale fine:

library(aqp)
library(soilDB)

data("loafercreek")

plotSPC(loafercreek[1:1,])
plotSPC(loafercreek[1:2,])
plotSPC(loafercreek[1:3,])
plotSPC(loafercreek[1:4,])
plotSPC(loafercreek[1:5,])
plotSPC(loafercreek[1:6,])
plotSPC(loafercreek[1:10,])
plotSPC(loafercreek[1:25,])
brownag commented 4 years ago

Cool, that is handy. I fixed the improper handling of relative.pos and plot.order. Now works perfectly. Even with things that rely on the last plot output. But I could just as well revert these changes.

library(aqp)
library(soilDB)

data("loafercreek")

par(mar=c(0,0,0,0))
s <- filter(loafercreek, clay > 50)
plotSPC(s, axis.line.offset = -8,
        buffer.spc=1,
        plot.order = c(2,1),
        relative.pos = c(1.7,2.1))
addDiagnosticBracket(s, kind = 'argillic horizon', lwd=3)
dylanbeaudette commented 4 years ago

That seems closer. The plot.order result in the last_spc_plot object is still kind of confusing.

image

I applaud the creativity, but this approach seems to incur a lot of overhead (in all related high/low level plotting functions). At this point I'd prefer a thoughtful re-design vs. bolting any more functionality onto this framework.

brownag commented 4 years ago

Thanks for implementing some better heuristics / closing #62

The default plot with one/few profiles looks a lot better. It makes a big difference when just interactively inspecting data -- where you commonly wind up with only a couple pedons in result.

dylanbeaudette commented 4 years ago

Well thanks for refusing to settle for a crappy degradation of plotSPC as n -> 1. I'm sorry about the time spent on the code. Once we are back at work it might be worth a re-design of plotSPC.

brownag commented 4 years ago

No worries -- it sure is nice to see those SPCs behave in e.g shiny-pedon-summary

I have thought a lot about trying to develop some sort of plotSPC analogue -- not a replacement for full functionality -- that allows for some/more user interaction -- say, to assign general horizon labels