rowtiffraster.py 9.64 KB
Newer Older
Hoek, Steven's avatar
Hoek, Steven committed
1
# Copyright (c) 2004-2020 WUR, Wageningen
Hoek, Steven's avatar
Hoek, Steven committed
2
from __future__ import division
3
4
5
from .const import constants as const
from .gridenvelope2d import GridEnvelope2D;
from .basetiffraster import BaseTiffRaster
Hoek, Steven's avatar
Hoek, Steven committed
6
7
8
9
from libtiff import TIFF
import numpy as np
import os

10
11
12
13
# TODO Python library pytiff may work better than libtiff. The problem is that it is difficult 
# if not impossible at this moment to install / run it on Windows. Let's hope this will be solved
# in the near future. If it is, then switch to pytiff.

14
15
16
__author__ = "Steven B. Hoek"

class RowTiffRaster(BaseTiffRaster, GridEnvelope2D):
Hoek, Steven's avatar
Hoek, Steven committed
17
18
19
20
21
22
23
24
25
    "A raster represented by 2 files, with extensions 'tif' and 'tfw'"
    "This class can ONLY deal with tiff files which have 1 row per strip"
    "Different layers - e.g. RGB - are as planes: contiguously = chunky = interleaved"
    "or separately = per channel. More info: http://www.fileformat.info/format/tiff/egff.htm"
    "It means that the number of planes and the planar configuration determines the shape"
    "of the array written as bitmapped data, with dimensions image_depth, image_height, "
    "image_width and samples. E.g. in the case of rgb and contiguous configuration, the last"
    "dimension of the array is expected to be 3 and the field samples per pixel will also be 3"
    # Private attributes
26
    _const = None
Hoek, Steven's avatar
Hoek, Steven committed
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
    __mode = 'r'
    __datatype = const.FLOAT;
    currow = -1;
    __envelope = None;
    __bits_per_sample = 8
    __sample_format = 1
    __samples_per_pixel = 1
    __numpy_type = np.uint8
    __itemsize = 1
    __layer_size = 1
    
    # Predefine __ReadStrip with a dummy function
    def dummy(self, *args): pass;
    __ReadStrip = dummy(0, 0, 1)
    __WriteStrip = dummy(0, 0, 1)

43
44
45
46
47
    def __init__(self, filepath='', *datatype):
        # Check input
        if filepath == '':
            print('File path cannot be an empty string (method __init__).')
            
48
49
50
51
52
        # Initialise
        super(RowTiffRaster, self).__init__(filepath)
        if self._const == None:
            raise AttributeError("TIFF raster not properly initialised!")
        
Hoek, Steven's avatar
Hoek, Steven committed
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
        # Finally set the datatype
        if len(datatype) > 0:
            if (datatype[0] == const.INTEGER): 
                self.__datatype = const.INTEGER;
            else: 
                self.__datatype = const.FLOAT; 
                
    def __get_sample_format(self, arr):
        result = None
        
        # Not considered: SAMPLEFORMAT_VOID=4 and SAMPLEFORMAT_COMPLEXINT=5
        if arr.dtype in np.sctypes['float']:
            result = 3 #SAMPLEFORMAT_IEEEFP
        elif arr.dtype in np.sctypes['uint']+[np.bool]:
            result = 1 #SAMPLEFORMAT_UINT
        elif arr.dtype in np.sctypes['int']:
            result = 2 #SAMPLEFORMAT_INT
        elif arr.dtype in np.sctypes['complex']:
            result = 6 #SAMPLEFORMAT_COMPLEXIEEEFP
        else:
73
            raise NotImplementedError(arr.dtype)
Hoek, Steven's avatar
Hoek, Steven committed
74
75
        return result
    
76
77
    def set_numpy_type(self, mytype):
        self.__numpy_type = mytype
Hoek, Steven's avatar
Hoek, Steven committed
78
79
80
81
    
    def get_numpy_type(self):
        return self.datafile.get_numpy_type(self.__bits_per_sample, self.__sample_format)
    
82
    def open(self, mode, ncols=1, nrows=1, xll=0, yll=0, cellsize=100, nodatavalue=-9999.0, byteorder='II', compression=1):     
Hoek, Steven's avatar
Hoek, Steven committed
83
84
85
86
87
88
        # Initialise
        super(RowTiffRaster, self).open(mode);
        
        # If file does not exist and mode[0] = 'w', create it!
        if (mode[0] == 'w'):
            self.__mode = mode
89
            self.datafile = TIFF.open(os.path.join(self.folder, self.name), mode='w');
Hoek, Steven's avatar
Hoek, Steven committed
90
            self.__envelope = GridEnvelope2D.__init__(self, ncols, nrows, xll, yll, cellsize, cellsize);
91
92
93
94
            self.datafile.SetField(self._const.IMAGE_WIDTH, ncols)
            self.datafile.SetField(self._const.IMAGE_LENGTH, nrows)
            self.datafile.SetField(self._const.BITS_PER_SAMPLE, self.__bits_per_sample)
            self.datafile.SetField(self._const.SAMPLE_PER_PIXEL, self.__samples_per_pixel)
Hoek, Steven's avatar
Hoek, Steven committed
95
            self.datafile.SetField("RowsPerStrip", 1)
96
97
98
99
100
            self.datafile.SetField(self._const.PLANAR_CONFIG, 1) # contiguous
            self.datafile.SetField(self._const.ORIENTATION, 1)  # top left
            self.datafile.SetField(self._const.PAGE_NUMBER, (0, 1))
            self.datafile.SetField(self._const.FILL_ORDER, 1) # MSB2LSB
            self.datafile.SetField(self._const.COMPRESSION, compression)
