/[thuban]/branches/WIP-pyshapelib-bramz/test/postgissupport.py
ViewVC logotype

Annotation of /branches/WIP-pyshapelib-bramz/test/postgissupport.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2096 - (hide annotations)
Thu Mar 11 13:50:53 2004 UTC (20 years, 11 months ago) by bh
Original Path: trunk/thuban/test/postgissupport.py
File MIME type: text/x-python
File size: 25586 byte(s)
* Thuban/Model/postgisdb.py (PostGISTable.__init__): New parameter
id_column to specify which column to use to identify rows.  Also
new instance variables id_column and quoted_id_column
(PostGISTable.RowIdToOrdinal, PostGISTable.RowOrdinalToId)
(PostGISTable.ReadRowAsDict, PostGISTable.ReadValue)
(PostGISTable.SimpleQuery): Use the id column name provided to the
constructor instead of "gid"
(PostGISShapeStore.__init__): New parameter id_column analogously
to PostGISTable.__init__.  This parameter is simply passed through
to the base class constructor
(PostGISShapeStore._create_col_from_description): Fix typo in
doc-string
(PostGISShapeStore.Shape, PostGISShapeStore.AllShapes)
(PostGISShapeStore.ShapesInRegion): Use the id column name
provided to the constructor instead of "gid"

* test/postgissupport.py
(PostgreSQLServer.get_default_static_data_db): New static table
landmarks_point_id with an id column != "gid.  Update the comments
a bit.
(skip_if_addgeometrycolumn_does_not_use_quote_ident): Fix typo in
doc-
(upload_shapefile): New parameter gid_column to use a name other
than "gid" for the column to store the shape ids

* test/test_postgis_db.py (TableTests): New.  Mixin-class
containing all tests previously in TestPostGISTable.  The actual
tests are the same but the code is a bit more configurable to
allow for different id columns etc.
(TestPostGISTable): Derive from TableTests now for the actual
tests.
(TestPostGISTableExplicitGIDColumn): New. Like TestPostGISTable
except that it the landmarks_point_id table to test the id_column
parameter
(PointTests): Extend the doc-string
(TestPostGISShapestorePointExplicitGIDColumn)
(TestPostGISShapestorePointOIDAsGIDColumn): New classes derived
from PointTests to test the explicit id_column parameter.  One
tests with the name of the column holding the shape ids, the other
uses PostgreSQL's OID column.  For the latter a number of methods
have to be overwritten to make them independent of the actual id
values.

