How to create a 3D network with GMT.jl

Hi everyone,
I would like to create a 3D network using real coordinates of seismic centers. Without going too much into detail, I have a graph file of edgelists and I laso have the coordinates of the nodes. I am able to 3D plot the vertices with no problem as such:

scatter3(df_filtered_cubes.cubeLongitude, df_filtered_cubes.cubeLatitude, -df_filtered_cubes.cubeDepth,
frame="SEnwZ1+b xafg yafg zafg", proj=:merc, marker=:cube, markerline=:faint, markersize=0.1,
view=(135,20), alpha=30, show=true)

The problem is when I want to connect the vertices in a specific way (given the graph edges). I tried numerous ways to do this but nothing works. I even tried a workaround by plotting recursively each edge, but for some reason, when calling the plot twice, the package does not plot on the same 3D plane, but it plots it 2D, under the original scatter!
Here is how to replicate the issue:

cat > example.txt << EOF
0 0 0
1 1 1
2 2 0
3 3 1

then in julia

plot3d!("./example.txt", pen=:thinner, savefig="example.png", show=true)

The proper way to do this seems to use the F|conn|connection option for the plot, but I cannot seem to figure out how to properly do this. I found this answer to a similar problem (but the person tries to plot in 2D, which is easier).

If anyone would be able to offer the proper way to pass the connection option for the plot, that would be much appreciated!

Thank you in advance!

Yes, the F|conn|connection option does not exist in the 3D case. You may ask in the GMT forum on how hard would it be to add that option to plot3d

The manual way works but you had the bad luck of falling onto a bug.
In the next I’ll use the Vd debugging option to show what is actual GMT command that is generated

t = [0 0 0; 1 1 1; 2 2 0; 3 3 1];

scatter3(t, Vd=1)
        psxyz  -JX14c/9.5c -Baf -Bza -p200/30 -R-0.1/3.1/-0.1/3.1/0/1 -JZ6c -Su2p -P -K > c:\TMP/

# The bug is that the vertical scale/size (JZ6c) is not going to be transmitted, so we'll need to add it manually.

plot3d!(t, pen=:thinner, JZ="6c", show=true)

Now, to the real case. You have two options.

1- In a single file separate the edges with lines with 3 NaN
2- Make that file a multi-segment file, where the different segments are separated with one line with only the “>” character

Second form is preferable as when you import that file with gmtread("file") you get a Vector of GMTdatset segments with both individual and a global BoundingBox that helps to automatically set the vertical scales. And see also the mat2ds docs.

1 Like

Thanks for the help, adding JZ="6c" works great for now! Thank you also for the other 2 options, I will check them out.

On the other hand, I have 3 more questions if you could kindly help me with.

  1. Is it possible to have the 3D axes bound separately (as you would do with initializing a basemap first). This would be good because if I choose to give an alpha value to my scatterplot, the axes also become transparent!
  2. Is it possible to somehow give different alpha values to different points (by using a vector of values corresponding to different alphas). I think this is not possible, but still I ask
  3. How would one place the colorbar on the vertical, along the Z axis, in a 3D plot. Mine bounds to the 2D base of the plot.

See the code to replicate the behaviors listed (the transparency of the axes and the misplaced colorbar):

plot3d(df_filtered_cubes.cubeLongitude, df_filtered_cubes.cubeLatitude, -df_filtered_cubes.cubeDepth,
frame="SEnwZ1+b xafg yafg zafg",proj=:merc, marker=:cube, markerline=:faint, markersize=marker_size,
cmap=C_markers, zcolor=connectivity, 
alpha=70, view=(135,20))

colorbar!(pos=(outside=:BC, offset=(0,1.5)), shade=0.4, xaxis=(annot=:auto,), frame=(xlabel="Degree",),par=(MAP_LABEL_OFFSET=0.8,), savefig="test.png", show=true)

If I try to put a basemap, only the bottom plane gets pasted in and it also covers the plot!

I fixed that bug in master plus another where basemap was not parsing the vertical scale option, but since you are not using it because your are passing the condensed form frame="SEnwZ1+b xafg yafg zafg" that should work on basemap to. So you can avoid the undesired transparency on axes by calling basemap first (but then you must provide the limits too).

It must be possible to assign different transparencies to each cube. One way, and this is only from memory, it to create a color map (the CPT) such that the colors have transparencies too (need to dig the GMT CookBook on how to do that). The other is to create a multi-segment file, with one point per segment and assign it different colors and transparencies to each point. The fillalpha, is3D and multicol options of the mat2ds function should help with that.

The colorbar issue is another thing that I would like that we had automatically in GMT but we don’t (you are very welcome to open a feature request for that). The solution here is to use the more cumbersome manual positioning using paper coordinates with position(paper=true, anchor=(xx,yy), ...)

1 Like

Sorry to bother you, but I am extremely new to GMT, even to Julia to some extent.

I do not get how I should pass the Z axis on the basemap, I have tried using a combination of Julia and GMT but nothing works.

So you can avoid the undesired transparency on axes by calling basemap first (but then you must provide the limits too).

Mind you, even that frame=“…” took me a while to get, I do not seem figure out how to provide the limits, so that the zaxis shows properly!

Also the colorbar anchoring seems to not work,
I’ve tried

colorbar!(pos=(paper=(2.5,2.5),  size=(10,1.25), vertical=true),show=true)

and it still shows on the bottom plane.
But this is not that important to me as the controling of the axes is.

Sorry, busy day and it’s not over yet. I intend to make some changes that will not apply the transparency to the axes and so this problem will disappear. But meanwhile you can create a Z axis with the “za” flags like in the bellow example. I don’t like that the default does not save us from having to do this (another thing to fix) and the verbose answer (specifying each of the 3 axis separately) would make a bit long to my taste here.

basemap(limits=(-0.1,3.1,-0.1,3.1,0,1), zsize=6, frame="af za", view=(200,30), show=1)

Well, all of those aliases are explained in the documentation and used extensively in the examples.
It’s either frame, or B. Second is shorter but much less comprehensible for the non-initiated
in GMT.

I’ll try to come with a simple colorbar example later tonight (or tomorrow) and meanwhile, for the axes control, see the examples here (and the docs ofc). You’ll find that you can control everything you want.

1 Like

If you update to master the transparency will no longer applies to the axes, no need to resort to a previous basemap call (in fact doing this under the hood is what I implemented).

Regarding the colorbar!, the issue here is that when one one uses the ! version it inherits the fig limits, perspective, etc, from the previous call and we don’t want that in this case. So we must pass those explicitly. e.g.

t = [0 0 0; 1 1 1; 2 2 0; 3 3 1];
scatter3(t, t=50)
colorbar!(region=(0,21,0,29), pos=(paper=(18,2.0), size=(8,1)), cmap=:jet, view=(180,90), show=true)
1 Like

Thank you very much for your help!
Based on your suggestions and fixes I’ve been able to create the plots that I require, exactly how I wanted them.