Hoek, Steven's avatar
Hoek, Steven committed
101
102
103
104
105
106
107
108
109
110
111
            super(RowTiffRaster, self).set_extra_tags()
            
            # Prepare to write per strip
            if compression == 1: # none
                self.__WriteStrip = self.datafile.WriteRawStrip
            else:
                self.__WriteStrip = self.datafile.WriteEncodedStrip
            self.writeheader()
            return True;
        else: 
            # Open the file as well as the header file
112
            if self.file_exists:            
Hoek, Steven's avatar
Hoek, Steven committed
113
114
115
116
                self.datafile = TIFF.open(os.path.join(self.folder, self.name), mode='r');
                self.readheader();
                
                # Check whether found values warrant further execution
117
118
119
120
121
122
123
                self.ncols = int(self.datafile.GetField(self._const.IMAGE_WIDTH))
                self.nrows = int(self.datafile.GetField(self._const.IMAGE_LENGTH))
                rows_per_strip = self.datafile.GetField("RowsPerStrip")
                if (rows_per_strip == None) :
                    msg = "Image file does not store data with 1 row per strip. This class is unable to handle this"
                    raise ValueError(msg) 
                if int(rows_per_strip) > 1:
Hoek, Steven's avatar
Hoek, Steven committed
124
125
126
127
128
                    msg = "Image file stores data with more than 1 row per strip. This class is unable to handle this"
                    raise ValueError(msg)
                
                # Process further information from the header file
                self.xll = self.xul;
129
                if self.ycoords_sort == const.DESC:
Hoek, Steven's avatar
Hoek, Steven committed
130
131
132
133
134
                    self.yll = self.yul - self.nrows * self.dy;
                else:
                    self.yll = self.yul + self.nrows * self.dy; 
                    
                # Prepare to read the file
135
136
137
                self.__bits_per_sample = self.datafile.GetField(self._const.BITS_PER_SAMPLE)
                self.__sample_format = self.datafile.GetField(self._const.SAMPLE_FORMAT)
                self.__samples_per_pixel = self.datafile.GetField(self._const.SAMPLE_PER_PIXEL)
Hoek, Steven's avatar
Hoek, Steven committed
138
139
140
                self.__numpy_type = self.datafile.get_numpy_type(self.__bits_per_sample, self.__sample_format)
                self.__itemsize = self.__bits_per_sample / 8
                self.__layer_size = self.ncols * self.__samples_per_pixel * self.__itemsize
141

142
                if self.datafile.GetField(self._const.GDAL_NODATA) != None:
143
                    if self.__datatype == const.INTEGER:
144
                        self.nodatavalue = int(self.datafile.GetField(self._const.GDAL_NODATA))                    
145
                    else:
146
                        self.nodatavalue = float(self.datafile.GetField(self._const.GDAL_NODATA))
Hoek, Steven's avatar
Hoek, Steven committed
147
                else:
148
149
                    if self.__datatype == const.INTEGER:
                        self.nodatavalue = int(self.nodatavalue)  
Hoek, Steven's avatar
Hoek, Steven committed
150
151
                super(RowTiffRaster, self).get_extra_tags()

152
                if self.datafile.GetField(self._const.COMPRESSION) == 1: # none
Hoek, Steven's avatar
Hoek, Steven committed
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
                    self.__ReadStrip = self.datafile.ReadRawStrip
                else:
                    self.__ReadStrip = self.datafile.ReadEncodedStrip  
                return True;
            else: return False;
    
    def next(self, parseLine=True):
        # Is it possible to proceed? Otherwise generate StopIteration
        self.currow += 1;
        if (self.currow > self.nrows): raise StopIteration;
        if (self.currow > int(self.datafile.NumberOfStrips())): raise StopIteration;
        
        # Read the next row
        buf = np.zeros((self.ncols, self.__samples_per_pixel), self.__numpy_type)
        self.__ReadStrip(self.currow, buf.ctypes.data, int(self.__layer_size))
        return buf      
    
    def writenext(self, sequence_with_data):
        # Write the next data if possible, otherwise generate StopIteration
        # We cannot know whether exactly 1 row is included or not.
        # Is it possible to proceed? Otherwise generate StopIteration
        if self.currow == -1:
            sample_format = self.__get_sample_format(sequence_with_data)
176
            self.datafile.SetField(self._const.SAMPLE_FORMAT, sample_format)
Hoek, Steven's avatar
Hoek, Steven committed
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
        self.currow += 1;
        if (self.currow > self.nrows): raise StopIteration;
        if (self.currow > int(self.datafile.NumberOfStrips())): raise StopIteration;
        
        # Ok, try to write the data
        try:
            #data_sequence = np.ascontiguousarray(sequence_with_data)
            size = self.ncols * self.__samples_per_pixel * sequence_with_data.itemsize
            if len(sequence_with_data.shape) == 1 or sequence_with_data.shape[1] == 1:
                self.__WriteStrip(self.currow, sequence_with_data.ctypes.data, int(size))
            else:
                size = size * sequence_with_data.shape[0]
                sequence_with_data = np.ascontiguousarray(sequence_with_data)
                self.__WriteStrip(self.currow, sequence_with_data.ctypes.data, int(size))
            
            return True
        except StopIteration:
            raise StopIteration
        except ValueError as e:
196
            print(str(e))
Hoek, Steven's avatar
Hoek, Steven committed
197
198
199
200
201
202
203
204
            raise ValueError
        except Exception as e:
            raise IOError(str(e)); 
        
    def reset(self):
        self.currow = -1;  
    
    def close(self):
205
206
207
208
209
210
211
        try:
            if self.__mode[0] == 'w':
                self.datafile.WriteDirectory()
        except Exception as e:
            print(e)
        finally:
            super(RowTiffRaster, self).close()
Hoek, Steven's avatar
Hoek, Steven committed
212