/[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 2106 - (hide annotations)
Fri Mar 12 12:59:33 2004 UTC (20 years, 11 months ago) by bh
Original Path: trunk/thuban/test/postgissupport.py
File MIME type: text/x-python
File size: 26741 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     # 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 2106 def new_postgis_db(self, dbname, tables = None, reference_systems = None,
202     views = None):
203 bh 1605 """Create and return a new PostGISDatabase object using self as server
204     """
205 bh 2057 db = PostGISDatabase(self, self.postgis_sql, dbname, tables = tables,
206 bh 2106 reference_systems = reference_systems,
207     views = views)
208 bh 1605 db.initdb()
209     self.known_dbs[dbname] = db
210     return db
211    
212 bh 2106 def get_static_data_db(self, dbname, tables, reference_systems, views):
213 bh 1605 """Return a PostGISDatabase for a database with the given static data
214    
215     If no databasse of the name dbname exists, create a new one via
216     new_postgis_db and upload the data.
217    
218     If a database of the name dbname already exists and uses the
219     indicated data, return that. If the already existing db uses
220     different data raise a value error.
221    
222 bh 2057 If the database doesn't exist, create a new one via
223     self.new_postgis_db.
224    
225     The parameters tables and reference_systems have the same
226     meaning as for new_postgis_db.
227 bh 1605 """
228     db = self.known_dbs.get(dbname)
229     if db is not None:
230 bh 2106 if db.has_data(tables, reference_systems, views):
231 bh 1605 return db
232     raise ValueError("PostGISDatabase named %r doesn't have tables %r"
233     % (dbname, tables))
234 bh 2106 return self.new_postgis_db(dbname, tables, reference_systems, views)
235 bh 1605
236     def get_default_static_data_db(self):
237     dbname = "PostGISStaticTests"
238 bh 2057 srids = [(1, "proj=longlat datum=WGS84")]
239 bh 1662 tables = [
240     # Direct copies of the shapefiles. The shapeids are exactly
241 bh 2057 # the same, except where changed with "gid_offset", of
242 bh 2096 # course. Note that the test implementation requires that
243     # all the landmard tables use an gid_offset of 1000.
244 bh 1662 ("landmarks", os.path.join("..", "Data", "iceland",
245     "cultural_landmark-point.shp"),
246     [("gid_offset", 1000)]),
247     ("political", os.path.join("..", "Data", "iceland",
248 bh 1605 "political.shp")),
249 bh 1662 ("roads", os.path.join("..", "Data", "iceland",
250     "roads-line.shp")),
251 bh 1656
252 bh 1662 # The polygon data as a MULTIPOLYGON geometry type
253     ("political_multi", os.path.join("..", "Data", "iceland",
254 bh 1656 "political.shp"),
255 bh 1662 [("force_wkt_type", "MULTIPOLYGON")]),
256 bh 2057
257 bh 2096 # Copy of landmarks but using an srid != -1
258 bh 2057 ("landmarks_srid", os.path.join("..", "Data", "iceland",
259     "cultural_landmark-point.shp"),
260     [("gid_offset", 1000),
261     ("srid", 1)]),
262 bh 2096
263     # Copy of landmarks with a gid column called "point_id" instead
264     # of "gid" and using an srid != -1.
265     ("landmarks_point_id", os.path.join("..", "Data", "iceland",
266     "cultural_landmark-point.shp"),
267     [("gid_offset", 1000),
268     ("srid", 1),
269     ("gid_column", "point_id")]),
270 bh 1662 ]
271 bh 2106 views = [("v_landmarks", "SELECT * FROM landmarks_point_id")]
272     return self.get_static_data_db(dbname, tables, srids, views)
273 bh 1605
274 bh 1634 def connection_params(self, user):
275     """Return the connection parameters for the given user
276 bh 1605
277 bh 1634 The return value is a dictionary suitable as keyword argument
278     list to PostGISConnection. The user parameter may be either
279     'admin' to connect as admin or 'user' to connect as an
280     unprivileged user.
281     """
282     return {"host": self.host, "port": self.port,
283     "user": getattr(self, user + "_name"),
284     "password": getattr(self, user + "_password")}
285 bh 1605
286 bh 1634 def connection_string(self, user):
287     """Return (part of) the connection string to pass to psycopg.connect
288 bh 1605
289 bh 1634 The string contains host, port, user and password. The user
290     parameter must be either 'admin' or 'user', as for
291     connection_params.
292     """
293     params = []
294     for key, value in self.connection_params(user).items():
295     # FIXME: this doesn't do quiting correctly but that
296     # shouldn't be much of a problem (people shouldn't be using
297     # single quotes in filenames anyway :) )
298     params.append("%s='%s'" % (key, value))
299     return " ".join(params)
300    
301     def execute_sql(self, dbname, user, sql):
302     """Execute the sql statament
303    
304     The user parameter us used as in connection_params. The dbname
305     parameter must be the name of a database in the cluster.
306     """
307     conn = psycopg.connect("dbname=%s " % dbname
308     + self.connection_string(user))
309     cursor = conn.cursor()
310     cursor.execute(sql)
311     conn.commit()
312     conn.close()
313    
314     def require_authentication(self, required):
315     """Switch authentication requirements on or off
316    
317     When started for the first time no passwords are required. Some
318     tests want to explicitly test whether Thuban's password
319     infrastructure works and switch password authentication on
320     explicitly. When switching it on, there should be a
321     corresponding call to switch it off again in the test case'
322     tearDown method or in a finally: block.
323     """
324     if required:
325     contents = "local all password\n"
326     else:
327     contents = "local all trust\n"
328     f = open(os.path.join(self.dbdir, "pg_hba.conf"), "w")
329     f.write(contents)
330     f.close()
331     run_command(["pg_ctl", "-D", self.dbdir, "reload"],
332     os.path.join(self.dbdir, "pg_ctl-reload.log"))
333    
334    
335     def create_user(self, username, password):
336     """Create user username with password in the database"""
337     self.execute_sql("template1", "admin",
338     "CREATE USER %s PASSWORD '%s';" % (username,password))
339    
340     def alter_user(self, username, password):
341     """Change the user username's password in the database"""
342     self.execute_sql("template1", "admin",
343     "ALTER USER %s PASSWORD '%s';" % (username,password))
344    
345    
346 bh 1605 class PostGISDatabase:
347    
348     """A PostGIS database in a PostgreSQLServer"""
349    
350 bh 2057 def __init__(self, server, postgis_sql, dbname, tables = None,
351 bh 2106 reference_systems = (), views = None):
352 bh 1662 """Initialize the PostGISDatabase
353    
354     Parameters:
355    
356     server -- The PostgreSQLServer instance containing the
357     database
358    
359     postgis_sql -- Filename of the postgis.sql file with the
360     postgis initialization code
361    
362     dbname -- The name of the database
363    
364     tables -- Optional description of tables to create in the
365     new database. If given it should be a list of
366     (tablename, shapefilename) pairs meaning that a table
367     tablename will be created with the contents of the given
368     shapefile or (tablename, shapefilename, extraargs)
369     triples. The extraargs should be a list of key, value
370     pairs to use as keyword arguments to upload_shapefile.
371 bh 2057
372     reference_systems -- Optional description of spatial
373     reference systems. If given, it should be a sequence of
374     (srid, params) pairs where srid is the srid defined by
375     the proj4 paramter string params. The srid can be given
376     as an extra parameter in the tables list.
377 bh 2106
378     views -- Optional description of views. If given it should
379     be a list of (viewname, select_stmt) pairs where
380     viewname is the name of the view to be created and
381     select_stmt is the select statement to use as the basis.
382     The views will be created after the tables and may refer
383     to them in the select_stmt.
384 bh 1662 """
385 bh 1605 self.server = server
386     self.postgis_sql = postgis_sql
387     self.dbname = dbname
388     self.tables = tables
389 bh 2106 self.views = views
390 bh 2057 if reference_systems:
391     self.reference_systems = reference_systems
392     else:
393     # Make sure that it's a sequence we can iterate over even if
394     # the parameter's None
395     self.reference_systems = ()
396 bh 1605
397     def initdb(self):
398     """Remove the old db directory and create and initialize a new database
399     """
400     run_command(["createdb", "-p", str(self.server.port),
401 bh 1634 "-h", self.server.host, "-U", self.server.admin_name,
402     self.dbname],
403 bh 1605 os.path.join(self.server.dbdir, "createdb.log"))
404     run_command(["createlang", "-p", str(self.server.port),
405 bh 1634 "-h", self.server.host, "-U", self.server.admin_name,
406     "plpgsql", self.dbname],
407 bh 1605 os.path.join(self.server.dbdir, "createlang.log"))
408     # for some reason psql doesn't exit with an error code if the
409     # file given as -f doesn't exist, so we check manually by trying
410     # to open it before we run psql
411     f = open(self.postgis_sql)
412     f.close()
413     del f
414     run_command(["psql", "-f", self.postgis_sql, "-d", self.dbname,
415 bh 1634 "-p", str(self.server.port), "-h", self.server.host,
416     "-U", self.server.admin_name],
417 bh 1605 os.path.join(self.server.dbdir, "psql.log"))
418    
419 bh 1634 self.server.execute_sql(self.dbname, "admin",
420     "GRANT SELECT ON geometry_columns TO PUBLIC;")
421 bh 2057 self.server.execute_sql(self.dbname, "admin",
422     "GRANT SELECT ON spatial_ref_sys TO PUBLIC;")
423 bh 1634
424 bh 2057 for srid, params in self.reference_systems:
425     self.server.execute_sql(self.dbname, "admin",
426     "INSERT INTO spatial_ref_sys VALUES"
427     " (%d, '', %d, '', '%s');"
428     % (srid, srid, params))
429 bh 1605 if self.tables is not None:
430 bh 1662 def unpack(item):
431 bh 2057 extra = {"force_wkt_type": None, "gid_offset": 0,
432     "srid": -1}
433 bh 1656 if len(info) == 2:
434     tablename, shapefile = info
435     else:
436 bh 1662 tablename, shapefile, kw = info
437     for key, val in kw:
438     extra[key] = val
439     return tablename, shapefile, extra
440 bh 1605
441 bh 1662 for info in self.tables:
442     tablename, shapefile, kw = unpack(info)
443     upload_shapefile(shapefile, self, tablename, **kw)
444    
445 bh 2106 if self.views is not None:
446     for viewname, select_stmt in self.views:
447     self.server.execute_sql(self.dbname, "admin",
448     "CREATE VIEW %s AS %s" % (viewname,
449     select_stmt))
450     self.server.execute_sql(self.dbname, "admin",
451     "GRANT SELECT ON %s TO PUBLIC;"
452     % viewname)
453    
454     def has_data(self, tables, reference_systems, views):
455 bh 2057 return (self.tables == tables
456 bh 2106 and self.reference_systems == reference_systems
457     and self.views == views)
458 bh 1605
459    
460     def find_postgis_sql():
461     """Return the name of the postgis_sql file
462    
463     A postgis installation usually has the postgis_sql file in
464     PostgreSQL's datadir (i.e. the directory where PostgreSQL keeps
465     static files, not the directory containing the databases).
466     Unfortunately there's no way to determine the name of this directory
467     with pg_config so we assume here that it's
468     $bindir/../share/postgresql/.
469     """
470     bindir = run_config_script("pg_config --bindir").strip()
471     return os.path.join(bindir, "..", "share", "postgresql",
472     "contrib", "postgis.sql")
473    
474     _postgres_server = None
475     def get_test_server():
476     """Return the test database server object.
477    
478     If it doesn't exist yet, create it first.
479    
480     The server will use the directory postgis under the temp dir (as
481     defined by support.create_temp_dir()) for the database cluster.
482     Sockets will be created in tempdir.
483     """
484     global _postgres_server
485     if _postgres_server is None:
486     tempdir = support.create_temp_dir()
487     dbdir = os.path.join(tempdir, "postgis")
488     socket_dir = tempdir
489    
490     _postgres_server = PostgreSQLServer(dbdir, 6543, find_postgis_sql(),
491     socket_dir = socket_dir)
492     _postgres_server.createdb()
493    
494     return _postgres_server
495    
496     def shutdown_test_server():
497     """Shutdown the test server if it is running"""
498     global _postgres_server
499     if _postgres_server is not None:
500     _postgres_server.shutdown()
501     _postgres_server = None
502    
503    
504     def reason_for_not_running_tests():
505     """
506     Determine whether postgis tests can be run and return a reason they can't
507    
508     There's no fool-proof way to reliably determine this short of
509     actually running the tests but we try the following here:
510    
511     - test whether pg_ctl --help can be run successfully
512     - test whether the postgis_sql can be opened
513     The name of the postgis_sql file is determined by find_postgis_sql()
514     - psycopg can be imported successfully.
515     """
516 bh 1679 # run_command currently uses Popen4 which is not available under
517     # Windows, for example.
518     if not hasattr(popen2, "Popen4"):
519     return "Can't run PostGIS test because popen2.Popen4 does not exist"
520    
521 bh 1605 try:
522     run_command(["pg_ctl", "--help"], None)
523     except RuntimeError:
524     return "Can't run PostGIS tests because pg_ctl fails"
525    
526     try:
527     postgis_sql = find_postgis_sql()
528     except:
529     return "Can't run PostGIS tests because postgis.sql can't be found"
530    
531     try:
532     f = open(postgis_sql)
533     f.close()
534     except:
535     return "Can't run PostGIS tests because postgis.sql can't be opened"
536    
537     # The test for psycopg was already done when this module was
538     # imported so we only have to check whether it was successful
539     if psycopg is None:
540     return "Can't run PostGIS tests because psycopg can't be imported"
541    
542     return ""
543    
544    
545     _cannot_run_postgis_tests = None
546     def skip_if_no_postgis():
547     global _cannot_run_postgis_tests
548     if _cannot_run_postgis_tests is None:
549     _cannot_run_postgis_tests = reason_for_not_running_tests()
550     if _cannot_run_postgis_tests:
551     raise support.SkipTest(_cannot_run_postgis_tests)
552    
553 bh 1947 def skip_if_addgeometrycolumn_does_not_use_quote_ident():
554     """Skip a test if the AddGeometryColumn function doesn't use quote_ident
555    
556     If the AddGeometryColumn function doesn't use quote_ident it doesn't
557     support unusual table or column names properly, that is, it will
558     fail with errors for names that contain spaces or double quotes.
559    
560     The test performed by this function is a bit simplistic because it
561     only tests whether the string 'quote_ident' occurs anywhere in the
562 bh 2096 postgis.sql file. This will hopefully work because when this was
563 bh 1947 fixed in postgis CVS AddGeometryColumn was the first function to use
564     quote_ident.
565     """
566     f = file(find_postgis_sql())
567     content = f.read()
568     f.close()
569     if content.find("quote_ident") < 0:
570     raise support.SkipTest("AddGeometryColumn doesn't use quote_ident")
571    
572 bh 1656 def coords_to_point(coords):
573 bh 1605 """Return string with a WKT representation of the point in coords"""
574     x, y = coords[0]
575     return "POINT(%r %r)" % (x, y)
576    
577 bh 1656 def coords_to_polygon(coords):
578 bh 1605 """Return string with a WKT representation of the polygon in coords"""
579     poly = []
580     for ring in coords:
581     poly.append(", ".join(["%r %r" % p for p in ring]))
582     return "POLYGON((%s))" % "), (".join(poly)
583    
584 bh 1656 def coords_to_multilinestring(coords):
585 bh 1605 """Return string with a WKT representation of the arc in coords"""
586     poly = []
587     for ring in coords:
588     poly.append(", ".join(["%r %r" % p for p in ring]))
589     return "MULTILINESTRING((%s))" % "), (".join(poly)
590    
591 bh 1656 def coords_to_multipolygon(coords):
592     """Return string with a WKT representation of the polygon in coords"""
593     poly = []
594     for ring in coords:
595     poly.append(", ".join(["%r %r" % p for p in ring]))
596     return "MULTIPOLYGON(((%s)))" % ")), ((".join(poly)
597    
598     wkt_converter = {
599     "POINT": coords_to_point,
600     "MULTILINESTRING": coords_to_multilinestring,
601     "POLYGON": coords_to_polygon,
602     "MULTIPOLYGON": coords_to_multipolygon,
603     }
604    
605 bh 1662 def upload_shapefile(filename, db, tablename, force_wkt_type = None,
606 bh 2096 gid_offset = 0, gid_column = "gid", srid = -1):
607 bh 1662 """Upload a shapefile into a new database table
608    
609     Parameters:
610    
611     filename -- The name of the shapefile
612    
613     db -- The PostGISDatabase instance representing the database
614    
615     tablename -- The name of the table to create and into which the data
616     is to be inserted
617    
618     force_wkt_type -- If given the real WKT geometry type to use instead
619     of the default that would be chosen based on the type of
620     the shapefile
621    
622     gid_offset -- A number to add to the shapeid to get the value for
623     the gid column (default 0)
624 bh 2057
625 bh 2096 gid_column -- The name of the column with the shape ids. Default
626     'gid'. If None, no gid column will be created. The
627     name is directly used in SQL statements, so if it
628     contains unusualy characters the caller should provide
629     a suitable quoted string.
630    
631 bh 2057 srid -- The srid of the spatial references system used by the table
632     and the data
633 bh 1662 """
634 bh 1605 import dbflib, shapelib
635    
636 bh 1656 # We build this map here because we need shapelib which can only be
637     # imported after support.initthuban has been called which we can't
638     # easily do in this module because it's imported by support.
639     shp_to_wkt = {
640     shapelib.SHPT_POINT: "POINT",
641     shapelib.SHPT_ARC: "MULTILINESTRING",
642     shapelib.SHPT_POLYGON: "POLYGON",
643     }
644    
645 bh 1605 server = db.server
646     dbname = db.dbname
647 bh 1634 conn = psycopg.connect("dbname=%s " % dbname
648     + db.server.connection_string("admin"))
649 bh 1605 cursor = conn.cursor()
650    
651     shp = shapelib.ShapeFile(filename)
652     dbf = dbflib.DBFFile(filename)
653     typemap = {dbflib.FTString: "VARCHAR",
654     dbflib.FTInteger: "INTEGER",
655     dbflib.FTDouble: "DOUBLE PRECISION"}
656    
657 bh 2096 insert_formats = []
658     if gid_column:
659     insert_formats.append("%(gid)s")
660    
661     fields = []
662     fields_decl = []
663     if gid_column:
664     fields.append(gid_column)
665     fields_decl.append("%s INT" % gid_column)
666 bh 1605 for i in range(dbf.field_count()):
667     ftype, name, width, prec = dbf.field_info(i)
668 bh 2096 fields.append(name)
669     fields_decl.append("%s %s" % (name, typemap[ftype]))
670 bh 1605 insert_formats.append("%%(%s)s" % name)
671     stmt = "CREATE TABLE %s (\n %s\n);" % (tablename,
672 bh 2096 ",\n ".join(fields_decl))
673 bh 1605 cursor.execute(stmt)
674     #print stmt
675    
676     numshapes, shapetype, mins, maxs = shp.info()
677 bh 1656 wkttype = shp_to_wkt[shapetype]
678     if force_wkt_type:
679     wkttype = force_wkt_type
680     convert = wkt_converter[wkttype]
681 bh 1605
682     cursor.execute("select AddGeometryColumn('%(dbname)s',"
683 bh 2057 "'%(tablename)s', 'the_geom', %(srid)d, '%(wkttype)s', 2);"
684 bh 1605 % locals())
685 bh 2096 fields.append("the_geom")
686 bh 2057 insert_formats.append("GeometryFromText(%(the_geom)s, %(srid)d)")
687 bh 1605
688 bh 2096 insert = ("INSERT INTO %s (%s) VALUES (%s)"
689     % (tablename, ", ".join(fields), ", ".join(insert_formats)))
690 bh 1605
691     for i in range(numshapes):
692     data = dbf.read_record(i)
693     data["tablename"] = tablename
694 bh 2096 if gid_column:
695     data["gid"] = i + gid_offset
696 bh 2057 data["srid"] = srid
697 bh 1605 data["the_geom"] = convert(shp.read_object(i).vertices())
698     #print insert % data
699     cursor.execute(insert, data)
700    
701 bh 1634 cursor.execute("GRANT SELECT ON %s TO PUBLIC;" % tablename)
702    
703 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