/[thuban]/branches/WIP-pyshapelib-bramz/Thuban/Model/postgisdb.py
ViewVC logotype

Annotation of /branches/WIP-pyshapelib-bramz/Thuban/Model/postgisdb.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2106 - (hide annotations)
Fri Mar 12 12:59:33 2004 UTC (20 years, 11 months ago) by bh
Original Path: trunk/thuban/Thuban/Model/postgisdb.py
File MIME type: text/x-python
File size: 25691 byte(s)
Support views in addition to normal tables in the postgis shapestore

* Thuban/Model/postgisdb.py
(PostGISShapeStore._fetch_table_information): Add a fallback for
the case where the table name is not in the geometry_columns
table.  This is usually the case for views.  Also, set
self.shapestore here.
(PostGISShapeStore.ShapeType): No need to query the database all
the time.  The shape type is now determined in
_fetch_table_information

* test/postgissupport.py (PostgreSQLServer.new_postgis_db)
(PostgreSQLServer.get_static_data_db, PostGISDatabase.__init__):
New parameter to specify views.
(PostGISDatabase.has_data): Also compare the views.  New views
parameter
(PostGISDatabase.initdb): Create the views.
(PostgreSQLServer.get_default_static_data_db): Add the v_landmarks
view

* test/test_postgis_db.py
(TestPostGISShapestorePointFromViews): New.  Test a
PostGISShapeStore with a view
(TestPostGISShapestorePointOIDAsGIDColumn.setUp): Pass the name of
the geometry_column explicitly to test whether that works

