26

Disclaimer: I already read questions like 2D surface on a 3D surface plot external data in a file and 3D surface plots in TikZ but these did not solve my problem.

I have this set of data:

x       y       z
2.17    0.001   0.82044815
2.17    0.002   0.82345825
2.17    0.004   0.82679255
2.17    0.008   0.83334715
2.17    0.016   0.84395915
2.17    0.032   0.8584953
2.21    0.001   0.77582165
2.21    0.003   0.78520505
2.21    0.009   0.80205985
2.21    0.027   0.83085105
2.24    0.001   0.7227885
2.24    0.002   0.73391615
2.24    0.005   0.7543979
2.24    0.015   0.78798745
2.24    0.003   0.74176635
2.24    0.009   0.77064805
2.24    0.027   0.81042375
2.26    0.001   0.66545585
2.26    0.003   0.7012046
2.26    0.005   0.721067
2.26    0.009   0.7447984
2.26    0.015   0.76715245
2.26    0.027   0.794177
2.27    0.001   0.62916195
2.27    0.003   0.6774642
2.27    0.009   0.72961785
2.27    0.027   0.7861086
2.28    0.001   0.5750828
2.28    0.003   0.65059675
2.28    0.005   0.6802631
2.28    0.009   0.7145367
2.28    0.015   0.74447695
2.28    0.027   0.7774403
2.29    0.001   0.51357255
2.29    0.002   0.581053
2.29    0.003   0.6173075
2.29    0.009   0.6972096
2.29    0.027   0.76793225
2.31    0.001   0.36997965
2.31    0.002   0.474415
2.31    0.003   0.53649295
2.31    0.009   0.6587164
2.31    0.016   0.70870255
2.31    0.027   0.7482423
2.31    0.05    0.7912395
2.34    0.001   0.2204104
2.34    0.002   0.316308
2.34    0.003   0.39256745
2.34    0.004   0.45240835
2.34    0.009   0.5883453
2.34    0.016   0.6590771
2.34    0.027   0.71444205
2.34    0.05    0.7690014
2.38    0.001   0.13286995
2.38    0.002   0.1828288
2.38    0.004   0.2980268
2.38    0.008   0.4507145
2.38    0.016   0.58417075
2.38    0.032   0.6833616

And if I plot it in Mathematica (with ListPlot3D[data]) it looks, without further tweaking and fiddling around, like:

enter image description here

But if I try this pgfplots code

\begin{tikzpicture}
  \begin{axis}
    \addplot3 [surf] table {data.txt};
  \end{axis}
\end{tikzpicture}

I only get this:

enter image description here

What do I have to do to get a surface like in Mathematica (color, smoothness, style, etc. does not matter, just a closed surface)?

5
  • 2
    The data need to be arranged on a grid, so the number of rows and columns must not change. How did you generate that dataset?
    – Jake
    Commented Jun 7, 2013 at 18:16
  • 3
    So if the picture produced by Mathematica is acceptable why don't you save it as .epsfile and include in the paper? Why do you need to use pgf? Jake's question is very good:"How do you generate that dataset". If you generate with Python for example just use matplotlib to plot the set. Commented Jun 7, 2013 at 19:32
  • @Jake It's experimental data that was given to me. I did not generate it. The first two columns are varied parameters and the last column is the measured value in a physical experiment.
    – Foo Bar
    Commented Jun 8, 2013 at 8:24
  • 6
    @PredragPunosevac I don't need to use pgfplots, but I'd like to. ;) Be it for learning or for fun or just because it generally looks better. ;)
    – Foo Bar
    Commented Jun 8, 2013 at 9:00
  • Alternatively you can plot the surface in Mathematica and use the axis from pgfplots. This is nicely described in the manual. If you want to convert the data to a regular grid Originlab has several options to do that as well.
    – Alexander
    Commented Jun 8, 2013 at 16:51

3 Answers 3

27

If you have octave installed, you can triangulate the data from within your LaTeX document using the \addplot shell functionality and plot it using the patch plot style.

\documentclass[border= 5mm]{standalone}
\usepackage{pgfplots}