1 bh 2057 # Copyright (C) 2003, 2004 by Intevation GmbH
2 bh 1605 # Authors:
3     # Bernhard Herzog <[email protected]>
4     #
5     # This program is free software under the GPL (>=v2)
6     # Read the file COPYING coming with the software for details.
7    
8     """Support module for tests that use a live PostGIS database"""
9    
10     __version__ = "$Revision$"
11     # $Source$
12     # $Id$
13    
14     import sys
15     import os
16     import time
17     import popen2
18     import shutil
19     import traceback
20    
21     import support
22    
23     try:
24     import psycopg
25     except ImportError:
26     psycopg = None
27    
28     #
29     # Helper code
30     #
31    
32     def run_config_script(cmdline):
33     """Run command cmdline and return its stdout or none in case of errors"""
34     pipe = os.popen(cmdline)
35     result = pipe.read()
36     if pipe.close() is not None:
37     raise RuntimeError('Command %r failed' % cmdline)
38     return result
39    
40     def run_command(command, outfilename = None):
41     """Run command as a subprocess and send its stdout and stderr to outfile
42    
43     The subprocess is run synchroneously so the function returns once
44     the subprocess has termninated. If the process' exit code is not
45     zero raise a RuntimeError.
46    
47     If outfilename is None stdout and stderr are still captured but they
48     are ignored and not written to any file.
49     """
50     proc = popen2.Popen4(command)
51     proc.tochild.close()
52     output = proc.fromchild.read()
53     status = proc.wait()
54     if outfilename is not None:
55     outfile = open(outfilename, "w")
56     outfile.write(output)
57     outfile.close()
58     if not os.WIFEXITED(status) or os.WEXITSTATUS(status) != 0:
59     if outfilename:
60     message = "see %s" % outfilename
61     else:
62     message = output
63     raise RuntimeError("command %r exited with code %d.\n%s"
64     % (command, status, message))
65    
66    
67     def run_boolean_command(command):
68     """
69     Run command as a subprocess silently and return whether it ran successfully
70    
71     Silently means that all output is captured and ignored. The exit
72     status is true if the command ran successfull, i.e. it terminated by
73     exiting and returned as zero exit code and false other wise
74     """
75     try:
76     run_command(command, None)
77     return 1
78     except RuntimeError:
79     pass
80     return 0
81    
82    
83     #
84     # PostgreSQL and database
85     #
86    
87     class PostgreSQLServer:
88    
89     """A PostgreSQL server
90    
91     Instances of this class represent a PostgreSQL server with postgis
92     extensions run explicitly for the test cases. Such a server has its
93     own database directory and its own directory for the unix sockets so
94     that it doesn't interfere with any other PostgreSQL server already
95     running on the system.
96     """
97    
98     def __init__(self, dbdir, port, postgis_sql, socket_dir):
99     """Initialize the PostgreSQLServer object
100    
101     Parameters:
102    
103     dbdir -- The directory for the databases
104     port -- The port to use
105     postgis_sql -- The name of the file with the SQL statements to
106     initialize a database for postgis.
107     socket_dir -- The directory for the socket files.
108    
109     When connecting to the database server use the port and host
110     instance variables.
111     """
112     self.dbdir = dbdir
113     self.port = port
114     self.postgis_sql = postgis_sql
115     self.socket_dir = socket_dir
116    
117     # For the client side the socket directory can be used as the
118 bh 1634 # host if the name starts with a slash.
119 bh 1605 self.host = os.path.abspath(socket_dir)
120    
121 bh 1634 # name and password for the admin and an unprivileged user
122     self.admin_name = "postgres"
123     self.admin_password = "postgres"
124     self.user_name = "observer"
125     self.user_password = "telescope"
126    
127 bh 1605 # Map db names to db objects
128     self.known_dbs = {}
129    
130     def createdb(self):
131     """Create the database in dbdir and start the server.
132    
133     First check whether the dbdir already exists and if necessary
134     stop an already running postmaster and remove the dbdir
135     directory completely. Then create a new database cluster in the
136     dbdir and start a postmaster.
137     """
138     if os.path.isdir(self.dbdir):
139     if self.is_running():
140     self.shutdown()
141     shutil.rmtree(self.dbdir)
142     os.mkdir(self.dbdir)
143    
144 bh 1634 run_command(["initdb", "-D", self.dbdir, "-U", self.admin_name],
145 bh 1605 os.path.join(self.dbdir, "initdb.log"))
146    
147     extra_opts = "-p %d" % self.port
148     if self.socket_dir is not None:
149     extra_opts += " -k %s" % self.socket_dir
150     run_command(["pg_ctl", "-D", self.dbdir,
151     "-l", os.path.join(self.dbdir, "logfile"),
152     "-o", extra_opts, "start"],
153     os.path.join(self.dbdir, "pg_ctl-start.log"))
154     # the -w option of pg_ctl doesn't work properly when the port is
155     # not the default port, so we have to implement waiting for the
156     # server ourselves
157     self.wait_for_postmaster()
158    
159 bh 1634 self.alter_user(self.admin_name, self.admin_password)
160     self.create_user(self.user_name, self.user_password)
161    
162 bh 1605 def wait_for_postmaster(self):
163     """Return when the database server is running
164    
165     Internal method to wait until the postmaster process has been
166     started and is ready for client connections.
167     """
168     max_count = 60
169     count = 0
170     while count < max_count:
171     try:
172     run_command(["psql", "-l", "-p", str(self.port),
173 bh 1634 "-h", self.host, "-U", self.admin_name],
174 bh 1605 os.path.join(self.dbdir, "psql-%d.log" % count))
175 bh 1634 except RuntimeError:
176     pass
177 bh 1605 except:
178 bh 1634 traceback.print_exc()
179 bh 1605 else:
180     break
181     time.sleep(0.5)
182     count += 1
183     else:
184     raise RuntimeError("postmaster didn't start")
185    
186     def is_running(self):
187     """Return true a postmaster process is running on self.dbdir
188    
189     This method runs pg_ctl status on the dbdir so even if the
190     object has just been created it is possible that this method
191     returns true if there's still a postmaster process running for
192     self.dbdir.
193     """
194     return run_boolean_command(["pg_ctl", "-D", self.dbdir, "status"])
195    
196     def shutdown(self):
197     """Stop the postmaster running for self.dbdir"""
198     run_command(["pg_ctl", "-m", "fast", "-D", self.dbdir, "stop"],
199     os.path.join(self.dbdir, "pg_ctl-stop.log"))
200    
201 bh 2057 def new_postgis_db(self, dbname, tables = None, reference_systems = None):
202 bh 1605 """Create and return a new PostGISDatabase object using self as server
203     """
204 bh 2057 db = PostGISDatabase(self, self.postgis_sql, dbname, tables = tables,
205     reference_systems = reference_systems)
206 bh 1605 db.initdb()
207     self.known_dbs[dbname] = db
208     return db
209    
210 bh 2057 def get_static_data_db(self, dbname, tables, reference_systems):
211 bh 1605 """Return a PostGISDatabase for a database with the given static data
212    
213     If no databasse of the name dbname exists, create a new one via
214     new_postgis_db and upload the data.
215    
216     If a database of the name dbname already exists and uses the
217     indicated data, return that. If the already existing db uses
218     different data raise a value error.
219    
220 bh 2057 If the database doesn't exist, create a new one via
221     self.new_postgis_db.
222    
223     The parameters tables and reference_systems have the same
224     meaning as for new_postgis_db.
225 bh 1605 """
226     db = self.known_dbs.get(dbname)
227     if db is not None:
228 bh 2057 if db.has_data(tables, reference_systems):
229 bh 1605 return db
230     raise ValueError("PostGISDatabase named %r doesn't have tables %r"
231     % (dbname, tables))
232 bh 2057 return self.new_postgis_db(dbname, tables, reference_systems)
233 bh 1605
234     def get_default_static_data_db(self):
235     dbname = "PostGISStaticTests"
236 bh 2057 srids = [(1, "proj=longlat datum=WGS84")]
237 bh 1662 tables = [
238     # Direct copies of the shapefiles. The shapeids are exactly
239 bh 2057 # the same, except where changed with "gid_offset", of
240 bh 2096 # course. Note that the test implementation requires that
241     # all the landmard tables use an gid_offset of 1000.
242 bh 1662 ("landmarks", os.path.join("..", "Data", "iceland",
243     "cultural_landmark-point.shp"),
244     [("gid_offset", 1000)]),
245     ("political", os.path.join("..", "Data", "iceland",
246 bh 1605 "political.shp")),
247 bh 1662 ("roads", os.path.join("..", "Data", "iceland",
248     "roads-line.shp")),
249 bh 1656
250 bh 1662 # The polygon data as a MULTIPOLYGON geometry type
251     ("political_multi", os.path.join("..", "Data", "iceland",
252 bh 1656 "political.shp"),
253 bh 1662 [("force_wkt_type", "MULTIPOLYGON")]),
254 bh 2057
255 bh 2096 # Copy of landmarks but using an srid != -1
256 bh 2057 ("landmarks_srid", os.path.join("..", "Data", "iceland",
257     "cultural_landmark-point.shp"),
258     [("gid_offset", 1000),
259     ("srid", 1)]),
260 bh 2096
261     # Copy of landmarks with a gid column called "point_id" instead
262     # of "gid" and using an srid != -1.
263     ("landmarks_point_id", os.path.join("..", "Data", "iceland",
264     "cultural_landmark-point.shp"),
265     [("gid_offset", 1000),
266     ("srid", 1),
267     ("gid_column", "point_id")]),
268 bh 1662 ]
269 bh 2057 return self.get_static_data_db(dbname, tables, srids)
270 bh 1605
271 bh 1634 def connection_params(self, user):
272     """Return the connection parameters for the given user
273 bh 1605
274 bh 1634 The return value is a dictionary suitable as keyword argument
275     list to PostGISConnection. The user parameter may be either
276     'admin' to connect as admin or 'user' to connect as an
277     unprivileged user.
278     """
279     return {"host": self.host, "port": self.port,
280     "user": getattr(self, user + "_name"),
281     "password": getattr(self, user + "_password")}
282 bh 1605
283 bh 1634 def connection_string(self, user):
284     """Return (part of) the connection string to pass to psycopg.connect
285 bh 1605
286 bh 1634 The string contains host, port, user and password. The user
287     parameter must be either 'admin' or 'user', as for
288     connection_params.
289     """
290     params = []
291     for key, value in self.connection_params(user).items():
292     # FIXME: this doesn't do quiting correctly but that
293     # shouldn't be much of a problem (people shouldn't be using
294     # single quotes in filenames anyway :) )
295     params.append("%s='%s'" % (key, value))
296     return " ".join(params)
297    
298     def execute_sql(self, dbname, user, sql):
299     """Execute the sql statament
300    
301     The user parameter us used as in connection_params. The dbname
302     parameter must be the name of a database in the cluster.
303     """
304     conn = psycopg.connect("dbname=%s " % dbname
305     + self.connection_string(user))
306     cursor = conn.cursor()
307     cursor.execute(sql)
308     conn.commit()
309     conn.close()
310    
311     def require_authentication(self, required):
312     """Switch authentication requirements on or off
313    
314     When started for the first time no passwords are required. Some
315     tests want to explicitly test whether Thuban's password
316     infrastructure works and switch password authentication on
317     explicitly. When switching it on, there should be a
318     corresponding call to switch it off again in the test case'
319     tearDown method or in a finally: block.
320     """
321     if required:
322     contents = "local all password\n"
323     else:
324     contents = "local all trust\n"
325     f = open(os.path.join(self.dbdir, "pg_hba.conf"), "w")
326     f.write(contents)
327     f.close()
328     run_command(["pg_ctl", "-D", self.dbdir, "reload"],
329     os.path.join(self.dbdir, "pg_ctl-reload.log"))
330    
331    
332     def create_user(self, username, password):
333     """Create user username with password in the database"""
334     self.execute_sql("template1", "admin",
335     "CREATE USER %s PASSWORD '%s';" % (username,password))
336    
337     def alter_user(self, username, password):
338     """Change the user username's password in the database"""
339     self.execute_sql("template1", "admin",
340     "ALTER USER %s PASSWORD '%s';" % (username,password))
341    
342    
343 bh 1605 class PostGISDatabase:
344    
345     """A PostGIS database in a PostgreSQLServer"""
346    
347 bh 2057 def __init__(self, server, postgis_sql, dbname, tables = None,
348     reference_systems = ()):
349 bh 1662 """Initialize the PostGISDatabase
350    
351     Parameters:
352    
353     server -- The PostgreSQLServer instance containing the
354     database
355    
356     postgis_sql -- Filename of the postgis.sql file with the
357     postgis initialization code
358    
359     dbname -- The name of the database
360    
361     tables -- Optional description of tables to create in the
362     new database. If given it should be a list of
363     (tablename, shapefilename) pairs meaning that a table
364     tablename will be created with the contents of the given
365     shapefile or (tablename, shapefilename, extraargs)
366     triples. The extraargs should be a list of key, value
367     pairs to use as keyword arguments to upload_shapefile.
368 bh 2057
369     reference_systems -- Optional description of spatial
370     reference systems. If given, it should be a sequence of
371     (srid, params) pairs where srid is the srid defined by
372     the proj4 paramter string params. The srid can be given
373     as an extra parameter in the tables list.
374 bh 1662 """
375 bh 1605 self.server = server
376     self.postgis_sql = postgis_sql
377     self.dbname = dbname
378     self.tables = tables
379 bh 2057 if reference_systems:
380     self.reference_systems = reference_systems
381     else:
382     # Make sure that it's a sequence we can iterate over even if
383     # the parameter's None
384     self.reference_systems = ()
385 bh 1605
386     def initdb(self):
387     """Remove the old db directory and create and initialize a new database
388     """
389     run_command(["createdb", "-p", str(self.server.port),
390 bh 1634 "-h", self.server.host, "-U", self.server.admin_name,
391     self.dbname],
392 bh 1605 os.path.join(self.server.dbdir, "createdb.log"))
393     run_command(["createlang", "-p", str(self.server.port),
394 bh 1634 "-h", self.server.host, "-U", self.server.admin_name,
395     "plpgsql", self.dbname],
396 bh 1605 os.path.join(self.server.dbdir, "createlang.log"))
397     # for some reason psql doesn't exit with an error code if the
398     # file given as -f doesn't exist, so we check manually by trying
399     # to open it before we run psql
400     f = open(self.postgis_sql)
401     f.close()
402     del f
403     run_command(["psql", "-f", self.postgis_sql, "-d", self.dbname,
404 bh 1634 "-p", str(self.server.port), "-h", self.server.host,
405     "-U", self.server.admin_name],
406 bh 1605 os.path.join(self.server.dbdir, "psql.log"))
407    
408 bh 1634 self.server.execute_sql(self.dbname, "admin",
409     "GRANT SELECT ON geometry_columns TO PUBLIC;")
410 bh 2057 self.server.execute_sql(self.dbname, "admin",
411     "GRANT SELECT ON spatial_ref_sys TO PUBLIC;")
412 bh 1634
413 bh 2057 for srid, params in self.reference_systems:
414     self.server.execute_sql(self.dbname, "admin",
415     "INSERT INTO spatial_ref_sys VALUES"
416     " (%d, '', %d, '', '%s');"
417     % (srid, srid, params))
418 bh 1605 if self.tables is not None:
419 bh 1662 def unpack(item):
420 bh 2057 extra = {"force_wkt_type": None, "gid_offset": 0,
421     "srid": -1}
422 bh 1656 if len(info) == 2:
423     tablename, shapefile = info
424     else:
425 bh 1662 tablename, shapefile, kw = info
426     for key, val in kw:
427     extra[key] = val
428     return tablename, shapefile, extra
429 bh 1605
430 bh 1662 for info in self.tables:
431     tablename, shapefile, kw = unpack(info)
432     upload_shapefile(shapefile, self, tablename, **kw)
433    
434 bh 2057 def has_data(self, tables, reference_systems):
435     return (self.tables == tables
436     and self.reference_systems == reference_systems)
437 bh 1605
438    
439     def find_postgis_sql():
440     """Return the name of the postgis_sql file
441    
442     A postgis installation usually has the postgis_sql file in
443     PostgreSQL's datadir (i.e. the directory where PostgreSQL keeps
444     static files, not the directory containing the databases).
445     Unfortunately there's no way to determine the name of this directory
446     with pg_config so we assume here that it's
447     $bindir/../share/postgresql/.
448     """
449     bindir = run_config_script("pg_config --bindir").strip()
450     return os.path.join(bindir, "..", "share", "postgresql",
451     "contrib", "postgis.sql")
452    
453     _postgres_server = None
454     def get_test_server():
455     """Return the test database server object.
456    
457     If it doesn't exist yet, create it first.
458    
459     The server will use the directory postgis under the temp dir (as
460     defined by support.create_temp_dir()) for the database cluster.
461     Sockets will be created in tempdir.
462     """
463     global _postgres_server
464     if _postgres_server is None:
465     tempdir = support.create_temp_dir()
466     dbdir = os.path.join(tempdir, "postgis")
467     socket_dir = tempdir
468    
469     _postgres_server = PostgreSQLServer(dbdir, 6543, find_postgis_sql(),
470     socket_dir = socket_dir)
471     _postgres_server.createdb()
472    
473     return _postgres_server
474    
475     def shutdown_test_server():
476     """Shutdown the test server if it is running"""
477     global _postgres_server
478     if _postgres_server is not None:
479     _postgres_server.shutdown()
480     _postgres_server = None
481    
482    
483     def reason_for_not_running_tests():
484     """
485     Determine whether postgis tests can be run and return a reason they can't
486    
487     There's no fool-proof way to reliably determine this short of
488     actually running the tests but we try the following here:
489    
490     - test whether pg_ctl --help can be run successfully
491     - test whether the postgis_sql can be opened
492     The name of the postgis_sql file is determined by find_postgis_sql()
493     - psycopg can be imported successfully.
494     """
495 bh 1679 # run_command currently uses Popen4 which is not available under
496     # Windows, for example.
497     if not hasattr(popen2, "Popen4"):
498     return "Can't run PostGIS test because popen2.Popen4 does not exist"
499    
500 bh 1605 try:
501     run_command(["pg_ctl", "--help"], None)
502     except RuntimeError:
503     return "Can't run PostGIS tests because pg_ctl fails"
504    
505     try:
506     postgis_sql = find_postgis_sql()
507     except:
508     return "Can't run PostGIS tests because postgis.sql can't be found"
509    
510     try:
511     f = open(postgis_sql)
512     f.close()
513     except:
514     return "Can't run PostGIS tests because postgis.sql can't be opened"
515    
516     # The test for psycopg was already done when this module was
517     # imported so we only have to check whether it was successful
518     if psycopg is None:
519     return "Can't run PostGIS tests because psycopg can't be imported"
520    
521     return ""
522    
523    
524     _cannot_run_postgis_tests = None
525     def skip_if_no_postgis():
526     global _cannot_run_postgis_tests
527     if _cannot_run_postgis_tests is None:
528     _cannot_run_postgis_tests = reason_for_not_running_tests()
529     if _cannot_run_postgis_tests:
530     raise support.SkipTest(_cannot_run_postgis_tests)
531    
532 bh 1947 def skip_if_addgeometrycolumn_does_not_use_quote_ident():
533     """Skip a test if the AddGeometryColumn function doesn't use quote_ident
534    
535     If the AddGeometryColumn function doesn't use quote_ident it doesn't
536     support unusual table or column names properly, that is, it will
537     fail with errors for names that contain spaces or double quotes.
538    
539     The test performed by this function is a bit simplistic because it
540     only tests whether the string 'quote_ident' occurs anywhere in the
541 bh 2096 postgis.sql file. This will hopefully work because when this was
542 bh 1947 fixed in postgis CVS AddGeometryColumn was the first function to use
543     quote_ident.
544     """
545     f = file(find_postgis_sql())
546     content = f.read()
547     f.close()
548     if content.find("quote_ident") < 0:
549     raise support.SkipTest("AddGeometryColumn doesn't use quote_ident")
550    
551 bh 1656 def coords_to_point(coords):
552 bh 1605 """Return string with a WKT representation of the point in coords"""
553     x, y = coords[0]
554     return "POINT(%r %r)" % (x, y)
555    
556 bh 1656 def coords_to_polygon(coords):
557 bh 1605 """Return string with a WKT representation of the polygon in coords"""
558     poly = []
559     for ring in coords:
560     poly.append(", ".join(["%r %r" % p for p in ring]))
561     return "POLYGON((%s))" % "), (".join(poly)
562    
563 bh 1656 def coords_to_multilinestring(coords):
564 bh 1605 """Return string with a WKT representation of the arc in coords"""
565     poly = []
566     for ring in coords:
567     poly.append(", ".join(["%r %r" % p for p in ring]))
568     return "MULTILINESTRING((%s))" % "), (".join(poly)
569    
570 bh 1656 def coords_to_multipolygon(coords):
571     """Return string with a WKT representation of the polygon in coords"""
572     poly = []
573     for ring in coords:
574     poly.append(", ".join(["%r %r" % p for p in ring]))
575     return "MULTIPOLYGON(((%s)))" % ")), ((".join(poly)
576    
577     wkt_converter = {
578     "POINT": coords_to_point,
579     "MULTILINESTRING": coords_to_multilinestring,
580     "POLYGON": coords_to_polygon,
581     "MULTIPOLYGON": coords_to_multipolygon,
582     }
583    
584 bh 1662 def upload_shapefile(filename, db, tablename, force_wkt_type = None,
585 bh 2096 gid_offset = 0, gid_column = "gid", srid = -1):
586 bh 1662 """Upload a shapefile into a new database table
587    
588     Parameters:
589    
590     filename -- The name of the shapefile
591    
592     db -- The PostGISDatabase instance representing the database
593    
594     tablename -- The name of the table to create and into which the data
595     is to be inserted
596    
597     force_wkt_type -- If given the real WKT geometry type to use instead
598     of the default that would be chosen based on the type of
599     the shapefile
600    
601     gid_offset -- A number to add to the shapeid to get the value for
602     the gid column (default 0)
603 bh 2057
604 bh 2096 gid_column -- The name of the column with the shape ids. Default
605     'gid'. If None, no gid column will be created. The
606     name is directly used in SQL statements, so if it
607     contains unusualy characters the caller should provide
608     a suitable quoted string.
609    
610 bh 2057 srid -- The srid of the spatial references system used by the table
611     and the data
612 bh 1662 """
613 bh 1605 import dbflib, shapelib
614    
615 bh 1656 # We build this map here because we need shapelib which can only be
616     # imported after support.initthuban has been called which we can't
617     # easily do in this module because it's imported by support.
618     shp_to_wkt = {
619     shapelib.SHPT_POINT: "POINT",
620     shapelib.SHPT_ARC: "MULTILINESTRING",
621     shapelib.SHPT_POLYGON: "POLYGON",
622     }
623    
624 bh 1605 server = db.server
625     dbname = db.dbname
626 bh 1634 conn = psycopg.connect("dbname=%s " % dbname
627     + db.server.connection_string("admin"))
628 bh 1605 cursor = conn.cursor()
629    
630     shp = shapelib.ShapeFile(filename)
631     dbf = dbflib.DBFFile(filename)
632     typemap = {dbflib.FTString: "VARCHAR",
633     dbflib.FTInteger: "INTEGER",
634     dbflib.FTDouble: "DOUBLE PRECISION"}
635    
636 bh 2096 insert_formats = []
637     if gid_column:
638     insert_formats.append("%(gid)s")
639    
640     fields = []
641     fields_decl = []
642     if gid_column:
643     fields.append(gid_column)
644     fields_decl.append("%s INT" % gid_column)
645 bh 1605 for i in range(dbf.field_count()):
646     ftype, name, width, prec = dbf.field_info(i)
647 bh 2096 fields.append(name)
648     fields_decl.append("%s %s" % (name, typemap[ftype]))
649 bh 1605 insert_formats.append("%%(%s)s" % name)
650     stmt = "CREATE TABLE %s (\n %s\n);" % (tablename,
651 bh 2096 ",\n ".join(fields_decl))
652 bh 1605 cursor.execute(stmt)
653     #print stmt
654    
655     numshapes, shapetype, mins, maxs = shp.info()
656 bh 1656 wkttype = shp_to_wkt[shapetype]
657     if force_wkt_type:
658     wkttype = force_wkt_type
659     convert = wkt_converter[wkttype]
660 bh 1605
661     cursor.execute("select AddGeometryColumn('%(dbname)s',"
662 bh 2057 "'%(tablename)s', 'the_geom', %(srid)d, '%(wkttype)s', 2);"
663 bh 1605 % locals())
664 bh 2096 fields.append("the_geom")
665 bh 2057 insert_formats.append("GeometryFromText(%(the_geom)s, %(srid)d)")
666 bh 1605
667 bh 2096 insert = ("INSERT INTO %s (%s) VALUES (%s)"
668     % (tablename, ", ".join(fields), ", ".join(insert_formats)))
669 bh 1605
670     for i in range(numshapes):
671     data = dbf.read_record(i)
672     data["tablename"] = tablename
673 bh 2096 if gid_column:
674     data["gid"] = i + gid_offset
675 bh 2057 data["srid"] = srid
676 bh 1605 data["the_geom"] = convert(shp.read_object(i).vertices())
677     #print insert % data
678     cursor.execute(insert, data)
679    
680 bh 1634 cursor.execute("GRANT SELECT ON %s TO PUBLIC;" % tablename)
681    
682 bh 1605 conn.commit()

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26