GenericMappingTools / gmt

The Generic Mapping Tools
https://www.generic-mapping-tools.org
Other
824 stars 343 forks source link

Plot lines in events as densely sampled points #4312

Closed PaulWessel closed 3 years ago

PaulWessel commented 3 years ago

Description of the desired feature

A very recent upgrade to events allows for plotting of lines and polygons. For lines, we consider the "event" a section of the line (given by x, y, time) records that falls within the visible time window set via -T, -L and -Es. That section is then plotted as a line, given the chosen color and pen. The effect is that the line segment moves over time as the window changes with time, but the line is of uniform color and width. See here for a demo of movies with lines and polygons.

An alternative way to implement lines in events (but not elsewhere) is to sample the line fairly densely and plot these points as circles with a diameter than matches the pen width w. With an along-line sampling interval of f*w, where f is a small constant < 1, the plotted points are indistinguishable from a solid line, especially given the lower resolution of movie frames. Below I show an example of a data set (red dots), these plotted as a line, then as resampled points for two f values:

point-lines

You will need to zoom in very hard to tell that the bottom line is made of dense points. But in movie frames you would not be able to given their finite and relatively low resolution (even at 4k). It is possible that f = 0.1 is overkill - I will need to do more experiments in a movie setting so will keep it a variable. Optimal f is likely a function of dots-per-inch and pen width.

This implementation would open up some interesting possibilities:

  1. The color of the "line" can now vary since each circle could be associated with a z-value (input records x, y, z, time) and converted to color via -Ccpt
  2. The event feature of magnifying the symbol size upon arrival (-Ms) could be implemented and allow for a variable-width line
  3. The event feature of intensifying the symbol color upon arrival (-Mi) could be implemented to allow for adding intensity to the symbol color
  4. The coda fade modifier would allow old line to remain on the screen but with a faded/transparent look.

The "line event" is still likely to contain several points making up the line segment, hence several points will be plotted for a single time selection (-T) but they could now possibly have different size, color, and intensity. Yet, with no -M selection we would visually get the same image as we do in the current line-implementation; we could even retain the current code to be called in that case - avoiding unnecessary resampling.

To ensure continuity across frames, I believe the resampling needs to happen once for the entire file in the pre-script. However, that would put too much responsibility on the user. An alternative is to let events do this the very first time it is called (known by looking for the sampled file which is not present the first time). This means frame one cannot be included in the parallel execution in movie since other frames would fail. Thus, for variable lines we could do frame one separately first before we enter the parallel execution loop. Thus, the bulk of the movie would use the resampled file and avoid repeated mapproject and sample1d calls.

I think this is a winner that will broadly extend the uses of movies for lines (cruise tracks, time-series, etc.).

Comments/suggestions welcome, @GenericMappingTools/core and @GenericMappingTools/gmt-contributors.

PaulWessel commented 3 years ago

OK, would seen nobody but me have needs to do animations with lines and polygons, which frees me to do whatever I deem reasonable. Surprisingly, I will endorse my own proposal.

seisman commented 3 years ago

It sounds like a good idea. Here are my two questions:

  1. Does plotting dense points increase the image size a lot, especially for long and complicated lines?
  2. What's the interpolation method you're going to use for sample1d? Linear interpolation?
PaulWessel commented 3 years ago

It sounds like a good idea. Here are my two questions:

  1. Does plotting dense points increase the image size a lot, especially for long and complicated lines?

It will take longer to render each PNG since circles are more complicated than lines (but not necessarily true for fat lines). However, in the end it becomes a relative low-res PNG (even HD is only 1920x1080) and one of many to convert to movie. And excellent quality is way higher on my list than quick and dirty. Note, however, we will only switch to dots if required (variable pen thickness, color, -Mi, -Ms settings; otherwise we might as well draw the line.

  1. What's the interpolation method you're going to use for sample1d? Linear interpolation?

Will be controlled by GMT_INTERPOLANT [akima].

PaulWessel commented 3 years ago

I am now leaning towards adding a simple recipe in the movie documentation on how to turn a line data sets into a collection of dense points (to be plotted as circles) and then uses can use events -Scsize instead and have all the benefits of symbol temporal behavior. It seems too much effort to have movie do this behind the scenes rather than do it one with a few GMT commands and prep the data set. I will experiment with this. The recipe (sort of used in the example above) requires -R -J (which the user needs anyway) and some measure of how often to sample the line. I will report back once I have examples of how this works.

PaulWessel commented 3 years ago

I am experimenting with how densely one need to sample a line in order to plot a sequence of circles instead and not being able to tell the difference. Remember, this is for making movies which have relative low dpi, but one could do the same as a trick for plotting a PDF by using a much higher dpi. It seems to me this is simply related to the dpi of the rasterized frame. Attached are 24 PNG images made by drawing a seismic trace from @seisman using four different pen thicknesses (0.25p, 0.5p, 1p, and 2p) for a (pretended) HD movie (which has a 200 dpi for -CHD in movie). For each pen thickness I compare that to 5 different sampling intervals (spacing between circles), d = X / dpi, where X is 0.5, 1, 2, 4, 8). Clearly, 8 means the circles are 8 times as far apart as the X = 1 value which means d = 1 /dpi (in inches). All of these PNGs can be made smoother by using -H in psconvert, so we are looking at the worst case scenario.

My observations are:

  1. The thinnest lines (e.g., 0.25p = 0.25 / 72 = 0.0035 inches is a bit smaller than the pixel size (1/200 dpi = 0.005 inc) and look a bit ugly for no -H
  2. Once X drops to 2 (or possibly 1) the line and circle renditions are very similar

Because the recipe is a bit ugly and depends on whether or not this is a time or space series, as well as number of data columns (e.g., -i, -o), it currently looks something like this:

d=$(gmt math -Q $dpi INV $x MUL =)
gmt mapproject linefile.txt $R $J -G+uC | gmt sample1d -N2 -T${d}c -Fl | gmt mapproject $R $J-I -o0-2,0 --> pointfile.txt

but there are times you may wish to control which spline in -F, and for absolute time you may need to change FORMAT_CLOCK_OUT and possibly -f settings. Thus, if I just document these steps I suspect nobody will ever try it but me, defeating the whole point.

My alternative solution is simply to let the existing (but new) psevents -Ar option become -Ar[dpi]. If a dpi is appended we simply use all the information we have (via the given options) and compute pointfile.txt internally and write to stdout no plot is made. Without the dpi we continue to read line records. This simple setup will allow me to apply the recipe above under-the-hood and no need to change movie.

dots.zip