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