1 bh 2057 # Copyright (C) 2003, 2004 by Intevation GmbH
2 bh 1605 # Authors:
3     # Martin Mueller <[email protected]>
4     # Bernhard Herzog <[email protected]>
5     #
6     # This program is free software under the GPL (>=v2)
7     # Read the file COPYING coming with the software for details.
8    
9     """Basic interface to a PostGIS database"""
10    
11     from __future__ import generators
12    
13     try:
14     import psycopg
15     except ImportError:
16     psycopg = None
17    
18     import table
19     import wellknowntext
20    
21     from data import SHAPETYPE_POLYGON, SHAPETYPE_ARC, SHAPETYPE_POINT, RAW_WKT
22    
23 bh 1625 def has_postgis_support():
24     """Return whether this Thuban instance supports PostGIS connections
25 bh 1605
26 bh 1625 Having PostGIS support means that the psycopg module can be
27     imported.
28     """
29     return psycopg is not None
30    
31 bh 1631 def psycopg_version():
32     return psycopg.__version__
33    
34 bh 1605 if psycopg is not None:
35 bh 2102 # type_map maps psycopg type objects. It's a list of pairs since
36     # the psycopg type objects are unhashable.
37 bh 1605 type_map = [(psycopg.STRING, table.FIELDTYPE_STRING),
38     (psycopg.INTEGER, table.FIELDTYPE_INT),
39 bh 2102 (psycopg.ROWID, table.FIELDTYPE_INT),
40 bh 1605 (psycopg.FLOAT, table.FIELDTYPE_DOUBLE)]
41    
42 bh 2102 # _raw_type_map maps the postgresql type constants to Thuban type
43     # constants. This is very low level and postgresql specific and
44     # should be used only when necessary.
45     _raw_type_map = {}
46     def _fill_raw_type_map():
47     for psycopg_type, thuban_type in type_map:
48     for value in psycopg_type.values:
49     _raw_type_map[value] = thuban_type
50     _fill_raw_type_map()
51 bh 1636
52 bh 2102
53 bh 1946 def quote_identifier(ident):
54     """Return a quoted version of the identifier ident.
55    
56     The return value is a string that can be put directly into an SQL
57 bh 2057 statement. The quoted identifier is surrounded by double quotes and
58     any double quotes already in the input value are converted to two
59     double quotes. Examples:
60 bh 1946
61     >>> quote_identifier("abc\"def")
62     '"abc""def"'
63     >>> quote_identifier("abc def")
64     '"abc def"'
65     """
66     return '"' + '""'.join(ident.split('"')) + '"'
67    
68    
69 bh 1636 class ConnectionError(Exception):
70    
71     """Class for exceptions occurring when establishing a Databse connection"""
72    
73    
74 bh 1605 class PostGISConnection:
75    
76     """Represent a PostGIS database
77    
78     A PostGISConnection instance has the following public attributes:
79    
80     dbname -- The name of the database
81     host, port -- Host and port to connect to
82     user -- The user name to connect as.
83    
84     All of these attributes are strings and may be empty strings to
85     indicate default values.
86     """
87    
88     def __init__(self, dbname, host="", user="", password="", dbtype="",
89     port=""):
90     self.dbname = dbname
91     self.host = host
92     self.port = port
93     self.user = user
94     self.password = password
95     self.dbtype = dbtype
96 bh 1620 self.connect()
97    
98     def connect(self):
99     """Internal: Establish the database connection"""
100 bh 1605 params = []
101     for name in ("host", "port", "dbname", "user", "password"):
102     val = getattr(self, name)
103     if val:
104     params.append("%s=%s" % (name, val))
105 bh 1636 try:
106     self.connection = psycopg.connect(" ".join(params))
107     except psycopg.OperationalError, val:
108     raise ConnectionError(str(val))
109 bh 1605
110     # determine the OID for the geometry type. This is PostGIS
111     # specific.
112     cursor = self.connection.cursor()
113     cursor.execute("SELECT OID, typname FROM pg_type WHERE"
114     +" typname = 'geometry'")
115     row = cursor.fetchone()
116     self.connection.commit()
117     if row is not None:
118     self.geometry_type = row[0]
119     else:
120     raise ValueError("Can't determine postgres type of geometries")
121    
122 bh 1620 def BriefDescription(self):
123     """Return a brief, one-line description of the connection
124    
125     The return value is suitable for a list box of all database
126     connections.
127     """
128     return ("postgis://%(user)s@%(host)s:%(port)s/%(dbname)s"
129     % self.__dict__)
130    
131 bh 1955 def MatchesParameters(self, parameters):
132     """Return whether the connection matches the dictionary of parameters
133    
134     Return whether instatiating the connection with the given
135     parameters would establish essentially the same connection as
136     self. The connection is essentially the same if the same
137     database (identified by host, port and databasename) is accessed
138     as the same user.
139     """
140     return (parameters["host"] == self.host
141     and parameters["port"] == self.port
142     and parameters["dbname"] == self.dbname
143     and parameters["user"] == self.user)
144    
145 bh 1605 def Close(self):
146     """Close the database connection"""
147     self.connection.close()
148    
149     def GeometryTables(self):
150     """Return a list with the names of all tables with a geometry column"""
151 bh 2102
152     # The query is basically taken from the psql v. 7.2.1. When
153     # started with -E it prints the queries used for internal
154     # commands such as \d, which does mostly what we need here.
155 bh 1605 cursor = self.connection.cursor()
156 bh 2102 cursor.execute("SELECT c.relname FROM pg_class c"
157     " WHERE c.relkind IN ('r', 'v')"
158     # Omit the system tables
159     " AND c.relname !~ '^pg_'"
160     # Omit the special PostGIS tables
161     " AND c.relname NOT IN ('geometry_columns',"
162     " 'spatial_ref_sys')"
163     " AND %d in (SELECT a.atttypid FROM pg_attribute a"
164     " WHERE a.attrelid = c.oid)"
165     " ORDER BY c.relname;", (self.geometry_type,))
166 bh 1605 result = [row[0] for row in cursor.fetchall()]
167     self.connection.commit()
168     return result
169    
170 bh 2102 def table_columns(self, tablename):
171     """Experimental: return information about the columns of a table
172    
173     Return value is a list of (name, type) pairs where name is the
174     name of the column and type either one of the field type columns
175     or the string 'geometry' indicating a geometry column.
176    
177     The intended use of this method is for table selection dialogs
178     which need to determine which columns are usable as id or
179     geometry columns respectively. Suitable id columns will have
180     type FIELDTYPE_INT and geometry columns will have 'geometry'.
181     """
182     result = []
183     cursor = self.connection.cursor()
184    
185     # This query is taken basically from the \d command of psql
186     # 7.2.1
187     cursor.execute("SELECT a.attname, a.atttypid, a.attnum"
188     " FROM pg_class c, pg_attribute a"
189     " WHERE c.relname = %s AND a.attrelid = c.oid"
190     " ORDER BY a.attnum;", (tablename,))
191    
192     for row in cursor.fetchall():
193     col_name, col_type, col_attnum = row
194     col = None
195     if col_attnum < 1:
196     # It's a system column. Only the OID is interesting
197     # here
198     if col_name == "oid":
199     col = (col_name, _raw_type_map[col_type])
200     else:
201     # If it's an integer
202     thuban_type = _raw_type_map.get(col_type)
203     if thuban_type is not None:
204     col = (col_name, thuban_type)
205     elif row[1] == self.geometry_type:
206     col = (col_name, "geometry")
207     if col is not None:
208     result.append(col)
209    
210     return result
211    
212 bh 1605 def cursor(self):
213     """Return a DB API 2.0 cursor for the database"""
214     return self.connection.cursor()
215    
216    
217    
218     class PostGISColumn:
219    
220 bh 1946 """Column description for a PostGISTable
221 bh 1605
222 bh 1946 In addition to the normal column object attributes name, type and
223     index, PostGISColumn objects have a quoted_name attribute which
224     contains a quoted version of name for use in SQL statements. The
225     quoted_name attribute is mainly intended for internal use by the
226     PostGISTable class.
227     """
228    
229 bh 1605 def __init__(self, name, type, index):
230     self.name = name
231 bh 1946 self.quoted_name = quote_identifier(name)
232 bh 1605 self.type = type
233     self.index = index
234    
235    
236     class PostGISTable:
237    
238 bh 1693 """A Table in a PostGIS database
239 bh 1605
240 bh 1693 A PostgreSQL table may contain columns with types not (yet)
241     supported by Thuban. Instances of this class ignore those columns
242     and pretend they don't exist, i.e. they won't show up in the column
243     descriptions returned by Columns() and other methods.
244     """
245    
246 bh 2102 def __init__(self, db, tablename, id_column = None):
247 bh 1605 """Initialize the PostGISTable.
248    
249     The db parameter should be an instance of PostGISConnection and
250     tablename the name of a table in the database represented by db.
251 bh 2096
252     The id_column parameter should be the name of a column in the
253     table that can be used to identify rows. The column must have
254     the type integer and be unique and not null.
255 bh 2102
256     For backwards compatibility reasons, the id_column parameter is
257     optional. If not given the table must have a column called
258     'gid' which is used as the id_column. New code should always
259     provide this parameter.
260 bh 1605 """
261     self.db = db
262     self.tablename = tablename
263 bh 1946 # Tablename quoted for use in SQL statements.
264     self.quoted_tablename = quote_identifier(tablename)
265    
266 bh 2102 if not id_column:
267     id_column = "gid"
268 bh 2096 self.id_column = id_column
269     # id column name quoted for use in SQL statements.
270     self.quoted_id_column = quote_identifier(id_column)
271    
272 bh 1946 # Map column names and indices to column objects.
273 bh 1605 self.column_map = {}
274 bh 1946
275 bh 1605 self._fetch_table_information()
276    
277     def _fetch_table_information(self):
278     """Internal: Update information about the table"""
279     self.columns = []
280     cursor = self.db.cursor()
281 bh 1946 cursor.execute("SELECT * FROM %s WHERE 0=1" % self.quoted_tablename)
282 bh 1605 description = cursor.description
283    
284     for i in range(len(description)):
285 bh 2059 col = self._create_col_from_description(i, description[i])
286     if col is not None:
287     self.columns.append(col)
288 bh 1605
289     for col in self.columns:
290     self.column_map[col.name] = col
291     self.column_map[col.index] = col
292    
293     # Build query string for ReadRowAsDict
294     self.query_stmt = ("SELECT %s from %s"
295 bh 1946 % (", ".join([col.quoted_name
296     for col in self.columns]),
297     self.quoted_tablename))
298 bh 1605
299 bh 2059 def _create_col_from_description(self, index, description):
300     """Return the column object for the column described by description
301    
302     The parameter index is the index of the column. The description
303     is a sequence taken from the cursor's description attribute for
304     the column. That means description[0] is the name of the column
305     and description[1] the type.
306    
307     Return None if the column can't be represented for some reason,
308     e.g. because its type is not yet supported or needs to be
309     treated in some special way. Derived classes may extend this
310     method.
311     """
312     for pgtyp, tabletyp in type_map:
313     if pgtyp == description[1]:
314     return PostGISColumn(description[0], tabletyp,
315     len(self.columns))
316     return None
317    
318 bh 1638 def DBConnection(self):
319     """Return the dbconnection used by the table"""
320     return self.db
321    
322 bh 2102 def IDColumn(self):
323     """Return the column description object for the id column.
324    
325     If the oid column was used as the id column, the return value is
326     not one of the regular column objects that would be returned by
327     e.g. the Column() method, but it still has meaningful name
328     attribute.
329     """
330     if self.id_column == "oid":
331     return PostGISColumn(self.id_column, table.FIELDTYPE_INT, None)
332     return self.column_map[self.id_column]
333    
334 bh 1638 def TableName(self):
335     """Return the name of the table in the database"""
336     return self.tablename
337    
338 bh 1658 def Title(self):
339     """Return the title of the table.
340    
341     The title is currently fixed and equal to the tablename
342     """
343     return self.tablename
344    
345 bh 1605 def Dependencies(self):
346     """Return an empty tuple because a PostGISTable depends on nothing else
347     """
348     return ()
349    
350     def NumColumns(self):
351     return len(self.columns)
352    
353     def Columns(self):
354     return self.columns
355    
356     def Column(self, col):
357     return self.column_map[col]
358    
359     def HasColumn(self, col):
360     return self.column_map.has_key(col)
361    
362     def NumRows(self):
363     cursor = self.db.cursor()
364 bh 1946 cursor.execute("SELECT count(*) FROM %s" % self.quoted_tablename)
365 bh 1605 return cursor.fetchone()[0]
366    
367 bh 1662 def RowIdToOrdinal(self, gid):
368     """Return the row ordinal given its id"""
369 bh 1605 cursor = self.db.cursor()
370 bh 2096 cursor.execute("SELECT count(*) FROM %s WHERE %s < %d;"
371     % (self.quoted_tablename, self.quoted_id_column, gid))
372 bh 1662 return cursor.fetchone()[0]
373    
374     def RowOrdinalToId(self, num):
375     """Return the rowid for given its ordinal"""
376     cursor = self.db.cursor()
377 bh 2096 cursor.execute("SELECT %s FROM %s LIMIT 1 OFFSET %d;"
378     % (self.quoted_id_column, self.quoted_tablename, num))
379 bh 1662 return cursor.fetchone()[0]
380    
381     def ReadRowAsDict(self, row, row_is_ordinal = 0):
382     cursor = self.db.cursor()
383     if row_is_ordinal:
384     stmt = self.query_stmt + " LIMIT 1 OFFSET %d" % row
385     else:
386 bh 2096 stmt = self.query_stmt + " WHERE %s = %d" % (self.quoted_id_column,
387     row)
388 bh 1662 cursor.execute(stmt)
389 bh 1605 result = {}
390     for col, value in zip(self.columns, cursor.fetchone()):
391     result[col.name] = value
392     return result
393    
394 bh 1662 def ReadValue(self, row, col, row_is_ordinal = 0):
395 bh 1605 cursor = self.db.cursor()
396 bh 1662 if row_is_ordinal:
397     stmt = ("SELECT %s FROM %s LIMIT 1 OFFSET %d" %
398 bh 1946 (self.column_map[col].quoted_name, self.quoted_tablename,
399     row))
400 bh 1662 else:
401 bh 2096 stmt = ("SELECT %s FROM %s WHERE %s = %d" %
402 bh 1946 (self.column_map[col].quoted_name, self.quoted_tablename,
403 bh 2096 self.quoted_id_column, row))
404 bh 1662 cursor.execute(stmt)
405 bh 1605 return cursor.fetchone()[0]
406    
407     def ValueRange(self, col):
408     cursor = self.db.cursor()
409 bh 1946 name = self.column_map[col].quoted_name
410 bh 1605 cursor.execute("SELECT min(%s), max(%s) FROM %s" %
411 bh 1946 (name, name, self.quoted_tablename))
412 bh 1605 return tuple(cursor.fetchone())
413    
414     def UniqueValues(self, col):
415     cursor = self.db.cursor()
416 bh 1946 name = self.column_map[col].quoted_name
417 bh 1605 cursor.execute("SELECT %s FROM %s GROUP BY %s" %
418 bh 1946 (name, self.quoted_tablename, name))
419 bh 1605 return [row[0] for row in cursor.fetchall()]
420    
421     def SimpleQuery(self, left, comparison, right):
422     if comparison not in ("==", "!=", "<", "<=", ">=", ">"):
423     raise ValueError("Comparison operator %r not allowed" % comparison)
424    
425     if comparison == "==":
426     comparison = "="
427    
428     if isinstance(right, PostGISColumn):
429 bh 1946 right_template = right.quoted_name
430 bh 1605 params = ()
431     else:
432     right_template = "%s"
433     params = (right,)
434    
435 bh 2096 query = "SELECT %s FROM %s WHERE %s %s %s ORDER BY %s;" \
436     % (self.quoted_id_column, self.quoted_tablename,
437     left.quoted_name, comparison, right_template,
438     self.quoted_id_column)
439 bh 1605
440     cursor = self.db.cursor()
441     cursor.execute(query, params)
442     result = []
443     while 1:
444     row = cursor.fetchone()
445     if row is None:
446     break
447     result.append(row[0])
448     return result
449    
450    
451     class PostGISShape:
452    
453     def __init__(self, shapeid, data):
454     self.shapeid = shapeid
455     self.data = data
456    
457     def compute_bbox(self):
458     """
459     Return the bounding box of the shape as a tuple (minx,miny,maxx,maxy)
460     """
461     xs = []
462     ys = []
463     for part in self.Points():
464     for x, y in part:
465     xs.append(x)
466     ys.append(y)
467     return (min(xs), min(ys), max(xs), max(ys))
468    
469     def ShapeID(self):
470     return self.shapeid
471    
472     def Points(self):
473     return wellknowntext.parse_wkt_thuban(self.data)
474    
475     def RawData(self):
476     return self.data
477    
478    
479     shapetype_map = {"POLYGON": SHAPETYPE_POLYGON,
480 bh 1656 "MULTIPOLYGON": SHAPETYPE_POLYGON,
481 bh 1605 "MULTILINESTRING": SHAPETYPE_ARC,
482     "POINT": SHAPETYPE_POINT}
483    
484    
485     class PostGISShapeStore(PostGISTable):
486    
487     """Shapestore interface to a table in a PostGIS database"""
488    
489 bh 2096 def __init__(self, db, tablename, id_column = "gid",
490     geometry_column = None):
491 bh 2059 """Initialize the PostGISShapeStore.
492    
493     The db parameter should be an instance of PostGISConnection and
494     tablename the name of a table in the database represented by db.
495    
496 bh 2096 The id_column parameter should be the name of a column in the
497     table that can be used to identify rows. The column must have
498     the type integer and be unique and not null.
499    
500 bh 2059 The geometry_column paramter, if given, should be the name of
501     the geometry column to use. If the name given is not a geometry
502     column, raise a ValueError.
503    
504     If no geometry_column is given, the table must have exactly one
505     geometry column. If it has more than one and the
506     geometry_column is not given, a ValueError will be raised.
507     """
508     self.geometry_column = geometry_column
509     self.geometry_column_was_given = geometry_column is not None
510 bh 2096 PostGISTable.__init__(self, db, tablename, id_column)
511 bh 2059
512     # For convenience, we have a quoted version of the geometry
513     # column in self.quoted_geo_col
514     self.quoted_geo_col = quote_identifier(self.geometry_column)
515    
516 bh 2057 def _fetch_table_information(self):
517 bh 2106 """Extend inherited method to retrieve the SRID and shape type"""
518 bh 2057 PostGISTable._fetch_table_information(self)
519 bh 2106
520     # First, try to get it from the geometry_columns table.
521 bh 2057 cursor = self.db.cursor()
522 bh 2106 cursor.execute("SELECT srid, type FROM geometry_columns"
523 bh 2059 " WHERE f_table_name = %s AND f_geometry_column=%s",
524     (self.tablename, self.geometry_column))
525 bh 2106 row = cursor.fetchone()
526     if row is not None:
527     self.srid = row[0]
528     self.shape_type = shapetype_map.get(row[1])
529     return
530 bh 2057
531 bh 2106 # The table is probably really a view and thus not in
532     # geometry_columns. Use a different approach
533     cursor = self.db.cursor()
534     cursor.execute("SELECT DISTINCT SRID(%s) FROM %s;" %
535     (quote_identifier(self.geometry_column),
536     self.tablename))
537     row = cursor.fetchone()
538     if row is not None:
539     self.srid = row[0]
540     # Try to see whether there's another one
541     row = cursor.fetchone()
542     if row is not None:
543     # There are at least two different srids. We don't
544     # support that
545     self.srid = None
546    
547     cursor = self.db.cursor()
548     cursor.execute("SELECT DISTINCT GeometryType(%s) FROM %s;"
549     % (quote_identifier(self.geometry_column),
550     self.tablename))
551     row = cursor.fetchone()
552     if row is not None:
553     self.shape_type = shapetype_map.get(row[0])
554     # Try to see whether there's another one
555     row = cursor.fetchone()
556     if row is not None:
557     # There are at least two different srids. We don't
558     # support that
559     self.shape_type = None
560    
561 bh 2059 def _create_col_from_description(self, index, description):
562     """Extend the inherited method to find geometry columns
563    
564 bh 2096 If the column indicated by the parameters is a geometry column,
565 bh 2059 record its name in self.geometry_column and a quoted version in
566     self.quoted_geo_col. In any case return the return value of the
567     inherited method.
568     """
569     col = PostGISTable._create_col_from_description(self, index,
570     description)
571     col_name, col_type = description[:2]
572     if self.geometry_column_was_given:
573     if (col_name == self.geometry_column
574     and col_type != self.db.geometry_type):
575     raise TypeError("Column %s in %s is not a geometry column"
576     % (self.geometry_column, self.tablename))
577     else:
578     if col is None:
579     if description[1] == self.db.geometry_type:
580     # The column is a geometry column. If the name of
581     # the geometry column was not given to the
582     # constructor, and we encounter two geometry
583     # columns, raise a value error
584     if self.geometry_column is None:
585     self.geometry_column = description[0]
586     else:
587     raise TypeError("Table %s has two geometry columns"
588     " and no column name was given"
589     % (self.tablename,))
590     return col
591    
592 bh 1605 def Table(self):
593     """Return self since a PostGISShapeStore is its own table."""
594     return self
595    
596     def OrigShapeStore(self):
597     """Return None since the PostGISShapeStore is not derived from another
598     """
599     return None
600    
601 bh 2102 def GeometryColumn(self):
602     """Return the column description object for the geometry column
603    
604     There's currently no FIELDTYPE constant for this column, so the
605     return value is not a regular column object that could also be
606     returned from e.g. the Column() method. Only the name attribute
607     of the return value is meaningful at the moment.
608     """
609     return PostGISColumn(self.geometry_column, None, None)
610    
611 bh 1605 def ShapeType(self):
612     """Return the type of the shapes in the shapestore."""
613 bh 2106 return self.shape_type
614 bh 1605
615     def RawShapeFormat(self):
616     """Return the raw data format of the shape data.
617    
618     For the PostGISShapeStore this is RAW_WKT.
619     """
620     return RAW_WKT
621    
622     def NumShapes(self):
623     # The number of shapes is the same as the number of rows,
624     # assuming that the geometry can't be NULL.
625     return self.NumRows()
626    
627     def BoundingBox(self):
628     """Return the bounding box of all shapes in the postgis table"""
629     minx = miny = maxx = maxy = None
630     x=[]
631     y=[]
632     cursor = self.db.cursor()
633     try:
634 bh 1660 # Using the extent function is postgis specific. An OGC
635     # Simple Features compliant solution would be to use a query
636     # like "SELECT AsText(Envelope(the_geom)) FROM mytable;" and
637     # calculate the bounding box by hand from that
638     cursor.execute("SELECT extent(%s) FROM %s;"
639 bh 1946 % (self.quoted_geo_col, self.quoted_tablename))
640 bh 1605 result = cursor.fetchone()
641 bh 2057 if result[0]:
642 bh 1660 (minx, miny), (maxx, maxy) \
643     = wellknowntext.parse_wkt_thuban(result[0])[0]
644     return (minx, miny, maxx, maxy)
645 bh 1605 finally:
646     cursor.close()
647    
648     def Shape(self, shapeid):
649     cursor = self.db.cursor()
650 bh 2096 cursor.execute("SELECT AsText(%s) FROM %s WHERE %s=%d"
651     % (self.quoted_geo_col, self.quoted_tablename,
652     self.quoted_id_column, shapeid))
653 bh 1605 wkt = cursor.fetchone()[0]
654     cursor.close()
655     return PostGISShape(shapeid, wkt)
656    
657 bh 1658 def AllShapes(self):
658     cursor = self.db.cursor()
659 bh 2096 cursor.execute("SELECT %s, AsText(%s) FROM %s ORDER BY %s"
660     % (self.quoted_id_column, self.quoted_geo_col,
661     self.quoted_tablename, self.quoted_id_column))
662 bh 1658 while 1:
663     result = cursor.fetchone()
664     if result is None:
665     return
666     yield PostGISShape(result[0], result[1])
667    
668    
669 bh 1605 def ShapesInRegion(self, bbox):
670     """Generate all shapes overlapping the region given by bbox."""
671     # IMPORTANT:This will work for PostGIS < 0.8
672     left, bottom, right, top = bbox
673     geom = (("POLYGON((" + ", ".join(["%f %f"] * 5) + "))")
674     % (left, bottom, left, top, right, top, right, bottom,
675     left, bottom))
676     cursor = self.db.cursor()
677 bh 2096 cursor.execute("SELECT %(gid)s, AsText(%(geom)s) FROM %(table)s"
678     " WHERE %(geom)s && GeometryFromText('%(box)s', %(srid)d)"
679     " ORDER BY %(gid)s"
680     % {"table": self.quoted_tablename,
681     "geom": self.quoted_geo_col,
682     "gid": self.quoted_id_column,
683     "box": geom,
684     "srid": self.srid})
685 bh 1605 while 1:
686     result = cursor.fetchone()
687     if result is None:
688 bh 1658 return
689 bh 1605 yield PostGISShape(result[0], result[1])

Properties

Name Value
svn:eol-style native
svn:keywords Author Date Id Revision

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26