source: TI02-CSML/trunk/services/3rdParty/GeoTypes-0.6.0/GeoTypes/_PsycopgInit.py @ 2194

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI02-CSML/trunk/services/3rdParty/GeoTypes-0.6.0/GeoTypes/_PsycopgInit.py@2194
Revision 2194, 9.0 KB checked in by lawrence, 13 years ago (diff)

Adding various specs and 3rd party code of interest for the CSML
services development.

Line 
1
2################################################################################
3# Copyright (c) QinetiQ Plc 2003
4#
5# Licensed under the LGPL. For full license details see the LICENSE file.
6################################################################################
7
8"""
9Initialisation support for hooking the GeoTypes into the psycopg typecast machinery.
10"""
11import sys, traceback,re
12
13from _Point import Point
14from _LineSeg import LineSeg
15from _Path import Path
16from _Polygon import Polygon
17from _Circle import Circle
18from _Box import Box
19
20_class_map = {'point':   {'class': Point, 'oid': 600, 'name': 'POINT'},
21              'lseg':    {'class': LineSeg, 'oid': 601, 'name': 'LINESEG'},
22              'path':    {'class': Path,  'oid': 602, 'name': 'PATH'},
23              'polygon': {'class': Polygon, 'oid': 604, 'name': 'POLYGON'},
24              'circle':  {'class': Circle, 'oid': 718, 'name': 'CIRCLE'},
25              'box':     {'class': Box, 'oid': 603, 'name': 'BOX'}}
26
27from _OGMultiPolygon import OGMultiPolygon
28from _OGPoint import OGPoint
29from _OGLineString import OGLineString
30from _OGPolygon import OGPolygon
31from _OGMultiPoint import OGMultiPoint
32from _OGMultiLineString import OGMultiLineString
33from _OGGeometryCollection import OGGeometryCollection
34from _OGGeoTypeFactory import OGGeoTypeFactory
35from _WKTParser import WKTParser
36from _WKBParser import WKBParser
37from _EWKBParser import EWKBParser, HEXEWKBParser
38
39class _PostWKTGISClassFactory:
40    """
41    Private class used as a factory for OpenGID types.
42    """
43    def __init__(self):
44        pass
45   
46    def __call__(self,s=None):
47        """
48        A factory method for creating objects of the correct OpenGIS type.
49        """
50        factory = OGGeoTypeFactory()
51        parser = WKTParser(factory)
52        parser.parseGeometry(s)
53
54        return factory.getGeometry()
55
56class _PostWKBGISClassFactory:
57    """
58    Private class used as a factory for OpenGID types.
59    """
60    def __init__(self):
61        pass
62   
63    def __call__(self,s=None):
64        """
65        A factory method for creating objects of the correct OpenGIS type.
66        """
67        factory = OGGeoTypeFactory()
68        parser = WKBParser(factory)
69        parser.parseGeometry(s)
70
71        return factory.getGeometry()
72
73class _PostHEXEWKBGISClassFactory:
74    """
75    Private class used as a factory for OpenGID types.
76    """
77    def __init__(self):
78        pass
79   
80    def __call__(self,s=None):
81        """
82        A factory method for creating objects of the correct OpenGIS type.
83        """
84        factory = OGGeoTypeFactory()
85
86        # We need to know whether the format of the s is EWKB or HEXEWKB
87        # there is no full proof way of doing this so we take a guess
88        # In all the cases I have seen the ord of the byte word of an
89        # EWKB string is below 32. Because HEXEWKB is a text string it
90        # is not possible for the ord of the first character to be below
91        # 32. So this is the best way I have.
92        try:
93            if ord(s[0]) < 32:
94                parser = EWKBParser(factory)
95            else:
96                parser = HEXEWKBParser(factory)
97        except:
98            # catch the very rare case where s[0] would fail because of the
99            # size of the string
100            parser = HEXEWKBParser(factory)
101
102        parser.parseGeometry(s)
103        return factory.getGeometry()
104
105
106def _getPostgisVersion(conn,curs):
107    """returns the postgis version as (major,minor,patch)"""
108    curs.execute("select postgis_full_version()")   
109    conn.commit()
110    m = re.compile('POSTGIS="([^"]*)"').match(curs.fetchall()[0][0])
111    return m.group(1).split('.')
112   
113def _getTypeOid(conn,curs,typename):
114    curs.execute("select oid from pg_type where typname='%s'" % (typename,))
115    conn.commit()
116    return curs.fetchall()[0][0]
117
118def initialisePsycopgTypes(psycopg_module, subclass_map={}, connect_string=None, register_opengis_types=None):
119    """
120    Inform psycopg about the GeoType types.
121
122    This ensures that when the 'bound' variable method of query generation is used
123    the GeoTypes are automatically coverted into the right format for use in postgres
124    queries.
125
126    It also ensures that any results columns from queries that are of a geometric
127    type are returned as instances of the GeoType classes.
128
129    (arg psycopg_module) is the psycopg module itself. This is passes in as a parameter
130    to ensure that the GeoTypes package can be used with psycopg is required. If it
131    were imported in the module directly it work reduce the flexibility of the package.
132
133    (arg subclass_map) is a dictionary of 'postgres_type':'GeoType class' pairs. It allows
134    a caller to subclass the GeoType classes and ensure that the subclasses get hooked into
135    the psycopg type machinery rather the plain GeoType classes. See the PsycopgInit_Test.py
136    file for an example of its use. Default {}.
137
138    (arg connect_string) is a postgres connection string of the form
139    'dbname=schema_test user=postgres. If this is not None an attempt it made to create a
140    test table from which to dynamically workout the type OIDS for the geometric types. If this
141    is None as list of OID values that where correct when I tested it are used. I am not sure
142    how oftern these OIDs change but if you have problems with typecast registration try
143    passing a connect_string and see if it fixes it. Default None.
144
145    (arg register_opengis_types) is a flag to control whether the OpenGIS types are registered.
146    The the flag is not None the OpenGIS types are registered with Psycopg. If this is not None
147    a (arg connect_string) must be provided as the type oids for the OpenGIS types differ
148    between databases and the correct oid must be calculated dynamically. Default None.
149   
150    """
151
152    if subclass_map != {}:
153        for override in subclass_map.keys():
154            _class_map[override]['class'] = subclass_map[override]
155
156    if connect_string != None:
157        conn = psycopg_module.connect(connect_string)
158
159        # Start by working out the oids for the standard Postgres geo types
160        curs = conn.cursor()
161
162        # check the postgis version number
163        (major,minor,patch) = _getPostgisVersion(conn,curs)
164
165        if int(major) < 1:
166            print ("This version of GeoTypes is only tested with PostGis-1.X. \n"\
167                   "Your PostGis is version %s.%s.%s. \n"\
168                   "GeoTypes will continue but it is likely that you will have "\
169                   "some problems." % (major,minor,patch))
170           
171        for typename in _class_map.keys():
172            _class_map[typename]['oid'] = _getTypeOid(conn,curs,typename)
173           
174        if register_opengis_types:
175            # Now calculate the type oid for the OpenGIS Geometry type.
176            # This one is different for every database.
177            # At some point in its development Postgis changed the oids that
178            # are used, so we need to try a couple of combinations.
179
180            # sentinals
181            geometry_type_oid = -1
182
183            try:
184                geometry_type_oid = _getTypeOid(conn,curs,'geometry')
185            except:
186                # We failed to find a working combination of oids.
187                type, value, tb = sys.exc_info()[:3]
188                error = ("%s , %s \n" % (type, value))
189                for bits in traceback.format_exception(type,value,tb):
190                    error = error + bits + '\n'
191                del tb
192
193                raise RuntimeError, \
194                      "Failed to get the type oid for the 'geometry' type from the database:\n\n"\
195                      "                   connection_string = '%s' \n\n"\
196                      "This is probably because you have not initialised the OpenGIS types\n"\
197                      "for this database. Look at http://postgis.refractions.net/docs/x83.html\n"\
198                      "for instructions on how to do this.\n\n"\
199                      "The actual exception raised was:\n\n"\
200                      "%s" % (connect_string, error)
201           
202
203            if int(major) < 1 :
204                wkb_type_oid = -1
205
206                try:
207                    wkb_type_oid = _getTypeOid(conn,curs,'wkb')
208                except IndexError:
209                    # raised if the typename is not present
210                    pass
211
212            # Register the type factory for the OpenGIS types.
213            if int(major) < 1 :
214                psycopg_module.register_type(psycopg_module.new_type((geometry_type_oid,), 'Geometry', _PostWKTGISClassFactory()))
215                psycopg_module.register_type(psycopg_module.new_type((wkb_type_oid,), 'WKB', _PostWKBGISClassFactory()))
216            else:
217                psycopg_module.register_type(psycopg_module.new_type((geometry_type_oid,), 'Geometry', _PostHEXEWKBGISClassFactory()))
218
219    # Finally, register the standard Postgres GIS types.
220    # If no connect_string is given these use a default set of type oids that were
221    # correct for the database I tested it on.
222    for new_class in _class_map.values():
223        psycopg_module.register_type(psycopg_module.new_type((new_class['oid'],), new_class['name'], new_class['class']))
224
Note: See TracBrowser for help on using the repository browser.