\begin{document}
    \begin{tikzpicture}
    \begin{axis}
        \addplot3 [patch, patch table={triangles.txt}
   ] shell {echo "data=dlmread('data.txt');
    tri=delaunay(data(:,1), data(:,2));
    dlmwrite('triangles.txt',tri-1,' ');
    disp(data)" | octave --silent};
    \end{axis}
    \end{tikzpicture}
\end{document}

You can also grid the data:

\documentclass[border= 5mm]{standalone}
\usepackage{pgfplots}

\begin{document}
    \begin{tikzpicture}
    \begin{axis}
    \addplot3 [surf, mesh/cols=50, z buffer=sort, restrict z to domain=0:inf, shader=faceted interp] shell {
    echo "
        data=dlmread('data.txt');
        res=\pgfkeysvalueof{/pgfplots/mesh/cols};
        [xx,yy] = meshgrid(linspace(min(data(:,1)), max(data(:,1)),res), linspace(min(data(:,2)), max(data(:,2)),res));
        zz=griddata(data(:,1),data(:,2),data(:,3),xx,yy);
        zz(isnan(zz))=-999;
        disp([xx(:) yy(:) zz(:)])
    " | octave --silent};   
    \addplot3 [only marks] file {data.txt};
    \end{axis}
    \end{tikzpicture}
\end{document}

Gnuplot could be used to interpolate scattered data, but the available interpolation functions don't work particularly well for this dataset.

\documentclass[border= 5mm]{standalone}
\usepackage{pgfplots}

\begin{document}
    \begin{tikzpicture}
    \begin{axis}
    \addplot3 [surf] gnuplot [raw gnuplot] {
        set dgrid3d 30,30 spline;
        splot 'data.txt';       
    };
    \addplot3 [only marks] table {data.txt};
    \end{axis}
    \end{tikzpicture}
\end{document}
6
  • Something similar that works with MATLAB? Or it's just octave because it works from terminal? And with gnuplot? I don't have octave and I would prefer not to install it. Great answer, by the way.
    – Manuel
    Commented Jun 8, 2013 at 20:08
  • 1
    @Manuel: You can run the octave commands without adaptation in Matlab, but unfortunately it's really hard to run Matlab from a command line. I've added a gnuplot approach, but the octave solution really gives best results in this case.
    – Jake
    Commented Jun 8, 2013 at 20:52
  • Thank you for the gnuplot solution, may be not the best one, but it's great to know how to do it in that program.
    – Manuel
    Commented Jun 9, 2013 at 19:01
  • 1
    I did choose the gnuplot method. Thank you. :)
    – Foo Bar
    Commented Jun 24, 2013 at 15:53
  • 1
    @Jake Thank you for your great answer! I'm having issues, though. When I try the first example, only an empty 2D-chart is created. Second and third only compile the dots, not the actual surface. It says: ! Package pgfplots Error: Sorry, the shell-result file 'filename.pgf-plot.out' could not be found. Maybe you need to enable the shell-escape feature? or, respectively ! Package pgfplots Error: Sorry, the gnuplot-result file filename.pgf-plot.table could not be found. Maybe you need to enable the shell-escape feature?
    – lactea
    Commented Feb 13, 2017 at 19:16
13

enter image description here

Check out this kind of a closed surface, produced with the Asymptote. A brief description:

  • The data are stored in a file s.dat;
  • since the data are not based on a regular grid, a 2d triangulation is used on x,y points;
  • a surface is constructed from corresponding 3d triangles;
  • z-value and two colors, pzmin and pzmax are used to color it.

irrsurf.tex

\begin{filecontents*}{s.dat}
#x       y       z
2.17    0.001   0.82044815
2.17    0.002   0.82345825
2.17    0.004   0.82679255
2.17    0.008   0.83334715
2.17    0.016   0.84395915
2.17    0.032   0.8584953
2.21    0.001   0.77582165
2.21    0.003   0.78520505
2.21    0.009   0.80205985
2.21    0.027   0.83085105
2.24    0.001   0.7227885
2.24    0.002   0.73391615
2.24    0.005   0.7543979
2.24    0.015   0.78798745
2.24    0.003   0.74176635
2.24    0.009   0.77064805
2.24    0.027   0.81042375
2.26    0.001   0.66545585
2.26    0.003   0.7012046
2.26    0.005   0.721067
2.26    0.009   0.7447984
2.26    0.015   0.76715245
2.26    0.027   0.794177
2.27    0.001   0.62916195
2.27    0.003   0.6774642
2.27    0.009   0.72961785
2.27    0.027   0.7861086
2.28    0.001   0.5750828
2.28    0.003   0.65059675
2.28    0.005   0.6802631
2.28    0.009   0.7145367
2.28    0.015   0.74447695
2.28    0.027   0.7774403
2.29    0.001   0.51357255
2.29    0.002   0.581053
2.29    0.003   0.6173075
2.29    0.009   0.6972096
2.29    0.027   0.76793225
2.31    0.001   0.36997965
2.31    0.002   0.474415
2.31    0.003   0.53649295
2.31    0.009   0.6587164
2.31    0.016   0.70870255
2.31    0.027   0.7482423
2.31    0.05    0.7912395
2.34    0.001   0.2204104
2.34    0.002   0.316308
2.34    0.003   0.39256745
2.34    0.004   0.45240835
2.34    0.009   0.5883453
2.34    0.016   0.6590771
2.34    0.027   0.71444205
2.34    0.05    0.7690014
2.38    0.001   0.13286995
2.38    0.002   0.1828288
2.38    0.004   0.2980268
2.38    0.008   0.4507145
2.38    0.016   0.58417075
2.38    0.032   0.6833616
\end{filecontents*}
\documentclass[10pt,a4paper]{article}
\usepackage{lmodern}
\usepackage[inline]{asymptote}
\begin{document}
\begin{figure}
\begin{asy}
settings.outformat="pdf";
settings.prc=false;
settings.render=0;

import graph3;
size3(200,200,80,IgnoreAspect);
size(300,300,IgnoreAspect);

file fin=input("s.dat");
real[][] A=fin.dimension(0,3);
A=transpose(A);

real xmin=min(A[0]);
real xmax=max(A[0]);
real ymin=min(A[1]);
real ymax=max(A[1]);
real zmin=min(A[2]);
real zmax=max(A[2]);

currentprojection=orthographic(
camera=(18.8549512615229,-2.05615180783542,61.9196974622481),
up=Z,
target=0.5((xmin,ymin,zmin)+(xmax,ymax,zmax)),
zoom=0.7);

pair[] p=new pair[A[0].length]; // conversion to pairs x,y 
for(int i=0;i<p.length;++i){    //   to get a triangulation
  p[i]=(A[0][i],A[1][i]);       // 
}                               // 
int[][] trn=triangulate(p);     //

pen pzmin=rgb(0,1,0);           // pen for min z
pen pzmax=rgb(0.8,0.8,1);       // pen for max z

pen[] zpen(triple a,triple b,triple c){  // return z-pen for three vertices
  pen[]zp=new pen[3];
  real t;
    t=(a.z-zmin)/(zmax-zmin);  
      zp[0]=(1-t)*pzmin+t*pzmax+opacity(0.9);
    t=(b.z-zmin)/(zmax-zmin);  
      zp[1]=(1-t)*pzmin+t*pzmax+opacity(0.9);
    t=(c.z-zmin)/(zmax-zmin);  
      zp[2]=(1-t)*pzmin+t*pzmax+opacity(0.9);
  return zp;
}

triple a,b,c;
for(int i=0; i < trn.length; ++i) {
  a=(A[0][trn[i][0]],A[1][trn[i][0]],A[2][trn[i][0]]);
  b=(A[0][trn[i][1]],A[1][trn[i][1]],A[2][trn[i][1]]);
  c=(A[0][trn[i][2]],A[1][trn[i][2]],A[2][trn[i][2]]);
  draw(surface(a--b--c--cycle),zpen(a,b,c));        // draw i-th triangle
}

defaultpen(fontsize(10pt));

xaxis3(Label("$x$",align=-3Z),Bounds, xmin,xmax,InTicks(Step=0.05,step=0.01));
yaxis3(Label("$y$",align=-3Z),Bounds, ymin,ymax,InTicks(Step=0.01,step=0.002));
zaxis3(Label("$z$",align=-3Y),Bounds, zmin,zmax,InTicks());
\end{asy}
\caption{Surface constructed from an array of irregularly spaced points.}
\end{figure}
\end{document}

To process it with latexmk, create file latexmkrc:

sub asy {return system("asy '$_[0]'");}
add_cus_dep("asy","eps",0,"asy");
add_cus_dep("asy","pdf",0,"asy");
add_cus_dep("asy","tex",0,"asy");

and run latexmk -pdf irrsurf.tex.

8

pgfplots supports two input formats for closed surfaces: either a regular grid (i.e. matrix form with N rows and M cols) or free-form patches where you have to triangulate the surface and provide the patches either as adjacency matrix or one patch after the other.

You can find details about both formats in the manual sections for the surfplot handler or the patch plot handler.

It cannot triangular your data automatically. Consequently, you would need a 3rd party tool to convert it. This can be mathematica as in your screenshot. Judging from that screenshot, it seems that mathematica has interpolated it on a regular grid. If you export that grid in a CSV file, you can immediately read that file by means of pgfplots.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .