0

I have written this code which will generate a number of contour plots, each of which corresponds to a single text file. I have multiple text files. Currently, I am able to generate all of the images separately in png format without any issues. When I try to save the images as a pdf file, it is saving only the last image generated in a loop.I tried using the PdfPages package. This question is similar to the one that I posted before but with a different question. Similar

Issue: I want to able to generate all of the images into a single pdf file automatically from python. So for eg. if I have 100 text files, then I want to save all of the 100 images onto a single pdf file.Also ideally I want to save 2 images in a single page in the pdf file. There are some questions in SO about this, but I couldn't find an appropriate solution for my issue. Since I have many case for which I have to generate the images, I want to save them as a single pdf file as it is more easier to analyze them. I would appreciate any suggestions/advice to help me with this.

This is link for the sample text file Sample Text ges

from __future__ import print_function
import numpy as np
from matplotlib import pyplot as plt
from scipy.interpolate  import griddata
from matplotlib.backends.backend_pdf import PdfPages
path = 'location of the text files'
FT_init = 5.4311
delt = 0.15
TS_init = 140
dj_length = 2.4384

def streamfunction2d(y,x,Si_f,q):       
  with PdfPages('location of the generated pdf') as pdf:  
        Stf= plt.contour(x,y,Si_f,20)
        Stf1 = plt.colorbar(Stf)
        plt.clabel(Stf,fmt='%.0f',inline=True)
        plt.figtext(0.37,0.02,'Flowtime(s)',style= 'normal',alpha=1.0)
        plt.figtext(0.5,0.02,str(q[p]),style= 'normal',alpha=1.0)
        plt.title('Streamfunction_test1')
        plt.hold(True)       
        plt.tight_layout()
        pdf.savefig()

        path1 = 'location where the image is saved'
        image = path1+'test_'+'Stream1_'+str((timestep[p]))+'.png'
        plt.savefig(image)    
        plt.close() 

timestep = np.linspace(500,600,2)
flowtime = np.zeros(len(timestep))
timestep = np.array(np.round(timestep),dtype = 'int')
###############################################################################
for p in range(len(timestep)):   
        if timestep[p]<TS_init:
            flowtime[p] = 1.1111e-01
        else:
            flowtime[p] = (timestep[p]-TS_init)*delt+FT_init 
        q = np.array(flowtime)

        timestepstring=str(timestep[p]).zfill(4)
        fname = path+"ddn150AE-"+timestepstring+".txt"
        f = open(fname,'r')
        data = np.loadtxt(f,skiprows=1)
        data = data[data[:, 1].argsort()]
        data = data[np.logical_not(data[:,11]== 0)]                                                                
        Y  = data[:,2]  # Assigning Y to column 2 from the text file
        limit = np.nonzero(Y==dj_length)[0][0]
        Y  = Y[limit:]

        Vf  = data[:,11] 
        Vf  = Vf[limit:]
        Tr  = data[:,9]  
        Tr  = Tr[limit:]

        X  = data[:,1]  
        X  = X[limit:]
        Y  = data[:,2]  
        Y  = Y[limit:]
        U  = data[:,3]  
        U  = U[limit:]
        V  = data[:,4]  
        V  = V[limit:]
        St = data[:,5]  
        St  = St[limit:]
        ###########################################################################     

        ## Using griddata for interpolation from Unstructured to Structured data
        # resample onto a 300x300 grid        
        nx, ny = 300,300

        # (N, 2) arrays of input x,y coords and dependent values
        pts = np.vstack((X,Y )).T
        vals = np.vstack((Tr))
        vals1 = np.vstack((St))

        # The new x and y coordinates for the grid
        x = np.linspace(X.min(), X.max(), nx)
        y = np.linspace(Y.min(), Y.max(), ny)
        r = np.meshgrid(y,x)[::-1]

        # An (nx * ny, 2) array of x,y coordinates to interpolate at
        ipts = np.vstack(a.ravel() for a in r).T 

        Si = griddata(pts, vals1, ipts, method='linear')
        print(Ti.shape,"Ti_Shape")
        Si_f = np.reshape(Si,(len(y),len(x)))
        print(Si_f.shape,"Streamfunction Shape")        
        Si_f = np.transpose(Si_f)        
        streamfunction2d(y,x,Si_f,q) 
Community
  • 1
  • 1
narayan.p
  • 105
  • 2
  • 12

1 Answers1

2

Edit : As you mentioned matplotlib is probably able to handle everything by itself using PdfPages function. See this related answer. My original answer is a hack.

I think the error in your code is that you are creating another PdfPage object each time you go through the loop. My advice would be to add the PdfPage object as an argument to your streamfunction2d function and create the PdfPage object once and for all before the loop (using a with statement as in the documentation seems a good idea).

Example:

def streamfunction2d(y,x,Si_f,q,pdf):       
    # (...)
    pdf.savefig(plt.gcf()) 

with PdfPages('output.pdf') as pdf:
   for p in range(len(timestep)): 
      # (...)  
      streamfunction2d(y,x,Si_f,q,pdf)

Original answer: Here is a quick and dirty solution using the pdfunite software.

from matplotlib import pyplot as plt
import numpy as np
import subprocess
import os 

X = np.linspace(0,1,100)
for i in range(10):
    # random plot
    plt.plot(X,np.cos(i*X))

    # Save each figure as a pdf file.
    plt.savefig("page_{:0}.pdf".format(i))
    plt.clf()

# Calling pdfunite to merge all the pages
subprocess.call("pdfunite page_*.pdf united.pdf",shell=True)

# Removing temporary files
for i in range(10):
    os.remove("page_{:0}.pdf".format(i))

It uses two things:

  • You can save your figures as pdf using matplotlib's savefig command.
  • You can call other programs using the subprocess library. I used pdfunite to merge all the pages. Be sure it is available on your machine !

If you want to have several graph by page, you can use subplots.

Alternatively, you could use another python library (such as pyPDF) to merge the pages, but it would require slightly more code. Here is an (untested) example:

from matplotlib import pyplot as plt
import numpy as np
from pyPdf import PdfFileWriter, PdfFileReader

# create an empty pdf file
output = PdfFileWriter()

X = np.linspace(0,1,100)
for i in range(10):
    # random plot
    plt.plot(X,np.cos(i*X))

    # Save each figure as a pdf file.
    fi = "page_{:0}.pdf".format(i)
    plt.savefig(fi)
    plt.clf()

    # add it to the end of the output
    input = PdfFileReader(file(fi, "rb"))
    output.addPage(input.getPage(0))

# Save the resulting pdf file. 
outputStream = file("document-output.pdf", "wb")
output.write(outputStream)
Community
  • 1
  • 1
Geeklhem
  • 689
  • 7
  • 12
  • In my code, I added image1 = path1+'test_'+'Stream1_'+str((timestep[p]))+'.pdf' and then used plt.savefig(image1). Now it is saving multiple pdfs and the merge function is not working. Could you please add your solution to my code? Thank you – narayan.p Jan 20 '16 at 19:12
  • If you have multiple files you are on the right track: you only need to merge now. (You can clean up afterward by removing all the one page pdf using `os.remove` as I added in my example code). Which merging method did you use ? What is the error message ? (Do you either have `pdfunite` or the `pypdf` library installed ?) – Geeklhem Jan 21 '16 at 09:22
  • I added a possible solution that relies only on matplotib as you originally intended. – Geeklhem Jan 21 '16 at 09:46
  • Thank you Geeklhem. I now know how different techniques can be used to complete our task. I will try out your new solution. By the way, your pdf unite solution works, but when it merges the pdf, the files are not combined in order. If there is a way to do this, please let me know. I will accept your answer. Thank you very much. – narayan.p Jan 21 '16 at 16:39