rikinet
rikinet

Reputation: 103

Continue with rest of multiplot despite 'all points Y value undefined' error in one sub-plot

In a multiplot, when I get the error all points Y value undefined for one sub-plot, how can I make gnuplot continue and output at least the remaining sub-plots?

Background:

Let's assume the following example time-latitude data from some GPS tracker. In reality, my data contains more than 100k sub-second samples spanning multiple days.

$DATA << EOD
# Zero values are invalid (no GPS fix) and shall be ignored.
#
# Segment 1) Trip 1 around longitude 50.0:
00:01   49.99990
00:02   49.99994
00:03   50.00006
00:04   50.00008
00:05   0
00:06   0
00:07   0
00:08   50.00004
00:09   50.00002
#
# Segment 2) no data recorded at all
# ...
#
# Segment 3) Trip 2 around longitude -164.0:
00:21   -163.99992
00:22   -163.99998
00:23   -164.00006
00:24   -164.00004
00:25   0
00:26   0
00:27   0
00:28   -164.00002
00:29   -164.00002
#
# Segment 3) only invalid data recorded
00:31   0
00:32   0
00:33   0
00:34   0
00:35   0
00:36   0
00:37   0
00:38   0
00:39   0
#
# Segment 4) Trip 3 around longitude 120.0:
00:41   120.00000
00:42   120.00002
00:43   119.99998
00:44   119.99996
00:45   119.99994
00:46   119.99998
00:47   120.00002
00:48   120.00006
00:49   120.00002
EOD

I want to plot that data in a multiplot, together with other time-value data from another source, with other sample times, but using the same X range. For demonstration purposes, let's simply use:

# data for second sub-plot
y(x) = sin(x)

I can plot the complete time range (00:00 to 00:50) with the following gnuplot code:

# General settings
timeformat = "%H:%M"
set xdata time
set timefmt timeformat
set xtics timedate
set xtics format timeformat
set xtics 10*60
set mxtics 10
set ytics format "%.5f"

# Function to skip zero / invalid longitude values:
strip_zero(Y) = ( Y == 0 ? NaN : Y )

# Plot full X range:
set multiplot layout 2, 1
set xrange [0 : 50*60]
set yrange [-180<*:*<180]
plot $DATA using 1: (strip_zero($2)) with lines notitle, \
     $DATA using 1: (strip_zero($2)) with points notitle
set yrange [*:*]
plot y(x) with lines
unset multiplot

Result: multiplot with full X range

Obviously, the resolution around the clusters at 50, -160 and 120 is quite bad. So I want to break down the X range into multiple 10-minute zoom slices. Using auto-scaling, the Y axis will be zoomed to give a better resolution. I would expect to get 5 plots covering (roughly) the areas marked as 1 - 5 in the diagram above.

I am using a do for loop to generate the zoomed diagrams:

# Plot 10-minute slices:
do for [x_min = 0 : 40 : 10] {
    set multiplot layout 2, 1
    set xrange [x_min * 60 : (x_min + 10) * 60]
    set yrange [-180<*:*<180]
    plot $DATA using 1: (strip_zero($2)) with lines notitle, \
         $DATA using 1: (strip_zero($2)) with point notitle
    set yrange [*:*]
    plot y(x) with lines
    unset multiplot
}

Segment 1 can still be produced as expected: multiplot zoomed to first 10 minutes

But when hitting segment 2 (X range 00:10 to 00:20) and later segment 4 (00:30 to 00:40), I get the error all points Y value undefined and gnuplot exits. Note that segment 2 fails because there are no points in the dataset at all, but segment 4 fails because the existing points are converted to NaN by strip_zero().

I am looking for a way to keep going after the error, so that all 5 multiplots are produced, and at least the second sub-plot in each multiplot is still plotted.

I don't care what gnuplot outputs instead of the failing sub-plot (nothing, a blank diagram space, an empty grid, the connecting line between points outside the given X range, ...).

I would prefer solutions that do not require external pre-processing with shell scripts etc., to ensure maximum portability.

Gnuplot 5.2 patchlevel 5 under native Windows 10 or cygwin.

Upvotes: 2

Views: 341

Answers (1)

theozh
theozh

Reputation: 25843

To skip a plot in a multiplot environment generally can be done with set multiplot next, however, skipping a plot with no data, I am not aware if there is an "easy" way to achieve this. So, have a look at the workaround below. It is unoptimized code but tested in gnuplot 5.2.5. The basic idea is to plot data into a dummy table and check if the time is within the range of the segment and also contains some data other than 0. There is probably a lot of more work to make the graph look nice. Just see it as a possible starting point.

### skipping plot having no data
reset session

$DATA << EOD
# Zero values are invalid (no GPS fix) and shall be ignored.
#
# Segment 1) Trip 1 around longitude 50.0:
00:01   49.99990
00:02   49.99994
00:03   50.00006
00:04   50.00008
00:05   0
00:06   0
00:07   0
00:08   50.00004
00:09   50.00002
#
# Segment 2) no data recorded at all
# ...
#
# Segment 3) Trip 2 around longitude -164.0:
00:21   -163.99992
00:22   -163.99998
00:23   -164.00006
00:24   -164.00004
00:25   0
00:26   0
00:27   0
00:28   -164.00002
00:29   -164.00002
#
# Segment 3) only invalid data recorded
00:31   0
00:32   0
00:33   0
00:34   0
00:35   0
00:36   0
00:37   0
00:38   0
00:39   0
#
# Segment 4) Trip 3 around longitude 120.0:
00:41   120.00000
00:42   120.00002
00:43   119.99998
00:44   119.99996
00:45   119.99994
00:46   119.99998
00:47   120.00002
00:48   120.00006
00:49   120.00002
EOD

# data for second sub-plot
y(x) = sin(x)

# General settings
timeformat = "%H:%M"
set xdata time
set timefmt timeformat
set xtics timedate
set xtics format timeformat
set xtics 10*60
set mxtics 10
set ytics format "%.5f"

# Function to skip zero / invalid longitude values:
strip_zero(Y) = ( Y == 0 ? NaN : Y )


# Plot 10-minute slices:
set multiplot layout 2,5 columnsfirst
set margins 8,1,2,1

x_start = 0
x_end = 40
x_step = 10
do for [i = x_start*60:x_end*60:x_step*60] {
    set xrange [i : i + x_step*60]
    ContainsData = 0
    set table $Dummy
        plot $DATA using 1:\
        ((timecolumn(1)>=i) && (timecolumn(1)<=(i + x_step*60)) && ($2!=0) ?\
        ContainsData=1 : 0) with table
    unset table

    if (ContainsData == 0) { set multiplot next }
    else {
        plot [i : i + x_step*60] $DATA using 1:(strip_zero($2)) with lp pt 7 lc rgb "red" notitle
    }
    plot y(x) with lines notitle
}
unset multiplot
### end of code

Which results in:

enter image description here

Upvotes: 0

Related Questions