PostGIS  2.3.7dev-r@@SVN_REVISION@@
rtreader.py
Go to the documentation of this file.
1 #! /usr/bin/env python
2 #
3 #
4 # A simple driver to read RASTER field data directly from PostGIS/WKTRaster.
5 #
6 # Copyright (C) 2009 Mateusz Loskot <mateusz@loskot.net>
7 #
8 # This program is free software; you can redistribute it and/or
9 # modify it under the terms of the GNU General Public License
10 # as published by the Free Software Foundation; either version 2
11 # of the License, or (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the GNU General Public License
19 # along with this program; if not, write to the Free Software Foundation,
20 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 #
22 ###############################################################################
23 #
24 # Requirements: psycopg2
25 #
26 # WARNING: Tha main purpose of the RasterReader is to test and debug
27 # WKT Raster implementation. It is not supposed to be an efficient
28 # performance killer, by no means.
29 #
30 ###############################################################################
31 import psycopg2
32 import sys
33 
34 ###############################################################################
35 # RASTER driver (read-only)
36 
37 class RasterError(Exception):
38  def __init__(self, msg):
39  self.msg = msg
40  def __str__(self):
41  return self.msg
42 
43 class RasterReader(object):
44  """Reader of RASTER data stored in specified column and row (where) in a table"""
45 
46  # Constructors
47 
48  def __init__(self, connstr, table, column, where = ""):
49  self._connstr = connstr
50  self._conn = None
51  self._table = table
52  self._column = column
53  self._where = where
54  self._sizes = None
55  self._types = None
56  self._logging = False
57  # Connect and read RASTER header
58  self._setup()
59 
60  # Public properties
61 
62  logging = property(fset = lambda self, v: setattr(self, '_logging', v))
63  db = property(fget = lambda self: self._get_db())
64  table = property(fget = lambda self: self._table)
65  column = property(fget = lambda self: self._column)
66  width = property(fget = lambda self: self._get_width())
67  height = property(fget = lambda self: self._get_height())
68  num_bands = property(fget = lambda self: self._get_num_bands())
69  pixel_types = property(fget = lambda self: self._get_pixel_types())
70 
71  # Public methods
72 
73  def get_value(self, band, x, y):
74  return self._query_value(band, x, y)
75 
76  def copy_to(self, file, raster_format='TIFF', output_format='HEX', sep='\t'):
77  """
78  Proxy for SQL command COPY TO,
79  Converts selected rasters to specified raster_format with output sent either to
80  single hex-based plain text file or one or more binary files in raster_format,
81  one raster binary file per tuple from the raster table.
82  The BIN output uses HEX output as intermediate stage.
83  raster_format - TIFF|JPEG|PNG
84  output_format - HEX|BIN; BIN is a binary file in raster_format
85  sep - if output_format=HEX, separates rid value from hex-encoded binary.
86  """
87  import os.path
88  filehex = file # No extension added, may be user-defiened
89  with open(filehex, 'w') as f:
90  select = "SELECT rid, encode(ST_As%s(%s), 'hex') As rt FROM %s" % (raster_format, self._column, self._table)
91  if self._where is not None and len(self._where) > 0:
92  select += ' WHERE %s' % self._where
93  sql = "COPY (%s) TO STDOUT (DELIMITER '%s')" % (select, sep)
94  cur = self._conn.cursor()
95  cur.copy_expert(sql, f)
96 
97  if output_format == 'BIN':
98  import binascii
99  with open(filehex, 'r') as f:
100  dirname = os.path.dirname(file)
101  ext = raster_format.lower()
102  for line in f.readlines():
103  rid, raster = line.split()
104  filebin = self._table + '_' + self._column + '_' + rid + '.' + ext
105  filebin = os.path.join(dirname, filebin)
106  with open(filebin, 'w+') as fbin:
107  fbin.write(binascii.unhexlify(raster))
108 
109  # Private methods
110 
111  def _log(self, m):
112  if self._logging:
113  sys.stderr.write('[rtreader] ' + str(m) + '\n')
114 
115  def _get_db(self):
116  n = filter(lambda db: db[:6] == 'dbname', self._connstr.split())[0].split('=')[1]
117  return n.strip('\'').strip()
118 
119  def _get_width(self):
120  return self._query_raster_size(0)
121 
122  def _get_height(self):
123  return self._query_raster_size(1)
124 
125  def _get_num_bands(self):
126  return self._query_raster_size(2)
127 
128  def _get_pixel_types(self):
129  return self._query_pixel_types()
130 
131  def _setup(self):
132  self._connect()
133 
134  def _connect(self):
135  try:
136  if self._conn is None:
137  self._conn = psycopg2.connect(self._connstr)
138  except Exception as e:
139  raise RasterError("Falied to connect to %s: %s" % (self._connstr, e))
140 
141  def _query_single_row(self, sql):
142  assert self._conn is not None
143  #self._log(sql)
144 
145  try:
146  cur = self._conn.cursor()
147  cur.execute(sql)
148  except Exception as e:
149  raise RasterError("Failed to execute query %s: %s" % (sql, e))
150 
151  row = cur.fetchone()
152  if row is None:
153  raise RasterError("No tupes returned for query: %s" % sql)
154  return row
155 
156  def _query_value(self, band, x, y):
157  sql = 'SELECT st_value(%s, %d, %d, %d) FROM %s' % \
158  (self._column, band, x, y, self._table)
159  if len(self._where) > 0:
160  sql += ' WHERE %s' % self._where
161 
162  row = self._query_single_row(sql)
163  if row is None:
164  raise RasterError("Value of pixel %dx%d of band %d is none" %(x, y, band))
165  return row[0]
166 
167  def _query_raster_size(self, dim, force = False):
168  if self._sizes is None or force is True:
169  sql = 'SELECT st_width(%s), st_height(%s), st_numbands(%s) FROM %s' % \
170  (self._column, self._column, self._column, self._table)
171  if len(self._where) > 0:
172  sql += ' WHERE %s' % self._where
173 
174  self._log(sql)
175  self._sizes = self._query_single_row(sql)
176 
177  if self._sizes is None:
178  raise RasterError("Falied to query %dx%d of band %d is none" %(x, y, band))
179  return self._sizes[dim]
180 
181  def _query_pixel_types(self):
182 
183  types = []
184  sql = 'SELECT '
185  for i in range(0, self.num_bands):
186  if i != 0:
187  sql += ','
188  nband = i + 1
189  sql += ' st_bandpixeltype(%s, %d) ' % (self._column, nband)
190  sql += ' FROM ' + self._table
191  return self._query_single_row(sql)
def _log(self, m)
Definition: rtreader.py:111
def _get_num_bands(self)
Definition: rtreader.py:125
def _get_width(self)
Definition: rtreader.py:119
def _query_pixel_types(self)
Definition: rtreader.py:181
def _query_value(self, band, x, y)
Definition: rtreader.py:156
def _get_height(self)
Definition: rtreader.py:122
def _query_single_row(self, sql)
Definition: rtreader.py:141
def get_value(self, band, x, y)
Definition: rtreader.py:73
def _connect(self)
Definition: rtreader.py:134
RASTER driver (read-only)
Definition: rtreader.py:37
def __str__(self)
Definition: rtreader.py:40
def __init__(self, msg)
Definition: rtreader.py:38
def _get_pixel_types(self)
Definition: rtreader.py:128