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