Django

Code

Changeset 6978

Show
Ignore:
Timestamp:
12/25/07 15:27:56 (7 months ago)
Author:
jbronn
Message:

gis: geos: all pointer access is now done via the ptr property to prevent calling GEOS routines on NULL pointers; added the geos_version_info routine; added __copy__ and __deepcopy__ interfaces that return clones (for compatibility w/queryset-refactor); __eq__ may now compare WKT strings (for compatibility w/newforms-admin); made tests compatible w/GEOS 3.0.0 release.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/branches/gis/django/contrib/gis/geos/base.py

    r6884 r6978  
    9191        #  geometries that do not have coordinate sequences) 
    9292        self._set_cs() 
     93         
     94    @property 
     95    def ptr(self): 
     96        """ 
     97        Property for controlling access to the GEOS geometry pointer.  Using 
     98        this raises an exception when the pointer is NULL, thus preventing 
     99        the C library from attempting to access an invalid memory location. 
     100        """ 
     101        if self._ptr:  
     102            return self._ptr 
     103        else: 
     104            raise GEOSException('NULL GEOS pointer encountered; was this geometry modified?') 
    93105 
    94106    def __del__(self): 
     
    98110        """ 
    99111        if self._ptr: destroy_geom(self._ptr) 
     112 
     113    def __copy__(self): 
     114        """ 
     115        Returns a clone because the copy of a GEOSGeometry may contain an 
     116        invalid pointer location if the original is garbage collected. 
     117        """ 
     118        return self.clone() 
     119 
     120    def __deepcopy__(self, memodict): 
     121        """ 
     122        The `deepcopy` routine is used by the `Node` class of django.utils.tree; 
     123        thus, the protocol routine needs to be implemented to return correct  
     124        copies (clones) of these GEOS objects, which use C pointers. 
     125        """ 
     126        return self.clone() 
    100127 
    101128    def __str__(self): 
     
    105132    def __repr__(self): 
    106133        "Short-hand representation because WKT may be very large." 
    107         return '<%s object at %s>' % (self.geom_type, hex(addressof(self._ptr))) 
     134        return '<%s object at %s>' % (self.geom_type, hex(addressof(self.ptr))) 
    108135 
    109136    # Comparison operators 
    110137    def __eq__(self, other): 
    111         "Equivalence testing." 
    112         return self.equals_exact(other) 
     138        """ 
     139        Equivalence testing, a Geometry may be compared with another Geometry 
     140        or a WKT representation. 
     141        """ 
     142        if isinstance(other, basestring): 
     143            return self.wkt == other 
     144        else: 
     145            return self.equals_exact(other) 
    113146 
    114147    def __ne__(self, other): 
    115148        "The not equals operator." 
    116         return not self.equals_exact(other) 
     149        return not (self == other) 
    117150 
    118151    ### Geometry set-like operations ### 
     
    152185        "Sets the coordinate sequence for this Geometry." 
    153186        if self.has_cs: 
    154             self._cs = GEOSCoordSeq(get_cs(self._ptr), self.hasz) 
     187            self._cs = GEOSCoordSeq(get_cs(self.ptr), self.hasz) 
    155188        else: 
    156189            self._cs = None 
     
    165198    def geom_type(self): 
    166199        "Returns a string representing the Geometry type, e.g. 'Polygon'" 
    167         return geos_type(self._ptr) 
     200        return geos_type(self.ptr) 
    168201 
    169202    @property 
    170203    def geom_typeid(self): 
    171204        "Returns an integer representing the Geometry type." 
    172         return geos_typeid(self._ptr) 
     205        return geos_typeid(self.ptr) 
    173206 
    174207    @property 
    175208    def num_geom(self): 
    176209        "Returns the number of geometries in the Geometry." 
    177         return get_num_geoms(self._ptr) 
     210        return get_num_geoms(self.ptr) 
    178211 
    179212    @property 
    180213    def num_coords(self): 
    181214        "Returns the number of coordinates in the Geometry." 
    182         return get_num_coords(self._ptr) 
     215        return get_num_coords(self.ptr) 
    183216 
    184217    @property 
     
    190223    def dims(self): 
    191224        "Returns the dimension of this Geometry (0=point, 1=line, 2=surface)." 
    192         return get_dims(self._ptr) 
     225        return get_dims(self.ptr) 
    193226 
    194227    def normalize(self): 
    195228        "Converts this Geometry to normal form (or canonical form)." 
    196         return geos_normalize(self._ptr) 
     229        return geos_normalize(self.ptr) 
    197230 
    198231    #### Unary predicates #### 
     
    203236        are empty. 
    204237        """ 
    205         return geos_isempty(self._ptr) 
     238        return geos_isempty(self.ptr) 
    206239 
    207240    @property 
    208241    def hasz(self): 
    209242        "Returns whether the geometry has a 3D dimension." 
    210         return geos_hasz(self._ptr) 
     243        return geos_hasz(self.ptr) 
    211244 
    212245    @property 
    213246    def ring(self): 
    214247        "Returns whether or not the geometry is a ring." 
    215         return geos_isring(self._ptr) 
     248        return geos_isring(self.ptr) 
    216249 
    217250    @property 
    218251    def simple(self): 
    219252        "Returns false if the Geometry not simple." 
    220         return geos_issimple(self._ptr) 
     253        return geos_issimple(self.ptr) 
    221254 
    222255    @property 
    223256    def valid(self): 
    224257        "This property tests the validity of this Geometry." 
    225         return geos_isvalid(self._ptr) 
     258        return geos_isvalid(self.ptr) 
    226259 
    227260    #### Binary predicates. #### 
    228261    def contains(self, other): 
    229262        "Returns true if other.within(this) returns true." 
    230         return geos_contains(self._ptr, other._ptr) 
     263        return geos_contains(self.ptr, other.ptr) 
    231264 
    232265    def crosses(self, other): 
     
    236269        an area) 0******** (for two curves). 
    237270        """ 
    238         return geos_crosses(self._ptr, other._ptr) 
     271        return geos_crosses(self.ptr, other.ptr) 
    239272 
    240273    def disjoint(self, other): 
     
    243276        is FF*FF****. 
    244277        """ 
    245         return geos_disjoint(self._ptr, other._ptr) 
     278        return geos_disjoint(self.ptr, other.ptr) 
    246279 
    247280    def equals(self, other): 
     
    250283        is T*F**FFF*. 
    251284        """ 
    252         return geos_equals(self._ptr, other._ptr) 
     285        return geos_equals(self.ptr, other.ptr) 
    253286 
    254287    def equals_exact(self, other, tolerance=0): 
     
    257290        specified tolerance. 
    258291        """ 
    259         return geos_equalsexact(self._ptr, other._ptr, float(tolerance)) 
     292        return geos_equalsexact(self.ptr, other.ptr, float(tolerance)) 
    260293 
    261294    def intersects(self, other): 
    262295        "Returns true if disjoint returns false." 
    263         return geos_intersects(self._ptr, other._ptr) 
     296        return geos_intersects(self.ptr, other.ptr) 
    264297 
    265298    def overlaps(self, other): 
     
    268301        is T*T***T** (for two points or two surfaces) 1*T***T** (for two curves). 
    269302        """ 
    270         return geos_overlaps(self._ptr, other._ptr) 
     303        return geos_overlaps(self.ptr, other.ptr) 
    271304 
    272305    def relate_pattern(self, other, pattern): 
     
    277310        if not isinstance(pattern, StringType) or len(pattern) > 9: 
    278311            raise GEOSException('invalid intersection matrix pattern') 
    279         return geos_relatepattern(self._ptr, other._ptr, pattern) 
     312        return geos_relatepattern(self.ptr, other.ptr, pattern) 
    280313 
    281314    def touches(self, other): 
     
    284317        is FT*******, F**T***** or F***T****. 
    285318        """ 
    286         return geos_touches(self._ptr, other._ptr) 
     319        return geos_touches(self.ptr, other.ptr) 
    287320 
    288321    def within(self, other): 
     
    291324        is T*F**F***. 
    292325        """ 
    293         return geos_within(self._ptr, other._ptr) 
     326        return geos_within(self.ptr, other.ptr) 
    294327 
    295328    #### SRID Routines #### 
    296329    def get_srid(self): 
    297330        "Gets the SRID for the geometry, returns None if no SRID is set." 
    298         s = geos_get_srid(self._ptr) 
     331        s = geos_get_srid(self.ptr) 
    299332        if s == 0: return None 
    300333        else: return s 
     
    302335    def set_srid(self, srid): 
    303336        "Sets the SRID for the geometry." 
    304         geos_set_srid(self._ptr, srid) 
     337        geos_set_srid(self.ptr, srid) 
    305338    srid = property(get_srid, set_srid) 
    306339 
     
    315348    def wkt(self): 
    316349        "Returns the WKT (Well-Known Text) of the Geometry." 
    317         return to_wkt(self._ptr) 
     350        return to_wkt(self.ptr) 
    318351 
    319352    @property 
     
    326359        # A possible faster, all-python, implementation:  
    327360        #  str(self.wkb).encode('hex') 
    328         return to_hex(self._ptr, byref(c_size_t())) 
     361        return to_hex(self.ptr, byref(c_size_t())) 
    329362 
    330363    @property 
    331364    def wkb(self): 
    332365        "Returns the WKB of the Geometry as a buffer." 
    333         bin = to_wkb(self._ptr, byref(c_size_t())) 
     366        bin = to_wkb(self.ptr, byref(c_size_t())) 
    334367        return buffer(bin) 
    335368 
     
    375408            if ptr: 
    376409                # Reassigning pointer, and resetting the SRID. 
    377                 destroy_geom(self._ptr) 
     410                destroy_geom(self.ptr) 
    378411                self._ptr = ptr 
    379412                self.srid = g.srid 
     
    389422    def boundary(self): 
    390423        "Returns the boundary as a newly allocated Geometry object." 
    391         return self._topology(geos_boundary(self._ptr)) 
     424        return self._topology(geos_boundary(self.ptr)) 
    392425 
    393426    def buffer(self, width, quadsegs=8): 
     
    399432        (Text from PostGIS documentation at ch. 6.1.3) 
    400433        """ 
    401         return self._topology(geos_buffer(self._ptr, width, quadsegs)) 
     434        return self._topology(geos_buffer(self.ptr, width, quadsegs)) 
    402435 
    403436    @property 
     
    408441        "weight" to the centroid). 
    409442        """ 
    410         return self._topology(geos_centroid(self._ptr)) 
     443        return self._topology(geos_centroid(self.ptr)) 
    411444 
    412445    @property 
     
    416449        in the Geometry. 
    417450        """ 
    418         return self._topology(geos_convexhull(self._ptr)) 
     451        return self._topology(geos_convexhull(self.ptr)) 
    419452 
    420453    def difference(self, other): 
     
    423456        that do not make up other. 
    424457        """ 
    425         return self._topology(geos_difference(self._ptr, other._ptr)) 
     458        return self._topology(geos_difference(self.ptr, other.ptr)) 
    426459 
    427460    @property 
    428461    def envelope(self): 
    429462        "Return the envelope for this geometry (a polygon)." 
    430         return self._topology(geos_envelope(self._ptr)) 
     463        return self._topology(geos_envelope(self.ptr)) 
    431464 
    432465    def intersection(self, other): 
    433466        "Returns a Geometry representing the points shared by this Geometry and other." 
    434         return self._topology(geos_intersection(self._ptr, other._ptr)) 
     467        return self._topology(geos_intersection(self.ptr, other.ptr)) 
    435468 
    436469    @property 
    437470    def point_on_surface(self): 
    438471        "Computes an interior point of this Geometry." 
    439         return self._topology(geos_pointonsurface(self._ptr)) 
     472        return self._topology(geos_pointonsurface(self.ptr)) 
    440473 
    441474    def relate(self, other): 
    442475        "Returns the DE-9IM intersection matrix for this Geometry and the other." 
    443         return geos_relate(self._ptr, other._ptr) 
     476        return geos_relate(self.ptr, other.ptr) 
    444477 
    445478    def simplify(self, tolerance=0.0, preserve_topology=False): 
     
    456489        """ 
    457490        if preserve_topology: 
    458             return self._topology(geos_preservesimplify(self._ptr, tolerance)) 
    459         else: 
    460             return self._topology(geos_simplify(self._ptr, tolerance)) 
     491            return self._topology(geos_preservesimplify(self.ptr, tolerance)) 
     492        else: 
     493            return self._topology(geos_simplify(self.ptr, tolerance)) 
    461494 
    462495    def sym_difference(self, other): 
     
    465498        and the points in other not in this Geometry. 
    466499        """ 
    467         return self._topology(geos_symdifference(self._ptr, other._ptr)) 
     500        return self._topology(geos_symdifference(self.ptr, other.ptr)) 
    468501 
    469502    def union(self, other): 
    470503        "Returns a Geometry representing all the points in this Geometry and other." 
    471         return self._topology(geos_union(self._ptr, other._ptr)) 
     504        return self._topology(geos_union(self.ptr, other.ptr)) 
    472505 
    473506    #### Other Routines #### 
     
    475508    def area(self): 
    476509        "Returns the area of the Geometry." 
    477         return geos_area(self._ptr, byref(c_double())) 
     510        return geos_area(self.ptr, byref(c_double())) 
    478511 
    479512    def distance(self, other): 
     
    485518        if not isinstance(other, GEOSGeometry):  
    486519            raise TypeError('distance() works only on other GEOS Geometries.') 
    487         return geos_distance(self._ptr, other._ptr, byref(c_double())) 
     520        return geos_distance(self.ptr, other.ptr, byref(c_double())) 
    488521 
    489522    @property 
     
    493526        circumfrence of a Polygon). 
    494527        """ 
    495         return geos_length(self._ptr, byref(c_double())) 
     528        return geos_length(self.ptr, byref(c_double())) 
    496529     
    497530    def clone(self): 
    498531        "Clones this Geometry." 
    499         return GEOSGeometry(geom_clone(self._ptr), srid=self.srid) 
     532        return GEOSGeometry(geom_clone(self.ptr), srid=self.srid) 
    500533 
    501534# Class mapping dictionary 
  • django/branches/gis/django/contrib/gis/geos/collections.py

    r6861 r6978  
    3939        ngeoms = len(init_geoms) 
    4040        geoms = get_pointer_arr(ngeoms) 
    41         for i in xrange(ngeoms): geoms[i] = geom_clone(init_geoms[i]._ptr) 
     41        for i in xrange(ngeoms): geoms[i] = geom_clone(init_geoms[i].ptr) 
    4242        super(GeometryCollection, self).__init__(create_collection(c_int(self._typeid), byref(geoms), c_uint(ngeoms)), **kwargs) 
    4343 
     
    4646        # Checking the index and returning the corresponding GEOS geometry. 
    4747        self._checkindex(index) 
    48         return GEOSGeometry(geom_clone(get_geomn(self._ptr, index)), srid=self.srid) 
     48        return GEOSGeometry(geom_clone(get_geomn(self.ptr, index)), srid=self.srid) 
    4949 
    5050    def __setitem__(self, index, geom): 
     
    5858        for i in xrange(ngeoms): 
    5959            if i == index: 
    60                 geoms[i] = geom_clone(geom._ptr) 
     60                geoms[i] = geom_clone(geom.ptr) 
    6161            else: 
    62                 geoms[i] = geom_clone(get_geomn(self._ptr, i)) 
     62                geoms[i] = geom_clone(get_geomn(self.ptr, i)) 
    6363         
    6464        # Creating a new collection, and destroying the contents of the previous poiner. 
    65         prev_ptr = self._ptr 
     65        prev_ptr = self.ptr 
    6666        srid = self.srid 
    6767        self._ptr = create_collection(c_int(self._typeid), byref(geoms), c_uint(ngeoms)) 
  • django/branches/gis/django/contrib/gis/geos/coordseq.py

    r6861 r6978  
    7777            raise GEOSException('invalid ordinate dimension "%d"' % dim) 
    7878 
     79    @property 
     80    def ptr(self): 
     81        """ 
     82        Property for controlling access to coordinate sequence pointer, 
     83        preventing attempted access to a NULL memory location. 
     84        """ 
     85        if self._ptr: return self._ptr 
     86        else: raise GEOSException('NULL coordinate sequence pointer encountered.') 
     87 
    7988    #### Ordinate getting and setting routines #### 
    8089    def getOrdinate(self, dimension, index): 
     
    8291        self._checkindex(index) 
    8392        self._checkdim(dimension) 
    84         return cs_getordinate(self._ptr, index, dimension, byref(c_double())) 
     93        return cs_getordinate(self.ptr, index, dimension, byref(c_double())) 
    8594 
    8695    def setOrdinate(self, dimension, index, value): 
     
    8897        self._checkindex(index) 
    8998        self._checkdim(dimension) 
    90         cs_setordinate(self._ptr, index, dimension, value) 
     99        cs_setordinate(self.ptr, index, dimension, value) 
    91100 
    92101    def getX(self, index): 
     
    118127    def size(self): 
    119128        "Returns the size of this coordinate sequence." 
    120         return cs_getsize(self._ptr, byref(c_uint())) 
     129        return cs_getsize(self.ptr, byref(c_uint())) 
    121130 
    122131    @property 
    123132    def dims(self): 
    124133        "Returns the dimensions of this coordinate sequence." 
    125         return cs_getdims(self._ptr, byref(c_uint())) 
     134        return cs_getdims(self.ptr, byref(c_uint())) 
    126135 
    127136    @property 
     
    136145    def clone(self): 
    137146        "Clones this coordinate sequence." 
    138         return GEOSCoordSeq(cs_clone(self._ptr), self.hasz) 
     147        return GEOSCoordSeq(cs_clone(self.ptr), self.hasz) 
    139148 
    140149    @property 
  • django/branches/gis/django/contrib/gis/geos/geometries.py

    r6861 r6978  
    167167        # Calling the base geometry initialization with the returned pointer  
    168168        #  from the function. 
    169         super(LineString, self).__init__(func(cs._ptr), srid=srid) 
     169        super(LineString, self).__init__(func(cs.ptr), srid=srid) 
    170170 
    171171    def __getitem__(self, index): 
     
    264264        nholes = len(init_holes) 
    265265        holes = get_pointer_arr(nholes) 
    266         for i in xrange(nholes): holes[i] = geom_clone(init_holes[i]._ptr) 
     266        for i in xrange(nholes): holes[i] = geom_clone(init_holes[i].ptr) 
    267267                       
    268268        # Getting the shell pointer address,  
    269         shell = geom_clone(ext_ring._ptr) 
     269        shell = geom_clone(ext_ring.ptr) 
    270270 
    271271        # Calling with the GEOS createPolygon factory. 
     
    292292        # Getting the shell 
    293293        if index == 0: 
    294             shell = geom_clone(ring._ptr) 
    295         else: 
    296             shell = geom_clone(get_extring(self._ptr)) 
     294            shell = geom_clone(ring.ptr) 
     295        else: 
     296            shell = geom_clone(get_extring(self.ptr)) 
    297297 
    298298        # Getting the interior rings (holes) 
     
    302302            for i in xrange(nholes): 
    303303                if i == (index-1): 
    304                     holes[i] = geom_clone(ring._ptr) 
     304                    holes[i] = geom_clone(ring.ptr) 
    305305                else: 
    306                     holes[i] = geom_clone(get_intring(self._ptr, i)) 
     306                    holes[i] = geom_clone(get_intring(self.ptr, i)) 
    307307            holes_param = byref(holes) 
    308308        else: 
     
    311311        # Getting the current pointer, replacing with the newly constructed 
    312312        # geometry, and destroying the old geometry. 
    313         prev_ptr = self._ptr 
     313        prev_ptr = self.ptr 
    314314        srid = self.srid 
    315315        self._ptr = create_polygon(shell, holes_param, c_uint(nholes)) 
     
    337337        """ 
    338338        self._checkindex(ring_i+1) 
    339         return GEOSGeometry(geom_clone(get_intring(self._ptr, ring_i)), srid=self.srid) 
     339        return GEOSGeometry(geom_clone(get_intring(self.ptr, ring_i)), srid=self.srid) 
    340340                                                         
    341341    #### Polygon Properties #### 
     
    344344        "Returns the number of interior rings." 
    345345        # Getting the number of rings 
    346         return get_nrings(self._ptr) 
     346        return get_nrings(self.ptr) 
    347347 
    348348    def get_ext_ring(self): 
    349349        "Gets the exterior ring of the Polygon." 
    350         return GEOSGeometry(geom_clone(get_extring(self._ptr)), srid=self.srid) 
     350        return GEOSGeometry(geom_clone(get_extring(self.ptr)), srid=self.srid) 
    351351 
    352352    def set_ext_ring(self, ring): 
  • django/branches/gis/django/contrib/gis/geos/__init__.py

    r6861 r6978  
    3333from django.contrib.gis.geos.collections import GeometryCollection, MultiPoint, MultiLineString, MultiPolygon 
    3434from django.contrib.gis.geos.error import GEOSException, GEOSIndexError 
    35 from django.contrib.gis.geos.libgeos import geos_version 
     35from django.contrib.gis.geos.libgeos import geos_version, geos_version_info 
    3636 
    3737def fromfile(file_name): 
  • django/branches/gis/django/contrib/gis/geos/libgeos.py

    r6861 r6978  
    77 get_pointer_arr(), and GEOM_PTR. 
    88""" 
    9 import atexit, os, sys 
     9import atexit, os, re, sys 
    1010from ctypes import c_char_p, string_at, Structure, CDLL, CFUNCTYPE, POINTER 
    1111from django.contrib.gis.geos.error import GEOSException 
     
    2222    from django.conf import settings 
    2323    lib_name = settings.GEOS_LIBRARY_PATH 
    24 except (AttributeError, EnvironmentError): 
     24except (AttributeError, EnvironmentError, ImportError): 
    2525    lib_name = None 
    2626 
     
    9797    return string_at(lgeos.GEOSversion()) 
    9898 
     99# Regular expression should be able to parse version strings such as 
     100# '3.0.0rc4-CAPI-1.3.3', or '3.0.0-CAPI-1.4.1' 
     101version_regex = re.compile(r'^(?P<version>\d+\.\d+\.\d+)(rc(?P<release_candidate>\d+))?-CAPI-(?P<capi_version>\d+\.\d+\.\d+)$') 
     102def geos_version_info(): 
     103    """ 
     104    Returns a dictionary containing the various version metadata parsed from 
     105    the GEOS version string, including the version number, whether the version 
     106    is a release candidate (and what number release candidate), and the C API 
     107    version. 
     108    """ 
     109    ver = geos_version() 
     110    m = version_regex.match(ver) 
     111    if not m: raise GEOSException('Could not parse version info string "%s"' % ver) 
     112    return dict((key, m.group(key)) for key in ('version', 'release_candidate', 'capi_version')) 
     113 
    99114# Calling the finishGEOS() upon exit of the interpreter. 
    100115atexit.register(lgeos.finishGEOS) 
  • django/branches/gis/django/contrib/gis/geos/prototypes/geom.py

    r6653 r6978  
    4949 
    5050### ctypes prototypes ### 
     51 
     52# TODO: Tell all users to use GEOS 3.0.0, instead of the release  
     53#  candidates, and use the new Reader and Writer APIs (e.g., 
     54#  GEOSWKT[Reader|Writer], GEOSWKB[Reader|Writer]).  A good time 
     55#  to do this will be when Refractions releases a Windows PostGIS 
     56#  installer using GEOS 3.0.0. 
    5157 
    5258# Creation routines from WKB, HEX, WKT 
  • django/branches/gis/django/contrib/gis/tests/test_geos.py

    r6884 r6978  
    11import random, unittest, sys 
    22from ctypes import ArgumentError 
    3 from django.contrib.gis.geos import \ 
    4     GEOSException, GEOSIndexError, \ 
    5     GEOSGeometry, Point, LineString, LinearRing, Polygon, \ 
    6     MultiPoint, MultiLineString, MultiPolygon, GeometryCollection, \ 
    7     fromstr, geos_version, HAS_NUMPY 
     3from django.contrib.gis.geos import * 
    84from django.contrib.gis.geos.base import HAS_GDAL 
    95from django.contrib.gis.tests.geometries import * 
     
    9086            self.assertEqual(srid, fromstr(poly.ewkt).srid) # Checking export 
    9187     
     88    def test01i_eq(self): 
     89        "Testing equivalence with WKT." 
     90        p = fromstr('POINT(5 23)') 
     91        self.assertEqual(p, p.wkt) 
     92        self.assertNotEqual(p, 'foo') 
     93        ls = fromstr('LINESTRING(0 0, 1 1, 5 5)') 
     94        self.assertEqual(ls, ls.wkt) 
     95        self.assertNotEqual(p, 'bar') 
     96 
    9297    def test02a_points(self): 
    9398        "Testing Point objects." 
     
    479484        # However, when HEX is exported, the SRID information is lost 
    480485        # and set to -1.  Essentially, the 'E' of the EWKB is not 
    481         # encoded in HEX by the GEOS C library for some reason. 
     486        # encoded in HEX by the GEOS C library unless the GEOSWKBWriter 
     487        # method is used.  GEOS 3.0.0 will not encode -1 in the HEX 
     488        # as is done in the release candidates. 
     489        info = geos_version_info() 
     490        if info['version'] == '3.0.0' and info['release_candidate']: 
     491            exp_srid = -1 
     492        else: 
     493            exp_srid = None 
     494 
    482495        p2 = fromstr(p1.hex) 
    483         self.assertEqual(-1, p2.srid) 
     496        self.assertEqual(exp_srid, p2.srid) 
    484497        p3 = fromstr(p1.hex, srid=-1) # -1 is intended. 
    485498        self.assertEqual(-1, p3.srid) 
     
    651664        self.assertEqual(g2.hex, g2.ogr.hex) 
    652665        self.assertEqual('WGS 84', g2.srs.name) 
    653          
     666 
     667    def test22_copy(self): 
     668        "Testing use with the Python `copy` module." 
     669        import copy 
     670        poly = GEOSGeometry('POLYGON((0 0, 0 23, 23 23, 23 0, 0 0), (5 5, 5 10, 10 10, 10 5, 5 5))') 
     671        cpy1 = copy.copy(poly) 
     672        cpy2 = copy.deepcopy(poly) 
     673        self.assertNotEqual(poly._ptr, cpy1._ptr) 
     674        self.assertNotEqual(poly._ptr, cpy2._ptr) 
     675 
    654676def suite(): 
    655677    s = unittest.TestSuite()