Django

Code

Changeset 6024

Show
Ignore:
Timestamp:
08/29/07 20:38:44 (1 year ago)
Author:
jbronn
Message:

gis: geos: Memory-management refactor, changes include:

(1) Moved GEOSPointer to its own module, and now tracks children geometries.
(2) Fixed 'deep-indexing' issues for nested rings in GeometryCollections/MultiPolygons? (e.g., mpoly[0][1] = new_ring).
(3) Added simplify() -- simplifies geometries using the Douglas-Peucker algorithm.
(4) Conformed docstrings to Django coding style.
(5) GEOSGeometry no longer uses parent and input_type keywords.

Files:

Legend:

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

    r5991 r6024  
    11""" 
    22  This module contains the 'base' GEOSGeometry object -- all GEOS geometries 
    3   inherit from this object. 
     3  inherit from this object. 
    44""" 
    5  
    65# ctypes and types dependencies. 
    76from ctypes import \ 
     
    1211# Python and GEOS-related dependencies. 
    1312import re 
    14 from warnings import warn 
    15 from django.contrib.gis.geos.libgeos import lgeos, GEOSPointer, HAS_NUMPY, ISQLQuote 
     13from django.contrib.gis.geos.coordseq import GEOSCoordSeq, create_cs 
    1614from django.contrib.gis.geos.error import GEOSException, GEOSGeometryIndexError 
    17 from django.contrib.gis.geos.coordseq import GEOSCoordSeq, create_cs 
    18 if HAS_NUMPY: from numpy import ndarray, array 
     15from django.contrib.gis.geos.libgeos import lgeos, HAS_NUMPY, ISQLQuote 
     16from django.contrib.gis.geos.pointer import GEOSPointer, NULL_GEOM 
    1917 
    2018# Regular expression for recognizing HEXEWKB and WKT.  A prophylactic measure 
     
    2725    "A class that, generally, encapsulates a GEOS geometry." 
    2826     
     27    # Initially, all geometries use a NULL pointer. 
     28    _ptr = NULL_GEOM 
     29     
    2930    #### Python 'magic' routines #### 
    30     def __init__(self, geo_input, input_type=False, parent=None, srid=None): 
    31         """The constructor for GEOS geometry objects.  May take the following 
    32         strings as inputs, WKT ("wkt"), HEXEWKB ("hex", PostGIS-specific canonical form). 
    33  
    34         The `input_type` keyword has been deprecated -- geometry type is now auto-detected. 
    35  
    36         The `parent` keyword is for internal use only, and indicates to the garbage collector 
    37           not to delete this geometry because it was spawned from a parent (e.g., the exterior 
    38           ring from a polygon).  Its value is the GEOSPointer of the parent geometry. 
    39         """ 
    40  
    41         # Initially, setting the pointer to NULL 
    42         self._ptr = GEOSPointer(0) 
     31    def __init__(self, geo_input, srid=None): 
     32        """ 
     33        The base constructor for GEOS geometry objects, and may take the following 
     34         string inputs: WKT and HEXEWKB (a PostGIS-specific canonical form). 
     35 
     36        The `srid` keyword is used to specify the Source Reference Identifier 
     37         (SRID) number for this Geometry.  If not set, the SRID will be None. 
     38        """ 
    4339 
    4440        if isinstance(geo_input, UnicodeType): 
    4541            # Encoding to ASCII, WKT or HEXEWKB doesn't need any more. 
    4642            geo_input = geo_input.encode('ascii') 
    47  
    4843        if isinstance(geo_input, StringType): 
    49             if input_type: warn('input_type keyword is deprecated') 
    50  
    5144            if hex_regex.match(geo_input): 
    5245                # If the regex matches, the geometry is in HEX form. 
     
    5952            else: 
    6053                raise GEOSException, 'given string input "%s" unrecognized as WKT or HEXEWKB.' % geo_input 
    61  
    6254        elif isinstance(geo_input, (IntType, GEOSPointer)): 
    63             # When the input is either a raw pointer value (an integer), or a GEOSPointer object. 
     55            # When the input is either a memory address (an integer), or a  
     56            #  GEOSPointer object. 
    6457            g = geo_input 
    6558        else: 
     
    6861 
    6962        if bool(g): 
    70             # If we have a GEOSPointer object, just set the '_ptr' attribute with input 
    71             if isinstance(g, GEOSPointer): self._ptr = g 
    72             else: self._ptr.set(g) # Otherwise, set with the address 
     63            # Setting the pointer object with a valid pointer. 
     64            self._ptr = GEOSPointer(g) 
    7365        else: 
    7466            raise GEOSException, 'Could not initialize GEOS Geometry with given input.' 
    7567 
    76         # Setting the 'parent' flag -- when the object is labeled with this flag 
    77         #  it will not be destroyed by __del__().  This is used for child geometries spawned from 
    78         #  parent geometries (e.g., LinearRings from a Polygon, Points from a MultiPoint, etc.). 
    79         if isinstance(parent, GEOSPointer): 
    80             self._parent = parent 
    81         else: 
    82             self._parent = GEOSPointer(0) 
    83          
    8468        # Setting the SRID, if given. 
    8569        if srid and isinstance(srid, int): self.srid = srid 
     
    8872        self.__class__ = GEOS_CLASSES[self.geom_type] 
    8973 
    90         # Getting the coordinate sequence for the geometry (will be None on geometries that 
    91         #  do not have coordinate sequences) 
    92         self._get_cs() 
    93  
    94         # Extra setup needed for Geometries that may be parents. 
     74        # Setting the coordinate sequence for the geometry (will be None on  
     75        #  geometries that do not have coordinate sequences) 
     76        self._set_cs() 
     77 
     78        # _populate() needs to be called for parent Geometries. 
    9579        if isinstance(self, (Polygon, GeometryCollection)): self._populate() 
    9680 
    9781    def __del__(self): 
    98         "Destroys this geometry -- only if the pointer is valid and whether or not it belongs to a parent." 
    99         #print 'base: Deleting %s (parent=%s, valid=%s)' % (self.__class__.__name__, self._parent, self._ptr.valid) 
    100         # Only calling destroy on valid pointers not spawned from a parent 
    101         if self._ptr.valid and not self._parent: lgeos.GEOSGeom_destroy(self._ptr()) 
     82        """ 
     83        Destroys this Geometry; in other words, frees the memory used by the 
     84         GEOS C++ object -- but only if the pointer is not a child Geometry 
     85         (e.g., don't delete the LinearRings spawned from a Polygon). 
     86        """ 
     87        #print 'base: Deleting %s (parent=%s, valid=%s)' % (self.__class__.__name__, self._ptr.parent, self._ptr.valid) 
     88        if not self._ptr.child: self._ptr.destroy() 
    10289 
    10390    def __str__(self): 
     
    157144    # g1 ^= g2 
    158145    def __ixor__(self, other): 
    159         "Reassigns this Geometry to the symmetric difference of this Geometry and the other." 
     146        """ 
     147        Reassigns this Geometry to the symmetric difference of this Geometry  
     148        and the other. 
     149        """ 
    160150        return self.sym_difference(other) 
    161151 
     152    #### Internal GEOSPointer-related routines. #### 
    162153    def _nullify(self): 
    163         """During initialization of geometries from other geometries, this routine is 
    164         used to nullify any parent geometries (since they will now be missing memory 
    165         components) and to nullify the geometry itself to prevent future access. 
    166         Only the address (an integer) of the current geometry is returned for use in 
    167         initializing the new geometry.""" 
     154        """ 
     155        Returns the address of this Geometry, and nullifies any related pointers. 
     156        This function is called if this Geometry is used in the initialization 
     157        of another Geometry. 
     158        """ 
    168159        # First getting the memory address of the geometry. 
    169160        address = self._ptr() 
     
    171162        # If the geometry is a child geometry, then the parent geometry pointer is 
    172163        #  nullified. 
    173         if self._parent: self._parent.nullify() 
    174  
     164        if self._ptr.child:  
     165            p = self._ptr.parent 
     166            # If we have a grandchild (a LinearRing from a MultiPolygon or 
     167            #  GeometryCollection), then nullify the collection as well. 
     168            if p.child: p.parent.nullify() 
     169            p.nullify() 
     170             
    175171        # Nullifying the geometry pointer 
    176172        self._ptr.nullify() 
     
    179175 
    180176    def _reassign(self, new_geom): 
    181         "Internal routine for reassigning internal pointer to a new geometry." 
     177        "Reassigns the internal pointer to that of the new Geometry." 
    182178        # Only can re-assign when given a pointer or a geometry. 
    183179        if not isinstance(new_geom, (GEOSPointer, GEOSGeometry)): 
     
    187183        # Re-assigning the internal GEOSPointer to the new geometry, nullifying 
    188184        #  the new Geometry in the process. 
    189         if isinstance(new_geom, GEOSGeometry): self._ptr.set(new_geom._nullify()) 
    190         else: self._ptr = new_geom 
     185        if isinstance(new_geom, GEOSPointer): self._ptr = new_geom 
     186        else: self._ptr = GEOSPointer(new_geom._nullify()) 
    191187         
    192188        # The new geometry class may be different from the original, so setting 
     
    201197            return self 
    202198        else: 
    203             raise GEOSException, 'Error implementing psycopg2 protocol. Is psycopg2 installed?' 
     199            raise GEOSException, 'Error implementing psycopg2 protocol. Is psycopg2 installed?' 
    204200 
    205201    def getquoted(self): 
    206         "Returns a properly quoted string for use in PostgresSQL/PostGIS." 
     202        "Returns a properly quoted string for use in PostgreSQL/PostGIS." 
    207203        # Using ST_GeomFromText(), corresponds to SQL/MM ISO standard. 
    208204        return "ST_GeomFromText('%s', %s)" % (self.wkt, self.srid or -1) 
     
    218214            return False 
    219215 
    220     def _get_cs(self): 
    221         "Gets the coordinate sequence for this Geometry." 
     216    def _set_cs(self): 
     217        "Sets the coordinate sequence for this Geometry." 
    222218        if self.has_cs: 
    223             self._ptr.set(lgeos.GEOSGeom_getCoordSeq(self._ptr()), coordseq=True) 
     219            if not self._ptr.coordseq_valid: 
     220                self._ptr.set_coordseq(lgeos.GEOSGeom_getCoordSeq(self._ptr())) 
    224221            self._cs = GEOSCoordSeq(self._ptr, self.hasz) 
    225222        else: 
     
    273270    ## Internal for GEOS unary & binary predicate functions ## 
    274271    def _unary_predicate(self, func): 
    275         """Returns the result, or raises an exception for the given unary  
    276         predicate function.""" 
     272        """ 
     273        Returns the result, or raises an exception for the given unary predicate  
     274         function. 
     275        """ 
    277276        val = func(self._ptr()) 
    278277        if val == 0: return False 
     
    281280 
    282281    def _binary_predicate(self, func, other, *args): 
    283         """Returns the result, or raises an exception for the given binary  
    284         predicate function.""" 
     282        """ 
     283        Returns the result, or raises an exception for the given binary  
     284         predicate function. 
     285        """ 
    285286        if not isinstance(other, GEOSGeometry): 
    286287            raise TypeError, 'Binary predicate operation ("%s") requires another GEOSGeometry instance.' % func.__name__ 
     
    293294    @property 
    294295    def empty(self): 
    295         "Returns a boolean indicating whether the set of points in this Geometry are empty." 
     296        """ 
     297        Returns a boolean indicating whether the set of points in this Geometry  
     298         are empty. 
     299        """ 
    296300        return self._unary_predicate(lgeos.GEOSisEmpty) 
    297301 
     
    318322    #### Binary predicates. #### 
    319323    def relate_pattern(self, other, pattern): 
    320         """Returns true if the elements in the DE-9IM intersection matrix for 
    321         the two Geometries match the elements in pattern.""" 
     324        """ 
     325        Returns true if the elements in the DE-9IM intersection matrix for the  
     326         two Geometries match the elements in pattern. 
     327        """ 
    322328        if len(pattern) > 9: 
    323329            raise GEOSException, 'invalid intersection matrix pattern' 
     
    325331 
    326332    def disjoint(self, other): 
    327         """Returns true if the DE-9IM intersection matrix for the two Geometries  
    328         is FF*FF****.""" 
     333        """ 
     334        Returns true if the DE-9IM intersection matrix for the two Geometries  
     335         is FF*FF****. 
     336        """ 
    329337        return self._binary_predicate(lgeos.GEOSDisjoint, other) 
    330338 
    331339    def touches(self, other): 
    332         """Returns true if the DE-9IM intersection matrix for the two Geometries  
    333         is FT*******, F**T***** or F***T****.""" 
     340        """ 
     341        Returns true if the DE-9IM intersection matrix for the two Geometries 
     342         is FT*******, F**T***** or F***T****. 
     343        """ 
    334344        return self._binary_predicate(lgeos.GEOSTouches, other) 
    335345 
     
    339349 
    340350    def crosses(self, other): 
    341         """Returns true if the DE-9IM intersection matrix for the two Geometries 
    342         is T*T****** (for a point and a curve,a point and an area or a line and  
    343         an area) 0******** (for two curves).""" 
     351        """ 
     352        Returns true if the DE-9IM intersection matrix for the two Geometries 
     353         is T*T****** (for a point and a curve,a point and an area or a line and 
     354         an area) 0******** (for two curves). 
     355        """ 
    344356        return self._binary_predicate(lgeos.GEOSCrosses, other) 
    345357 
    346358    def within(self, other): 
    347         """Returns true if the DE-9IM intersection matrix for the two Geometries  
    348         is T*F**F***.""" 
     359        """ 
     360        Returns true if the DE-9IM intersection matrix for the two Geometries  
     361         is T*F**F***. 
     362        """ 
    349363        return self._binary_predicate(lgeos.GEOSWithin, other) 
    350364 
     
    354368 
    355369    def overlaps(self, other): 
    356         """Returns true if the DE-9IM intersection matrix for the two Geometries  
    357         is T*T***T** (for two points or two surfaces) 1*T***T** (for two curves).""" 
     370        """ 
     371        Returns true if the DE-9IM intersection matrix for the two Geometries  
     372         is T*T***T** (for two points or two surfaces) 1*T***T** (for two curves). 
     373        """ 
    358374        return self._binary_predicate(lgeos.GEOSOverlaps, other) 
    359375 
    360376    def equals(self, other): 
    361         """Returns true if the DE-9IM intersection matrix for the two Geometries  
    362         is T*F**FFF*.""" 
     377        """ 
     378        Returns true if the DE-9IM intersection matrix for the two Geometries  
     379         is T*F**FFF*. 
     380        """ 
    363381        return self._binary_predicate(lgeos.GEOSEquals, other) 
    364382 
    365383    def equals_exact(self, other, tolerance=0): 
    366         """Returns true if the two Geometries are exactly equal, up to a  
    367         specified tolerance.""" 
     384        """ 
     385        Returns true if the two Geometries are exactly equal, up to a 
     386         specified tolerance. 
     387        """ 
    368388        return self._binary_predicate(lgeos.GEOSEqualsExact, other,  
    369389                                      c_double(tolerance)) 
     
    384404    @property 
    385405    def wkt(self): 
    386         "Returns the WKT of the Geometry." 
     406        "Returns the WKT (Well-Known Text) of the Geometry." 
    387407        return string_at(lgeos.GEOSGeomToWKT(self._ptr())) 
    388408 
    389409    @property 
    390410    def hex(self): 
    391         "Returns the WKBHEX of the Geometry." 
     411        "Returns the HEXEWKB of the Geometry." 
    392412        sz = c_size_t() 
    393413        h = lgeos.GEOSGeomToHEX_buf(self._ptr(), byref(sz)) 
     
    402422    #### Topology Routines #### 
    403423    def _unary_topology(self, func, *args): 
    404         """Returns a GEOSGeometry for the given unary (takes only one Geomtery  
    405         as a paramter) topological operation.""" 
     424        """ 
     425        Returns a GEOSGeometry for the given unary (takes only one Geomtery  
     426         as a paramter) topological operation. 
     427        """ 
    406428        return GEOSGeometry(func(self._ptr(), *args), srid=self.srid) 
    407429 
    408430    def _binary_topology(self, func, other, *args): 
    409         """Returns a GEOSGeometry for the given binary (takes two Geometries  
    410         as parameters) topological operation.""" 
     431        """ 
     432        Returns a GEOSGeometry for the given binary (takes two Geometries  
     433         as parameters) topological operation. 
     434        """ 
    411435        return GEOSGeometry(func(self._ptr(), other._ptr(), *args), srid=self.srid) 
    412436 
    413437    def buffer(self, width, quadsegs=8): 
    414         """Returns a geometry that represents all points whose distance from this 
    415         Geometry is less than or equal to distance. Calculations are in the 
    416         Spatial Reference System of this Geometry. The optional third parameter sets 
    417         the number of segment used to approximate a quarter circle (defaults to 8). 
    418         (Text from PostGIS documentation at ch. 6.1.3) 
     438        """ 
     439        Returns a geometry that represents all points whose distance from this 
     440         Geometry is less than or equal to distance. Calculations are in the 
     441         Spatial Reference System of this Geometry. The optional third parameter sets 
     442         the number of segment used to approximate a quarter circle (defaults to 8). 
     443         (Text from PostGIS documentation at ch. 6.1.3) 
    419444        """ 
    420445        if not isinstance(width, (FloatType, IntType)): 
     
    431456    @property 
    432457    def centroid(self): 
    433         """The centroid is equal to the centroid of the set of component Geometries 
    434         of highest dimension (since the lower-dimension geometries contribute zero 
    435         "weight" to the centroid).""" 
     458        """ 
     459        The centroid is equal to the centroid of the set of component Geometries 
     460         of highest dimension (since the lower-dimension geometries contribute zero 
     461         "weight" to the centroid). 
     462        """ 
    436463        return self._unary_topology(lgeos.GEOSGetCentroid) 
    437464 
     
    443470    @property 
    444471    def convex_hull(self): 
    445         """Returns the smallest convex Polygon that contains all the points  
    446         in the Geometry.""" 
     472        """ 
     473        Returns the smallest convex Polygon that contains all the points  
     474         in the Geometry. 
     475        """ 
    447476        return self._unary_topology(lgeos.GEOSConvexHull) 
    448477 
     
    452481        return self._unary_topology(lgeos.GEOSPointOnSurface) 
    453482 
     483    def simplify(self, tolerance=0.0): 
     484        """ 
     485        Returns the Geometry, simplified using the Douglas-Peucker algorithm 
     486         to the specified tolerance (higher tolerance => less points).  If no 
     487         tolerance provided, defaults to 0. 
     488        """ 
     489        return self._unary_topology(lgeos.GEOSSimplify, c_double(tolerance)) 
     490 
    454491    def relate(self, other): 
    455         "Returns the DE-9IM intersection matrix for this geometry and the other." 
     492        "Returns the DE-9IM intersection matrix for this Geometry and the other." 
    456493        return string_at(lgeos.GEOSRelate(self._ptr(), other._ptr())) 
    457494 
     
    462499 
    463500    def sym_difference(self, other): 
    464         """Returns a set combining the points in this Geometry not in other, 
    465         and the points in other not in this Geometry.""" 
     501        """ 
     502        Returns a set combining the points in this Geometry not in other, 
     503         and the points in other not in this Geometry. 
     504        """ 
    466505        return self._binary_topology(lgeos.GEOSSymDifference, other) 
    467506 
     
    484523 
    485524    def distance(self, other): 
    486         """Returns the distance between the closest points on this Geometry 
    487         and the other. Units will be in those of the coordinate system. of 
    488         the Geometry.""" 
     525        """ 
     526        Returns the distance between the closest points on this Geometry 
     527         and the other. Units will be in those of the coordinate system of 
     528         the Geometry. 
     529        """ 
    489530        if not isinstance(other, GEOSGeometry):  
    490531            raise TypeError, 'distance() works only on other GEOS Geometries.' 
     
    496537    @property 
    497538    def length(self): 
    498         """Returns the length of this Geometry (e.g., 0 for point, or the 
    499         circumfrence of a Polygon).""" 
     539        """ 
     540        Returns the length of this Geometry (e.g., 0 for point, or the 
     541         circumfrence of a Polygon). 
     542        """ 
    500543        l = c_double() 
    501544        status = lgeos.GEOSLength(self._ptr(), byref(l)) 
  • django/branches/gis/django/contrib/gis/geos/collections.py

    r5805 r6024  
    11""" 
    2   This module houses the Geometry Collection objects: 
    3      GeometryCollection, MultiPoint, MultiLineString, and MultiPolygon 
     2  This module houses the Geometry Collection objects: 
     3   GeometryCollection, MultiPoint, MultiLineString, and MultiPolygon 
    44""" 
    55from ctypes import c_int, c_uint, byref, cast 
    66from types import TupleType, ListType 
    7 from django.contrib.gis.geos.libgeos import lgeos, GEOSPointer, get_pointer_arr, GEOM_PTR 
    87from django.contrib.gis.geos.base import GEOSGeometry 
    98from django.contrib.gis.geos.error import GEOSException, GEOSGeometryIndexError 
    109from django.contrib.gis.geos.geometries import Point, LineString, LinearRing, Polygon 
     10from django.contrib.gis.geos.libgeos import lgeos, get_pointer_arr, GEOM_PTR 
     11from django.contrib.gis.geos.pointer import GEOSPointer 
    1112 
    1213class GeometryCollection(GEOSGeometry): 
     
    1617    def __init__(self, *args, **kwargs): 
    1718        "Initializes a Geometry Collection from a sequence of Geometry objects." 
    18         # Setting up the collection for creation 
    19         self._ptr = GEOSPointer(0) # Initially NULL 
    20         self._geoms = {} 
    21         self._parent = None 
    2219 
    2320        # Checking the arguments 
     
    2522            raise TypeError, 'Must provide at least one Geometry to initialize %s.' % self.__class__.__name__ 
    2623 
    27         if len(args) == 1: # If only one geometry provided or a list of geometries is provided 
     24        if len(args) == 1:  
     25            # If only one geometry provided or a list of geometries is provided 
     26            #  in the first argument. 
    2827            if isinstance(args[0], (TupleType, ListType)): 
    2928                init_geoms = args[0] 
     
    3736            raise TypeError, 'Invalid Geometry type encountered in the arguments.' 
    3837 
    39         # Creating the geometry pointer array 
     38        # Creating the geometry pointer array, and populating each element in 
     39        #  the array with the address of the Geometry returned by _nullify(). 
    4040        ngeom = len(init_geoms) 
    4141        geoms = get_pointer_arr(ngeom) 
    42  
    43         # Incrementing through each input geometry. 
    4442        for i in xrange(ngeom): 
    4543            geoms[i] = cast(init_geoms[i]._nullify(), GEOM_PTR) 
    4644         
    47         # Calling the parent class, using the pointer returned from GEOS createCollection() 
    48         super(GeometryCollection, self).__init__(lgeos.GEOSGeom_createCollection(c_int(self._typeid), byref(geoms), c_uint(ngeom)), **kwargs) 
     45        # Calling the parent class, using the pointer returned from the  
     46        #  GEOS createCollection() factory. 
     47        addr = lgeos.GEOSGeom_createCollection(c_int(self._typeid),  
     48                                               byref(geoms), c_uint(ngeom)) 
     49        super(GeometryCollection, self).__init__(addr, **kwargs) 
    4950 
    5051    def __del__(self): 
    5152        "Overloaded deletion method for Geometry Collections." 
    52         #print 'collection: Deleting %s (parent=%s, valid=%s)' % (self.__class__.__name__, self._parent, self._ptr.valid) 
     53        #print 'collection: Deleting %s (parent=%s, valid=%s)' % (self.__class__.__name__, self._ptr.parent, self._ptr.valid) 
    5354        # If this geometry is still valid, it hasn't been modified by others. 
    5455        if self._ptr.valid: 
    55             # Nullifying pointers to internal geometries, preventing any attempted future access. 
    56             for k in self._geoms: self._geoms[k].nullify() 
     56            # Nullifying pointers to internal Geometries, preventing any  
     57            #  attempted future access. 
     58            for g in self._ptr: g.nullify() 
    5759        else: 
    58             # Internal memory has become part of other Geometry objects, must delete the 
    59             #  internal objects which are still valid individually, since calling destructor 
    60             #  on entire geometry will result in an attempted deletion of NULL pointers for 
    61             #  the missing components. 
    62             for k in self._geoms: 
    63                 if self._geoms[k].valid: 
    64                     lgeos.GEOSGeom_destroy(self._geoms[k].address) 
    65                     self._geoms[k].nullify() 
     60            # Internal memory has become part of other Geometry objects; must  
     61            #  delete the internal objects which are still valid individually,  
     62            #  because calling the destructor on the entire geometry will result  
     63            #  in an attempted deletion of NULL pointers for the missing  
     64            #  components (which may crash Python). 
     65            for g in self._ptr: 
     66                if len(g) > 0: 
     67                    # The collection geometry is a Polygon, destroy any leftover 
     68                    #  LinearRings. 
     69                    for r in g: r.destroy() 
     70                g.destroy() 
     71                     
    6672        super(GeometryCollection, self).__del__() 
    6773             
     
    7076        # Checking the index and returning the corresponding GEOS geometry. 
    7177        self._checkindex(index) 
    72         return GEOSGeometry(self._geoms[index], parent=self._ptr, srid=self.srid) 
     78        return GEOSGeometry(self._ptr[index], srid=self.srid) 
    7379 
    7480    def __setitem__(self, index, geom): 
     
    106112        "Overloaded from base method to nullify geometry references in this Collection." 
    107113        # Nullifying the references to the internal Geometry objects from this Collection. 
    108         for k in self._geoms: self._geoms[k].nullify() 
     114        for g in self._ptr: g.nullify() 
    109115        return super(GeometryCollection, self)._nullify() 
    110116 
    111117    def _populate(self): 
    112         "Populates the internal child geometry dictionary." 
    113         self._geoms = {} 
    114         for i in xrange(self.num_geom): 
    115             self._geoms[i] = GEOSPointer(lgeos.GEOSGetGeometryN(self._ptr(), c_int(i))) 
     118        "Internal routine that populates the internal children geometries list." 
     119        ptr_list = [] 
     120        for i in xrange(len(self)): 
     121            # Getting the geometry pointer for the geometry at the index. 
     122            geom_ptr = lgeos.GEOSGetGeometryN(self._ptr(), c_int(i)) 
     123 
     124            # Adding the coordinate sequence to the list, or using None if the 
     125            #  collection Geometry doesn't support coordinate sequences. 
     126            if lgeos.GEOSGeomTypeId(geom_ptr) in (0, 1, 2): 
     127                ptr_list.append((geom_ptr, lgeos.GEOSGeom_getCoordSeq(geom_ptr))) 
     128            else: 
     129                ptr_list.append((geom_ptr, None)) 
     130        self._ptr.set_children(ptr_list) 
    116131 
    117132    @property 
  • django/branches/gis/django/contrib/gis/geos/coordseq.py

    r5805 r6024  
    1 from django.contrib.gis.geos.libgeos import lgeos, GEOSPointer, HAS_NUMPY 
     1""" 
     2  This module houses the GEOSCoordSeq object, and is used internally 
     3   by GEOSGeometry to house the actual coordinates of the Point, 
     4   LineString, and LinearRing geometries. 
     5""" 
    26from django.contrib.gis.geos.error import GEOSException, GEOSGeometryIndexError 
     7from django.contrib.gis.geos.libgeos import lgeos, HAS_NUMPY 
     8from django.contrib.gis.geos.pointer import GEOSPointer 
    39from ctypes import c_double, c_int, c_uint, byref 
    410from types import ListType, TupleType 
    511if HAS_NUMPY: from numpy import ndarray 
    6  
    7 """ 
    8   This module houses the GEOSCoordSeq object, and is used internally 
    9   by GEOSGeometry to house the actual coordinates of the Point, 
    10   LineString, and LinearRing geometries. 
    11 """ 
    1212 
    1313class GEOSCoordSeq(object): 
     
    3232 
    3333    def __str__(self): 
    34         "The string representation of the coordinate sequence." 
     34        "Returns the string representation of the coordinate sequence." 
    3535        return str(self.tuple) 
    3636 
    3737    def __getitem__(self, index): 
    38         "Can use the index [] operator to get coordinate sequence at an index." 
     38        "Returns the coordinate sequence value at the given index." 
    3939        coords = [self.getX(index), self.getY(index)] 
    4040        if self.dims == 3 and self._z: 
     
    4343 
    4444    def __setitem__(self, index, value): 
    45         "Can use the index [] operator to set coordinate sequence at an index." 
     45        "Sets the coordinate sequence value at the given index." 
    4646        # Checking the input value 
    4747        if isinstance(value, (ListType, TupleType)): 
     
    6767    #### Internal Routines #### 
    6868    def _checkindex(self, index): 
    69         "Checks the index." 
     69        "Checks the given index." 
    7070        sz = self.size 
    7171        if (sz < 1) or (index < 0) or (index >= sz): 
     
    7979    #### Ordinate getting and setting routines #### 
    8080    def getOrdinate(self, dimension, index): 
    81         "Gets the value for the given dimension and index." 
     81        "Returns the value for the given dimension and index." 
    8282        self._checkindex(index) 
    8383        self._checkdim(dimension) 
     
    153153    @property 
    154154    def hasz(self): 
    155         "Inherits this from the parent geometry." 
     155        """ 
     156        Returns whether this coordinate sequence is 3D.  This property value is 
     157         inherited from the parent Geometry. 
     158        """ 
    156159        return self._z 
    157160 
  • django/branches/gis/django/contrib/gis/geos/error.py

    r5655 r6024  
     1""" 
     2  This module houses the GEOS exceptions, specifically, GEOSException and 
     3   GEOSGeometryIndexError. 
     4""" 
    15 
    26class GEOSException(Exception): 
     
    59 
    610class GEOSGeometryIndexError(GEOSException, KeyError): 
    7     """This exception is raised when an invalid index is encountered, and has 
     11    """ 
     12    This exception is raised when an invalid index is encountered, and has 
    813    the 'silent_variable_feature' attribute set to true.  This ensures that 
    914    django's templates proceed to use the next lookup type gracefully when 
     
    1116    """ 
    1217    # "If, during the method lookup, a method raises an exception, the exception 
    13     #  will be propagated, unless the exception has an attribute silent_variable_failure 
    14     #  whose value is True." -- django template docs. 
     18    #  will be propagated, unless the exception has an attribute  
     19    #  `silent_variable_failure` whose value is True." -- Django template docs. 
    1520    silent_variable_failure = True 
  • django/branches/gis/django/contrib/gis/geos/geometries.py

    r5832 r6024  
    44   GEOSGeometry. 
    55""" 
    6  
    76from ctypes import c_double, c_int, c_uint, byref, cast 
    87from types import FloatType, IntType, ListType, TupleType 
     8from django.contrib.gis.geos.base import GEOSGeometry 
    99from django.contrib.gis.geos.coordseq import GEOSCoordSeq, create_cs 
    10 from django.contrib.gis.geos.libgeos import lgeos, GEOSPointer, get_pointer_arr, GEOM_PTR, HAS_NUMPY 
    11 from django.contrib.gis.geos.base import GEOSGeometry 
     10from django.contrib.gis.geos.libgeos import lgeos, get_pointer_arr, GEOM_PTR, HAS_NUMPY 
     11from django.contrib.gis.geos.pointer import GEOSPointer 
    1212from django.contrib.gis.geos.error import GEOSException, GEOSGeometryIndexError 
    1313if HAS_NUMPY: from numpy import ndarray, array 
     
    1616 
    1717    def __init__(self, x, y=None, z=None, srid=None): 
    18         """The Point object may be initialized with either a tuple, or individual 
    19         parameters.  For example: 
     18        """ 
     19        The Point object may be initialized with either a tuple, or individual 
     20         parameters.  For example: 
    2021          >>> p = Point((5, 23)) # 2D point, passed in as a tuple 
    2122          >>> p = Point(5, 23, 8) # 3D point, passed in with individual parameters 
    2223        """ 
    2324 
    24         # Setting-up for Point Creation 
    25         self._ptr = GEOSPointer(0) # Initially NULL 
    26         self._parent = None 
    27  
    2825        if isinstance(x, (TupleType, ListType)): 
    29             # Here a tuple or list was passed in under the ``x`` parameter. 
     26            # Here a tuple or list was passed in under the `x` parameter. 
    3027            ndim = len(x) 
    3128            if ndim < 2 or ndim > 3: 
     
    3330            coords = x 
    3431        elif isinstance(x, (IntType, FloatType)) and isinstance(y, (IntType, FloatType)): 
    35             # Here X, Y, and (optionally) Z were passed in individually as parameters. 
     32            # Here X, Y, and (optionally) Z were passed in individually, as parameters. 
    3633            if isinstance(z, (IntType, FloatType)): 
    3734                ndim = 3 
     
    4340            raise TypeError, 'Invalid parameters given for Point initialization.' 
    4441 
    45         # Creating the coordinate sequence 
     42        # Creating the coordinate sequence, and setting X, Y, [Z] 
    4643        cs = create_cs(c_uint(1), c_uint(ndim)) 
    47  
    48         # Setting the X 
    4944        status = lgeos.GEOSCoordSeq_setX(cs, c_uint(0), c_double(coords[0])) 
    5045        if not status: raise GEOSException, 'Could not set X during Point initialization.' 
    51  
    52         # Setting the Y 
    5346        status = lgeos.GEOSCoordSeq_setY(cs, c_uint(0), c_double(coords[1])) 
    5447        if not status: raise GEOSException, 'Could not set Y during Point initialization.' 
    55  
    56         # Setting the Z 
    5748        if ndim == 3: 
    5849            status = lgeos.GEOSCoordSeq_setZ(cs, c_uint(0), c_double(coords[2])) 
    5950 
    60         # Initializing from the geometry, and getting a Python object 
     51        # Initializing using the address returned from the GEOS  
     52        #  createPoint factory. 
    6153        super(Point, self).__init__(lgeos.GEOSGeom_createPoint(cs), srid=srid) 
    6254 
     
    126118    #### Python 'magic' routines #### 
    127119    def __init__(self, *args, **kwargs): 
    128         """Initializes on the given sequence -- may take lists, tuples, NumPy arrays 
    129         of X,Y pairs, or Point objects.  If Point objects are used, ownership is 
    130         _not_ transferred to the LineString object. 
     120        """ 
     121        Initializes on the given sequence -- may take lists, tuples, NumPy arrays 
     122         of X,Y pairs, or Point objects.  If Point objects are used, ownership is 
     123         _not_ transferred to the LineString object. 
    131124 
    132125        Examples: 
     
    136129          ls = LineString(Point(1, 1), Point(2, 2)) 
    137130        """ 
    138         # Setting up for LineString creation 
    139         self._ptr = GEOSPointer(0) # Initially NULL 
    140         self._parent = None 
    141  
    142         # If only one argument was provided, then set the coords array appropriately 
     131        # If only one argument provided, set the coords array appropriately 
    143132        if len(args) == 1: coords = args[0] 
    144133        else: coords = args 
     
    167156            raise TypeError, 'Invalid initialization input for LineStrings.' 
    168157 
    169         # Creating the coordinate sequence 
     158        # Creating a coordinate sequence object because it is easier to  
     159        #  set the points using GEOSCoordSeq.__setitem__(). 
    170160        cs = GEOSCoordSeq(GEOSPointer(0, create_cs(c_uint(ncoords), c_uint(ndim))), z=bool(ndim==3)) 
    171  
    172         # Setting each point in the coordinate sequence 
    173161        for i in xrange(ncoords): 
    174162            if numpy_coords: cs[i] = coords[i,:] 
     
    185173        srid = kwargs.get('srid', None) 
    186174        
    187         # Calling the base geometry initialization with the returned pointer from the function. 
     175        # Calling the base geometry initialization with the returned pointer  
     176        #  from the function. 
    188177        super(LineString, self).__init__(func(cs._ptr.coordseq()), srid=srid) 
    189178 
     
    215204 
    216205    def _listarr(self, func): 
    217         """Internal routine that returns a sequence (list) corresponding with 
    218         the given function.  Will return a numpy array if possible.""" 
    219         lst = [func(i) for i in xrange(len(self))] # constructing the list, using the function 
     206        """ 
     207        Internal routine that returns a sequence (list) corresponding with 
     208        the given function.  Will return a numpy array if possible. 
     209        """ 
     210        lst = [func(i) for i in xrange(len(self))] 
    220211        if HAS_NUMPY: return array(lst) # ARRRR! 
    221212        else: return lst 
     
    252243 
    253244    def __init__(self, *args, **kwargs): 
    254         """Initializes on an exterior ring and a sequence of holes (both instances of LinearRings. 
    255         All LinearRing instances used for creation will become owned by this Polygon. 
     245        """ 
     246        Initializes on an exterior ring and a sequence of holes (both 
     247         instances of LinearRings. All LinearRing instances used for creation 
     248         will become owned by this Polygon. 
    256249         
    257         Examples, where shell, hole1, and hole2 are valid LinearRing geometries: 
    258           poly = Polygon(shell, hole1, hole2) 
    259           poly = Polygon(shell, (hole1, hole2)) 
    260         """ 
    261         self._ptr = GEOSPointer(0) # Initially NULL 
    262         self._parent = None 
    263         self._rings = {}  
     250        Below are some examples of initialization, where shell, hole1, and  
     251         hole2 are valid LinearRing geometries: 
     252         >>> poly = Polygon(shell, hole1, hole2) 
     253         >>> poly = Polygon(shell, (hole1, hole2)) 
     254        """ 
    264255        if not args: 
    265256            raise TypeError, 'Must provide at list one LinearRing instance to initialize Polygon.' 
     
    294285    def __del__(self): 
    295286        "Overloaded deletion method for Polygons." 
    296         #print 'polygon: Deleting %s (parent=%s, valid=%s)' % (self.__class__.__name__, self._parent, self._ptr.valid) 
    297         # If this geometry is still valid, it hasn't been modified by others. 
    298         if self._ptr.valid: 
    299             # Nulling the pointers to internal rings, preventing any attempted future access 
    300             for k in self._rings: self._rings[k].nullify() 
    301         elif not self._parent:  
    302             # Internal memory has become part of other objects; must delete the  
    303             #  internal objects which are still valid individually, since calling 
    304             #  destructor on entire geometry will result in an attempted deletion  
    305             #  of NULL pointers for the missing components.  Not performed on 
    306             #  children Polygons from MultiPolygon or GeometryCollection objects. 
    307             for k in self._rings: 
    308                 if self._rings[k].valid: 
    309                     lgeos.GEOSGeom_destroy(self._rings[k].address) 
    310                     self._rings[k].nullify() 
     287        #print 'polygon: Deleting %s (parent=%s, valid=%s)' % (self.__class__.__name__, self._ptr.parent, self._ptr.valid) 
     288        # Not performed on children Polygons from MultiPolygon or GeometryCollection objects. 
     289        if not self._ptr.child: 
     290            # If this geometry is still valid, it hasn't been modified by others. 
     291            if self._ptr.valid: 
     292                # Nulling the pointers to internal rings, preventing any  
     293                #  attempted future access. 
     294                for r in self._ptr: r.nullify() 
     295            else:  
     296                # Internal memory has become part of other Geometry objects; must  
     297                #  delete the internal objects which are still valid individually,  
     298                #  because calling the destructor on entire geometry will result  
     299                #  in an attempted deletion of NULL pointers for th