mirror of
https://github.com/django/django.git
synced 2025-07-04 09:49:12 +00:00
gis: GEOS library has been re-factored to, changes include:
(1) allow write-access to Geometries (for future lazy-geometries) (2) Point, LineString, LinearRing have their own constructors (e.g., p = Point(5, 17)) (3) improved ctypes memory management (as part of writability enhancements) (4) improved tests and comments (5) numpy support git-svn-id: http://code.djangoproject.com/svn/django/branches/gis@5637 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
c31d20858d
commit
342dbc6738
162
django/contrib/gis/geos/GEOSCoordSeq.py
Normal file
162
django/contrib/gis/geos/GEOSCoordSeq.py
Normal file
@ -0,0 +1,162 @@
|
||||
from django.contrib.gis.geos.libgeos import lgeos
|
||||
from django.contrib.gis.geos.GEOSError import GEOSException, GEOSGeometryIndexError
|
||||
from ctypes import c_double, c_int, c_uint, byref
|
||||
|
||||
"""
|
||||
This module houses the GEOSCoordSeq object, and is used internally
|
||||
by GEOSGeometry to house the actual coordinates of the Point,
|
||||
LineString, and LinearRing geometries.
|
||||
"""
|
||||
|
||||
class GEOSCoordSeq(object):
|
||||
"The internal representation of a list of coordinates inside a Geometry."
|
||||
|
||||
#### Python 'magic' routines ####
|
||||
def __init__(self, ptr, z=False):
|
||||
"Initializes from a GEOS pointer."
|
||||
self._ptr = ptr
|
||||
self._z = z
|
||||
|
||||
def __iter__(self):
|
||||
"Iterates over each point in the coordinate sequence."
|
||||
for i in xrange(self.size):
|
||||
yield self.__getitem__(i)
|
||||
|
||||
def __len__(self):
|
||||
"Returns the number of points in the coordinate sequence."
|
||||
return int(self.size)
|
||||
|
||||
def __str__(self):
|
||||
"The string representation of the coordinate sequence."
|
||||
return str(self.tuple)
|
||||
|
||||
def __getitem__(self, index):
|
||||
"Can use the index [] operator to get coordinate sequence at an index."
|
||||
coords = [self.getX(index), self.getY(index)]
|
||||
if self.dims == 3 and self._z:
|
||||
coords.append(self.getZ(index))
|
||||
return tuple(coords)
|
||||
|
||||
def __setitem__(self, index, value):
|
||||
"Can use the index [] operator to set coordinate sequence at an index."
|
||||
if self.dims == 3 and self._z:
|
||||
n_args = 3
|
||||
set_3d = True
|
||||
else:
|
||||
n_args = 2
|
||||
set_3d = False
|
||||
if len(value) != n_args:
|
||||
raise TypeError, 'Dimension of value does not match.'
|
||||
self.setX(index, value[0])
|
||||
self.setY(index, value[1])
|
||||
if set_3d: self.setZ(index, value[2])
|
||||
|
||||
#### Internal Routines ####
|
||||
def _checkindex(self, index):
|
||||
"Checks the index."
|
||||
sz = self.size
|
||||
if (sz < 1) or (index < 0) or (index >= sz):
|
||||
raise GEOSGeometryIndexError, 'invalid GEOS Geometry index: %s' % str(index)
|
||||
|
||||
def _checkdim(self, dim):
|
||||
"Checks the given dimension."
|
||||
if dim < 0 or dim > 2:
|
||||
raise GEOSException, 'invalid ordinate dimension "%d"' % dim
|
||||
|
||||
#### Ordinate getting and setting routines ####
|
||||
def getOrdinate(self, dimension, index):
|
||||
"Gets the value for the given dimension and index."
|
||||
self._checkindex(index)
|
||||
self._checkdim(dimension)
|
||||
|
||||
# Wrapping the dimension, index
|
||||
dim = c_uint(dimension)
|
||||
idx = c_uint(index)
|
||||
|
||||
# 'd' is the value of the point, passed in by reference
|
||||
d = c_double()
|
||||
status = lgeos.GEOSCoordSeq_getOrdinate(self._ptr(), idx, dim, byref(d))
|
||||
if status == 0:
|
||||
raise GEOSException, 'could not retrieve %th ordinate at index: %s' % (str(dimension), str(index))
|
||||
return d.value
|
||||
|
||||
def setOrdinate(self, dimension, index, value):
|
||||
"Sets the value for the given dimension and index."
|
||||
self._checkindex(index)
|
||||
self._checkdim(dimension)
|
||||
|
||||
# Wrapping the dimension, index
|
||||
dim = c_uint(dimension)
|
||||
idx = c_uint(index)
|
||||
|
||||
# Setting the ordinate
|
||||
status = lgeos.GEOSCoordSeq_setOrdinate(self._ptr(), idx, dim, c_double(value))
|
||||
if status == 0:
|
||||
raise GEOSException, 'Could not set the ordinate for (dim, index): (%d, %d)' % (dimension, index)
|
||||
|
||||
def getX(self, index):
|
||||
"Get the X value at the index."
|
||||
return self.getOrdinate(0, index)
|
||||
|
||||
def setX(self, index, value):
|
||||
"Set X with the value at the given index."
|
||||
self.setOrdinate(0, index, value)
|
||||
|
||||
def getY(self, index):
|
||||
"Get the Y value at the given index."
|
||||
return self.getOrdinate(1, index)
|
||||
|
||||
def setY(self, index, value):
|
||||
"Set Y with the value at the given index."
|
||||
self.setOrdinate(1, index, value)
|
||||
|
||||
def getZ(self, index):
|
||||
"Get Z with the value at the given index."
|
||||
return self.getOrdinate(2, index)
|
||||
|
||||
def setZ(self, index, value):
|
||||
"Set Z with the value at the given index."
|
||||
self.setOrdinate(2, index, value)
|
||||
|
||||
### Dimensions ###
|
||||
@property
|
||||
def size(self):
|
||||
"Returns the size of this coordinate sequence."
|
||||
n = c_uint(0)
|
||||
status = lgeos.GEOSCoordSeq_getSize(self._ptr(), byref(n))
|
||||
if status == 0:
|
||||
raise GEOSException, 'Could not get CoordSeq size.'
|
||||
return n.value
|
||||
|
||||
@property
|
||||
def dims(self):
|
||||
"Returns the dimensions of this coordinate sequence."
|
||||
n = c_uint(0)
|
||||
status = lgeos.GEOSCoordSeq_getDimensions(self._ptr(), byref(n))
|
||||
if status == 0:
|
||||
raise GEOSException, 'Could not get CoordSeq dimensions.'
|
||||
return n.value
|
||||
|
||||
@property
|
||||
def hasz(self):
|
||||
"Inherits this from the parent geometry."
|
||||
return self._z
|
||||
|
||||
### Other Methods ###
|
||||
@property
|
||||
def clone(self):
|
||||
"Clones this coordinate sequence."
|
||||
pass
|
||||
|
||||
@property
|
||||
def tuple(self):
|
||||
"Returns a tuple version of this coordinate sequence."
|
||||
n = self.size
|
||||
if n == 1:
|
||||
return self.__getitem__(0)
|
||||
else:
|
||||
return tuple(self.__getitem__(i) for i in xrange(n))
|
||||
|
||||
# ctypes prototype for the Coordinate Sequence creation factory
|
||||
create_cs = lgeos.GEOSCoordSeq_create
|
||||
create_cs.argtypes = [c_uint, c_uint]
|
15
django/contrib/gis/geos/GEOSError.py
Normal file
15
django/contrib/gis/geos/GEOSError.py
Normal file
@ -0,0 +1,15 @@
|
||||
|
||||
class GEOSException(Exception):
|
||||
"The base GEOS exception, indicates a GEOS-related error."
|
||||
pass
|
||||
|
||||
class GEOSGeometryIndexError(GEOSException, KeyError):
|
||||
"""This exception is raised when an invalid index is encountered, and has
|
||||
the 'silent_variable_feature' attribute set to true. This ensures that
|
||||
django's templates proceed to use the next lookup type gracefully when
|
||||
an Exception is raised. Fixes ticket #4740.
|
||||
"""
|
||||
# "If, during the method lookup, a method raises an exception, the exception
|
||||
# will be propagated, unless the exception has an attribute silent_variable_failure
|
||||
# whose value is True." -- django template docs.
|
||||
silent_variable_failure = True
|
File diff suppressed because it is too large
Load Diff
27
django/contrib/gis/geos/LICENSE
Normal file
27
django/contrib/gis/geos/LICENSE
Normal file
@ -0,0 +1,27 @@
|
||||
Copyright (c) 2007, Justin Bronn
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of GEOSGeometry nor the names of its contributors may be used
|
||||
to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
@ -1,4 +1,36 @@
|
||||
from GEOSGeometry import GEOSGeometry, GEOSException
|
||||
"""
|
||||
The goal of this module is to be a ctypes wrapper around the GEOS library
|
||||
that will work on both *NIX and Windows systems. Specifically, this uses
|
||||
the GEOS C api.
|
||||
|
||||
I have several motivations for doing this:
|
||||
(1) The GEOS SWIG wrapper is no longer maintained, and requires the
|
||||
installation of SWIG.
|
||||
(2) The PCL implementation is over 2K+ lines of C and would make
|
||||
PCL a requisite package for the GeoDjango application stack.
|
||||
(3) Windows and Mac compatibility becomes substantially easier, and does not
|
||||
require the additional compilation of PCL or GEOS and SWIG -- all that
|
||||
is needed is a Win32 or Mac compiled GEOS C library (dll or dylib)
|
||||
in a location that Python can read (e.g. 'C:\Python25').
|
||||
|
||||
In summary, I wanted to wrap GEOS in a more maintainable and portable way using
|
||||
only Python and the excellent ctypes library (now standard in Python 2.5).
|
||||
|
||||
In the spirit of loose coupling, this library does not require Django or
|
||||
GeoDjango. Only the GEOS C library and ctypes are needed for the platform
|
||||
of your choice.
|
||||
|
||||
For more information about GEOS:
|
||||
http://geos.refractions.net
|
||||
|
||||
For more info about PCL and the discontinuation of the Python GEOS
|
||||
library see Sean Gillies' writeup (and subsequent update) at:
|
||||
http://zcologia.com/news/150/geometries-for-python/
|
||||
http://zcologia.com/news/429/geometries-for-python-update/
|
||||
"""
|
||||
|
||||
from GEOSGeometry import GEOSGeometry, Point, LineString, LinearRing, HAS_NUMPY
|
||||
from GEOSError import GEOSException
|
||||
|
||||
def hex_to_wkt(hex):
|
||||
"Converts HEXEWKB into WKT."
|
||||
|
106
django/contrib/gis/geos/libgeos.py
Normal file
106
django/contrib/gis/geos/libgeos.py
Normal file
@ -0,0 +1,106 @@
|
||||
"""
|
||||
This module houses the ctypes initialization procedures, as well
|
||||
as the notice and error handler function callbacks (get called
|
||||
when an error occurs in GEOS).
|
||||
"""
|
||||
|
||||
from django.contrib.gis.geos.GEOSError import GEOSException
|
||||
from ctypes import \
|
||||
c_char_p, c_int, c_size_t, c_ubyte, pointer, addressof, \
|
||||
CDLL, CFUNCTYPE, POINTER, Structure
|
||||
import os, sys
|
||||
|
||||
# NumPy supported?
|
||||
try:
|
||||
from numpy import array, ndarray
|
||||
HAS_NUMPY = True
|
||||
except ImportError:
|
||||
HAS_NUMPY = False
|
||||
|
||||
# Setting the appropriate name for the GEOS-C library, depending on which
|
||||
# OS and POSIX platform we're running.
|
||||
if os.name == 'nt':
|
||||
# Windows NT library
|
||||
lib_name = 'libgeos_c-1.dll'
|
||||
elif os.name == 'posix':
|
||||
platform = os.uname()[0] # Using os.uname()
|
||||
if platform in ('Linux', 'SunOS'):
|
||||
# Linux or Solaris shared library
|
||||
lib_name = 'libgeos_c.so'
|
||||
elif platform == 'Darwin':
|
||||
# Mac OSX Shared Library (Thanks Matt!)
|
||||
lib_name = 'libgeos_c.dylib'
|
||||
else:
|
||||
raise GEOSException, 'Unknown POSIX platform "%s"' % platform
|
||||
else:
|
||||
raise GEOSException, 'Unsupported OS "%s"' % os.name
|
||||
|
||||
# Getting the GEOS C library. The C interface (CDLL) is used for
|
||||
# both *NIX and Windows.
|
||||
# See the GEOS C API source code for more details on the library function calls:
|
||||
# http://geos.refractions.net/ro/doxygen_docs/html/geos__c_8h-source.html
|
||||
lgeos = CDLL(lib_name)
|
||||
|
||||
# The notice and error handler C function callback definitions.
|
||||
# Supposed to mimic the GEOS message handler (C below):
|
||||
# "typedef void (*GEOSMessageHandler)(const char *fmt, ...);"
|
||||
NOTICEFUNC = CFUNCTYPE(None, c_char_p, c_char_p)
|
||||
def notice_h(fmt, list, output_h=sys.stdout):
|
||||
output_h.write('GEOS_NOTICE: %s\n' % (fmt % list))
|
||||
notice_h = NOTICEFUNC(notice_h)
|
||||
|
||||
ERRORFUNC = CFUNCTYPE(None, c_char_p, c_char_p)
|
||||
def error_h(fmt, lst, output_h=sys.stderr):
|
||||
try:
|
||||
err_msg = fmt % lst
|
||||
except:
|
||||
err_msg = fmt
|
||||
output_h.write('GEOS_ERROR: %s\n' % err_msg)
|
||||
error_h = ERRORFUNC(error_h)
|
||||
|
||||
# The initGEOS routine should be called first, however, that routine takes
|
||||
# the notice and error functions as parameters. Here is the C code that
|
||||
# we need to wrap:
|
||||
# "extern void GEOS_DLL initGEOS(GEOSMessageHandler notice_function, GEOSMessageHandler error_function);"
|
||||
lgeos.initGEOS(notice_h, error_h)
|
||||
|
||||
class GEOSPointer(object):
|
||||
"""The GEOSPointer provides a layer of abstraction to accessing the values returned by
|
||||
GEOS geometry creation routines."""
|
||||
|
||||
### Python 'magic' routines ###
|
||||
def __init__(self, ptr):
|
||||
"Given a ctypes pointer(c_int)"
|
||||
if isinstance(ptr, int):
|
||||
self._ptr = pointer(c_int(ptr))
|
||||
else:
|
||||
raise TypeError, 'GEOSPointer object must initialize with an integer.'
|
||||
|
||||
def __call__(self):
|
||||
"""If the pointer is NULL, then an exception will be raised, otherwise the
|
||||
address value (a GEOSGeom_ptr) will be returned."""
|
||||
if self.valid: return self.address
|
||||
else: raise GEOSException, 'GEOS pointer no longer valid (was the parent geometry deleted?)'
|
||||
|
||||
### GEOSPointer Properties ###
|
||||
@property
|
||||
def address(self):
|
||||
"Returns the address of the GEOSPointer (represented as an integer)."
|
||||
return self._ptr.contents.value
|
||||
|
||||
@property
|
||||
def valid(self):
|
||||
"Tests whether this GEOSPointer is valid."
|
||||
if bool(self.address): return True
|
||||
else: return False
|
||||
|
||||
### GEOSPointer Methods ###
|
||||
def set(self, address):
|
||||
"Sets this pointer with the new address (represented as an integer)"
|
||||
if not isinstance(address, int):
|
||||
raise TypeError, 'GEOSPointer must be set with an address (an integer).'
|
||||
self._ptr.contents = c_int(address)
|
||||
|
||||
def nullify(self):
|
||||
"Nullify this geometry pointer (set the address to 0)."
|
||||
self.set(0)
|
@ -90,7 +90,11 @@ multipoints = (TestGeom('MULTIPOINT(10 10, 20 20 )', n_p=2, points=((10., 10.),
|
||||
|
||||
# LineStrings
|
||||
linestrings = (TestGeom('LINESTRING (60 180, 120 100, 180 180)', n_p=3, centroid=(120, 140), tup=((60, 180), (120, 100), (180, 180))),
|
||||
TestGeom('LINESTRING (0 0, 5 5, 10 5, 10 10)', n_p=4, centroid=(6.1611652351681556, 4.6966991411008934), tup=((0, 0), (5, 5), (10, 5), (10, 10)),)
|
||||
TestGeom('LINESTRING (0 0, 5 5, 10 5, 10 10)', n_p=4, centroid=(6.1611652351681556, 4.6966991411008934), tup=((0, 0), (5, 5), (10, 5), (10, 10)),),
|
||||
)
|
||||
|
||||
# Linear Rings
|
||||
linearrings = (TestGeom('LINEARRING (649899.3065171393100172 4176512.3807915160432458, 649902.7294133581453934 4176512.7834989596158266, 649906.5550170192727819 4176514.3942507002502680, 649910.5820134161040187 4176516.0050024418160319, 649914.4076170771149918 4176518.0184616246260703, 649917.2264131171396002 4176519.4278986593708396, 649920.0452871860470623 4176521.6427505780011415, 649922.0587463703704998 4176522.8507948759943247, 649924.2735982896992937 4176524.4616246484220028, 649926.2870574744883925 4176525.4683542405255139, 649927.8978092158213258 4176526.8777912775985897, 649929.3072462501004338 4176528.0858355751261115, 649930.1126611357321963 4176529.4952726080082357, 649927.4951798024121672 4176506.9444361114874482, 649899.3065171393100172 4176512.3807915160432458)', n_p=15),
|
||||
)
|
||||
|
||||
# MultiLineStrings
|
||||
@ -128,5 +132,12 @@ relate_geoms = ( (TestGeom('MULTIPOINT(80 70, 20 20, 200 170, 140 120)'),
|
||||
TestGeom('MULTILINESTRING((90 20, 170 100, 170 140), (130 140, 130 60, 90 20, 20 90, 90 20))'),
|
||||
'FF10F0102', True,),
|
||||
)
|
||||
#((TestGeom('POLYGON ((545 317, 617 379, 581 321, 545 317))')),
|
||||
# (TestGeom('POLYGON ((484 290, 558 359, 543 309, 484 290))'))),
|
||||
|
||||
buffer_geoms = ( (TestGeom('POINT(0 0)'),
|
||||
TestGeom('POLYGON ((5 0,4.903926402016153 -0.97545161008064,4.619397662556435 -1.913417161825447,4.157348061512728 -2.777851165098009,3.53553390593274 -3.535533905932735,2.777851165098015 -4.157348061512724,1.913417161825454 -4.619397662556431,0.975451610080648 -4.903926402016151,0.000000000000008 -5.0,-0.975451610080632 -4.903926402016154,-1.913417161825439 -4.619397662556437,-2.777851165098002 -4.157348061512732,-3.53553390593273 -3.535533905932746,-4.157348061512719 -2.777851165098022,-4.619397662556429 -1.913417161825462,-4.903926402016149 -0.975451610080656,-5.0 -0.000000000000016,-4.903926402016156 0.975451610080624,-4.619397662556441 1.913417161825432,-4.157348061512737 2.777851165097995,-3.535533905932752 3.535533905932723,-2.777851165098029 4.157348061512714,-1.913417161825468 4.619397662556426,-0.975451610080661 4.903926402016149,-0.000000000000019 5.0,0.975451610080624 4.903926402016156,1.913417161825434 4.61939766255644,2.777851165097998 4.157348061512735,3.535533905932727 3.535533905932748,4.157348061512719 2.777851165098022,4.619397662556429 1.91341716182546,4.90392640201615 0.975451610080652,5 0))'),
|
||||
5.0, 8),
|
||||
(TestGeom('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))'),
|
||||
TestGeom('POLYGON ((-2 0,-2 10,-1.961570560806461 10.390180644032258,-1.847759065022573 10.765366864730179,-1.662939224605091 11.111140466039204,-1.414213562373095 11.414213562373096,-1.111140466039204 11.662939224605092,-0.765366864730179 11.847759065022574,-0.390180644032256 11.961570560806461,0 12,10 12,10.390180644032256 11.961570560806461,10.765366864730179 11.847759065022574,11.111140466039204 11.66293922460509,11.414213562373096 11.414213562373096,11.66293922460509 11.111140466039204,11.847759065022574 10.765366864730179,11.961570560806461 10.390180644032256,12 10,12 0,11.961570560806461 -0.390180644032256,11.847759065022574 -0.76536686473018,11.66293922460509 -1.111140466039204,11.414213562373096 -1.414213562373095,11.111140466039204 -1.66293922460509,10.765366864730179 -1.847759065022573,10.390180644032256 -1.961570560806461,10 -2,0.0 -2.0,-0.390180644032255 -1.961570560806461,-0.765366864730177 -1.847759065022575,-1.1111404660392 -1.662939224605093,-1.41421356237309 -1.4142135623731,-1.662939224605086 -1.111140466039211,-1.84775906502257 -0.765366864730189,-1.961570560806459 -0.390180644032268,-2 0))'),
|
||||
2.0, 8),
|
||||
)
|
||||
|
||||
|
@ -1,39 +1,39 @@
|
||||
import unittest
|
||||
from copy import copy
|
||||
from django.contrib.gis.geos import GEOSGeometry, GEOSException
|
||||
from django.contrib.gis.geos import GEOSGeometry, GEOSException, Point, LineString, LinearRing, HAS_NUMPY
|
||||
from geometries import *
|
||||
|
||||
class GeosTest2(unittest.TestCase):
|
||||
if HAS_NUMPY:
|
||||
from numpy import array
|
||||
|
||||
def test0100_wkt(self):
|
||||
class GEOSTest(unittest.TestCase):
|
||||
|
||||
def test01a_wkt(self):
|
||||
"Testing WKT output."
|
||||
for g in wkt_out:
|
||||
geom = GEOSGeometry(g.wkt)
|
||||
self.assertEqual(g.ewkt, geom.wkt)
|
||||
|
||||
def test0101_hex(self):
|
||||
def test01b_hex(self):
|
||||
"Testing HEX output."
|
||||
for g in hex_wkt:
|
||||
geom = GEOSGeometry(g.wkt)
|
||||
self.assertEqual(g.hex, geom.hex)
|
||||
|
||||
def test0102_errors(self):
|
||||
def test01c_errors(self):
|
||||
"Testing the Error handlers."
|
||||
|
||||
print "\nBEGIN - expecting ParseError; safe to ignore.\n"
|
||||
print "\nBEGIN - expecting GEOS_ERROR; safe to ignore.\n"
|
||||
for err in errors:
|
||||
if err.hex:
|
||||
self.assertRaises(GEOSException, GEOSGeometry, err.wkt, 'hex')
|
||||
else:
|
||||
self.assertRaises(GEOSException, GEOSGeometry, err.wkt)
|
||||
print "\nEND - expecting ParseError; safe to ignore.\n"
|
||||
print "\nEND - expecting GEOS_ERROR; safe to ignore.\n"
|
||||
|
||||
|
||||
def test02_points(self):
|
||||
def test02a_points(self):
|
||||
"Testing Point objects."
|
||||
prev = GEOSGeometry('POINT(0 0)')
|
||||
|
||||
for p in points:
|
||||
# Creating the point from the WKT
|
||||
pnt = GEOSGeometry(p.wkt)
|
||||
self.assertEqual(pnt.geom_type, 'Point')
|
||||
self.assertEqual(pnt.geom_typeid, 0)
|
||||
@ -41,22 +41,39 @@ class GeosTest2(unittest.TestCase):
|
||||
self.assertEqual(p.y, pnt.y)
|
||||
self.assertEqual(True, pnt == GEOSGeometry(p.wkt))
|
||||
self.assertEqual(False, pnt == prev)
|
||||
prev = pnt
|
||||
|
||||
# Making sure that the point's X, Y components are what we expect
|
||||
self.assertAlmostEqual(p.x, pnt.tuple[0], 9)
|
||||
self.assertAlmostEqual(p.y, pnt.tuple[1], 9)
|
||||
|
||||
# Testing the third dimension, and getting the tuple arguments
|
||||
if hasattr(p, 'z'):
|
||||
self.assertEqual(True, pnt.hasz)
|
||||
self.assertEqual(p.z, pnt.z)
|
||||
self.assertEqual(p.z, pnt.tuple[2], 9)
|
||||
tup_args = (p.x, p.y, p.z)
|
||||
else:
|
||||
self.assertEqual(False, pnt.hasz)
|
||||
self.assertEqual(None, pnt.z)
|
||||
tup_args = (p.x, p.y)
|
||||
|
||||
|
||||
# Centroid operation on point should be point itself
|
||||
self.assertEqual(p.centroid, pnt.centroid.tuple)
|
||||
|
||||
def test02_multipoints(self):
|
||||
# Now testing the different constructors
|
||||
pnt2 = Point(tup_args) # e.g., Point((1, 2))
|
||||
pnt3 = Point(*tup_args) # e.g., Point(1, 2)
|
||||
self.assertEqual(True, pnt == pnt2)
|
||||
self.assertEqual(True, pnt == pnt3)
|
||||
|
||||
# Now testing setting the x and y
|
||||
pnt.y = 3.14
|
||||
pnt.x = 2.71
|
||||
self.assertEqual(3.14, pnt.y)
|
||||
self.assertEqual(2.71, pnt.x)
|
||||
prev = pnt # setting the previous geometry
|
||||
|
||||
def test02b_multipoints(self):
|
||||
"Testing MultiPoint objects."
|
||||
for mp in multipoints:
|
||||
mpnt = GEOSGeometry(mp.wkt)
|
||||
@ -74,80 +91,37 @@ class GeosTest2(unittest.TestCase):
|
||||
self.assertEqual(p.empty, False)
|
||||
self.assertEqual(p.valid, True)
|
||||
|
||||
def test03_polygons(self):
|
||||
"Testing Polygon objects."
|
||||
|
||||
prev = GEOSGeometry('POINT(0 0)')
|
||||
|
||||
for p in polygons:
|
||||
poly = GEOSGeometry(p.wkt)
|
||||
self.assertEqual(poly.geom_type, 'Polygon')
|
||||
self.assertEqual(poly.geom_typeid, 3)
|
||||
self.assertEqual(poly.empty, False)
|
||||
self.assertEqual(poly.ring, False)
|
||||
self.assertEqual(p.n_i, poly.num_interior_rings)
|
||||
self.assertEqual(p.n_i + 1, len(poly)) # Testing __len__
|
||||
self.assertEqual(p.n_p, poly.num_points)
|
||||
|
||||
# Area & Centroid
|
||||
self.assertAlmostEqual(p.area, poly.area, 9)
|
||||
self.assertAlmostEqual(p.centroid[0], poly.centroid.tuple[0], 9)
|
||||
self.assertAlmostEqual(p.centroid[1], poly.centroid.tuple[1], 9)
|
||||
|
||||
# Testing the geometry equivalence
|
||||
self.assertEqual(True, poly == GEOSGeometry(p.wkt))
|
||||
self.assertEqual(False, poly == prev)
|
||||
prev = poly
|
||||
|
||||
# Testing the exterior ring
|
||||
ring = poly.exterior_ring
|
||||
self.assertEqual(ring.geom_type, 'LinearRing')
|
||||
self.assertEqual(ring.geom_typeid, 2)
|
||||
if p.ext_ring_cs:
|
||||
self.assertEqual(p.ext_ring_cs, ring.tuple)
|
||||
self.assertEqual(p.ext_ring_cs, poly[0].tuple) # Testing __getitem__
|
||||
|
||||
def test03_multipolygons(self):
|
||||
"Testing MultiPolygon objects."
|
||||
|
||||
prev = GEOSGeometry('POINT (0 0)')
|
||||
|
||||
for mp in multipolygons:
|
||||
mpoly = GEOSGeometry(mp.wkt)
|
||||
self.assertEqual(mpoly.geom_type, 'MultiPolygon')
|
||||
self.assertEqual(mpoly.geom_typeid, 6)
|
||||
self.assertEqual(mp.valid, mpoly.valid)
|
||||
|
||||
if mp.valid:
|
||||
self.assertEqual(mp.num_geom, mpoly.num_geom)
|
||||
self.assertEqual(mp.n_p, mpoly.num_coords)
|
||||
self.assertEqual(mp.num_geom, len(mpoly))
|
||||
for p in mpoly:
|
||||
self.assertEqual(p.geom_type, 'Polygon')
|
||||
self.assertEqual(p.geom_typeid, 3)
|
||||
self.assertEqual(p.valid, True)
|
||||
|
||||
def test04_linestring(self):
|
||||
def test03a_linestring(self):
|
||||
"Testing LineString objects."
|
||||
|
||||
prev = GEOSGeometry('POINT(0 0)')
|
||||
|
||||
for l in linestrings:
|
||||
ls = GEOSGeometry(l.wkt)
|
||||
self.assertEqual(ls.geom_type, 'LineString')
|
||||
self.assertEqual(ls.geom_typeid, 1)
|
||||
self.assertEqual(ls.empty, False)
|
||||
self.assertEqual(ls.ring, False)
|
||||
self.assertEqual(l.centroid, ls.centroid.tuple)
|
||||
if hasattr(l, 'centroid'):
|
||||
self.assertEqual(l.centroid, ls.centroid.tuple)
|
||||
if hasattr(l, 'tup'):
|
||||
self.assertEqual(l.tup, ls.tuple)
|
||||
|
||||
self.assertEqual(True, ls == GEOSGeometry(l.wkt))
|
||||
self.assertEqual(False, ls == prev)
|
||||
|
||||
prev = ls
|
||||
|
||||
def test04_multilinestring(self):
|
||||
# Creating a LineString from a tuple, list, and numpy array
|
||||
ls2 = LineString(ls.tuple)
|
||||
self.assertEqual(ls, ls2)
|
||||
ls3 = LineString([list(tup) for tup in ls.tuple])
|
||||
self.assertEqual(ls, ls3)
|
||||
if HAS_NUMPY:
|
||||
ls4 = LineString(array(ls.tuple))
|
||||
self.assertEqual(ls, ls4)
|
||||
|
||||
def test03b_multilinestring(self):
|
||||
"Testing MultiLineString objects."
|
||||
|
||||
prev = GEOSGeometry('POINT(0 0)')
|
||||
|
||||
for l in multilinestrings:
|
||||
ml = GEOSGeometry(l.wkt)
|
||||
self.assertEqual(ml.geom_type, 'MultiLineString')
|
||||
@ -165,14 +139,152 @@ class GeosTest2(unittest.TestCase):
|
||||
self.assertEqual(ls.geom_typeid, 1)
|
||||
self.assertEqual(ls.empty, False)
|
||||
|
||||
#def test05_linearring(self):
|
||||
# "Testing LinearRing objects."
|
||||
# pass
|
||||
def test04a_linearring(self):
|
||||
"Testing LinearRing objects."
|
||||
for rr in linearrings:
|
||||
lr = GEOSGeometry(rr.wkt)
|
||||
self.assertEqual(lr.geom_type, 'LinearRing')
|
||||
self.assertEqual(lr.geom_typeid, 2)
|
||||
self.assertEqual(rr.n_p, len(lr))
|
||||
self.assertEqual(True, lr.valid)
|
||||
self.assertEqual(False, lr.empty)
|
||||
|
||||
# Creating a LinearRing from a tuple, list, and numpy array
|
||||
lr2 = LinearRing(lr.tuple)
|
||||
self.assertEqual(lr, lr2)
|
||||
lr3 = LinearRing([list(tup) for tup in lr.tuple])
|
||||
self.assertEqual(lr, lr3)
|
||||
if HAS_NUMPY:
|
||||
lr4 = LineString(array(lr.tuple))
|
||||
self.assertEqual(lr, lr4)
|
||||
|
||||
def test05a_polygons(self):
|
||||
"Testing Polygon objects."
|
||||
prev = GEOSGeometry('POINT(0 0)')
|
||||
for p in polygons:
|
||||
# Creating the Polygon, testing its properties.
|
||||
poly = GEOSGeometry(p.wkt)
|
||||
self.assertEqual(poly.geom_type, 'Polygon')
|
||||
self.assertEqual(poly.geom_typeid, 3)
|
||||
self.assertEqual(poly.empty, False)
|
||||
self.assertEqual(poly.ring, False)
|
||||
self.assertEqual(p.n_i, poly.num_interior_rings)
|
||||
self.assertEqual(p.n_i + 1, len(poly)) # Testing __len__
|
||||
self.assertEqual(p.n_p, poly.num_points)
|
||||
|
||||
# Area & Centroid
|
||||
self.assertAlmostEqual(p.area, poly.area, 9)
|
||||
self.assertAlmostEqual(p.centroid[0], poly.centroid.tuple[0], 9)
|
||||
self.assertAlmostEqual(p.centroid[1], poly.centroid.tuple[1], 9)
|
||||
|
||||
# Testing the geometry equivalence
|
||||
self.assertEqual(True, poly == GEOSGeometry(p.wkt))
|
||||
self.assertEqual(False, poly == prev) # Should not be equal to previous geometry
|
||||
|
||||
# Testing the exterior ring
|
||||
ring = poly.exterior_ring
|
||||
self.assertEqual(ring.geom_type, 'LinearRing')
|
||||
self.assertEqual(ring.geom_typeid, 2)
|
||||
if p.ext_ring_cs:
|
||||
self.assertEqual(p.ext_ring_cs, ring.tuple)
|
||||
self.assertEqual(p.ext_ring_cs, poly[0].tuple) # Testing __getitem__
|
||||
|
||||
# Testing __iter__
|
||||
for r in poly:
|
||||
self.assertEqual(ring.geom_type, 'LinearRing')
|
||||
self.assertEqual(ring.geom_typeid, 2)
|
||||
|
||||
# Setting the second point of the first ring (which should set the
|
||||
# first point of the polygon).
|
||||
prev = poly.clone() # Using clone() to get a copy of the current polygon
|
||||
self.assertEqual(True, poly == prev) # They clone should be equal to the first
|
||||
newval = (poly[0][1][0] + 5.0, poly[0][1][1] + 5.0) # really testing __getitem__ ([ring][point][tuple])
|
||||
try:
|
||||
poly[0][1] = ('cannot assign with', 'string values')
|
||||
except TypeError:
|
||||
pass
|
||||
poly[0][1] = newval # setting the second point in the polygon with the newvalue (based on the old)
|
||||
self.assertEqual(newval, poly[0][1]) # The point in the polygon should be the
|
||||
self.assertEqual(False, poly == prev) # Even different from the clone we just made
|
||||
|
||||
def test05b_multipolygons(self):
|
||||
"Testing MultiPolygon objects."
|
||||
print "\nBEGIN - expecting GEOS_NOTICE; safe to ignore.\n"
|
||||
prev = GEOSGeometry('POINT (0 0)')
|
||||
for mp in multipolygons:
|
||||
mpoly = GEOSGeometry(mp.wkt)
|
||||
self.assertEqual(mpoly.geom_type, 'MultiPolygon')
|
||||
self.assertEqual(mpoly.geom_typeid, 6)
|
||||
self.assertEqual(mp.valid, mpoly.valid)
|
||||
|
||||
if mp.valid:
|
||||
self.assertEqual(mp.num_geom, mpoly.num_geom)
|
||||
self.assertEqual(mp.n_p, mpoly.num_coords)
|
||||
self.assertEqual(mp.num_geom, len(mpoly))
|
||||
for p in mpoly:
|
||||
self.assertEqual(p.geom_type, 'Polygon')
|
||||
self.assertEqual(p.geom_typeid, 3)
|
||||
self.assertEqual(p.valid, True)
|
||||
print "\nEND - expecting GEOS_NOTICE; safe to ignore.\n"
|
||||
|
||||
def test06_memory_hijinks(self):
|
||||
"Testing Geometry __del__() in different scenarios"
|
||||
#### Memory issues with rings and polygons
|
||||
|
||||
# These tests are needed to ensure sanity with writable geometries.
|
||||
|
||||
# Getting a polygon with interior rings, and pulling out the interior rings
|
||||
poly = GEOSGeometry(polygons[1].wkt)
|
||||
ring1 = poly[0]
|
||||
ring2 = poly[1]
|
||||
|
||||
# These deletes should be 'harmless' since they are done on child geometries
|
||||
del ring1
|
||||
del ring2
|
||||
ring1 = poly[0]
|
||||
ring2 = poly[1]
|
||||
|
||||
# Deleting the polygon
|
||||
del poly
|
||||
|
||||
# Ensuring that trying to access the deleted memory (by getting the string
|
||||
# representation of the ring of a deleted polygon) raises a GEOSException
|
||||
# instead of something worse..
|
||||
self.assertRaises(GEOSException, str, ring1)
|
||||
self.assertRaises(GEOSException, str, ring2)
|
||||
|
||||
#### Memory issues with geometries from Geometry Collections
|
||||
mp = GEOSGeometry('MULTIPOINT(85 715, 235 1400, 4620 1711)')
|
||||
|
||||
# Getting the points
|
||||
pts = [p for p in mp]
|
||||
|
||||
# More 'harmless' child geometry deletes
|
||||
for p in pts: del p
|
||||
|
||||
# Cloning for comparisons
|
||||
clones = [p.clone() for p in pts]
|
||||
|
||||
for i in xrange(len(clones)):
|
||||
# Testing equivalence before & after modification
|
||||
self.assertEqual(True, pts[i] == clones[i]) # before
|
||||
pts[i].x = 3.14159
|
||||
pts[i].y = 2.71828
|
||||
self.assertEqual(False, pts[i] == clones[i]) # after
|
||||
self.assertEqual(3.14159, mp[i].x) # parent x,y should be modified
|
||||
self.assertEqual(2.71828, mp[i].y)
|
||||
|
||||
# Should raise GEOSException when trying to get geometries from the multipoint
|
||||
# after it has been deleted.
|
||||
del mp
|
||||
for p in pts:
|
||||
self.assertRaises(GEOSException, str, p)
|
||||
|
||||
def test08_coord_seq(self):
|
||||
"Testing Coordinate Sequence objects."
|
||||
for p in polygons:
|
||||
if p.ext_ring_cs:
|
||||
# Constructing the polygon and getting the coordinate sequence
|
||||
poly = GEOSGeometry(p.wkt)
|
||||
cs = poly.exterior_ring.coord_seq
|
||||
|
||||
@ -181,15 +293,19 @@ class GeosTest2(unittest.TestCase):
|
||||
|
||||
# Checks __getitem__ and __setitem__
|
||||
for i in xrange(len(p.ext_ring_cs)):
|
||||
c1 = p.ext_ring_cs[i]
|
||||
c2 = cs[i]
|
||||
c1 = p.ext_ring_cs[i] # Expected value
|
||||
c2 = cs[i] # Value from coordseq
|
||||
self.assertEqual(c1, c2)
|
||||
|
||||
# Constructing the test value to set the coordinate sequence with
|
||||
if len(c1) == 2: tset = (5, 23)
|
||||
else: tset = (5, 23, 8)
|
||||
cs[i] = tset
|
||||
|
||||
# Making sure every set point matches what we expect
|
||||
for j in range(len(tset)):
|
||||
cs[i] = tset
|
||||
self.assertEqual(tset, cs[i])
|
||||
self.assertEqual(tset[j], cs[i][j])
|
||||
|
||||
def test09_relate_pattern(self):
|
||||
"Testing relate() and relate_pattern()."
|
||||
@ -208,7 +324,6 @@ class GeosTest2(unittest.TestCase):
|
||||
|
||||
def test10_intersection(self):
|
||||
"Testing intersects() and intersection()."
|
||||
|
||||
for i in xrange(len(topology_geoms)):
|
||||
g_tup = topology_geoms[i]
|
||||
a = GEOSGeometry(g_tup[0].wkt)
|
||||
@ -239,10 +354,36 @@ class GeosTest2(unittest.TestCase):
|
||||
d2 = a.difference(b)
|
||||
self.assertEqual(d1, d2)
|
||||
|
||||
def test13_buffer(self):
|
||||
"Testing buffer()."
|
||||
for i in xrange(len(buffer_geoms)):
|
||||
g_tup = buffer_geoms[i]
|
||||
g = GEOSGeometry(g_tup[0].wkt)
|
||||
|
||||
# The buffer we expect
|
||||
exp_buf = GEOSGeometry(g_tup[1].wkt)
|
||||
|
||||
# Can't use a floating-point for the number of quadsegs.
|
||||
self.assertRaises(TypeError, g.buffer, g_tup[2], float(g_tup[3]))
|
||||
|
||||
# Constructing our buffer
|
||||
buf = g.buffer(g_tup[2], g_tup[3])
|
||||
self.assertEqual(exp_buf.num_coords, buf.num_coords)
|
||||
self.assertEqual(len(exp_buf), len(buf))
|
||||
|
||||
# Now assuring that each point in the buffer is almost equal
|
||||
for j in xrange(len(exp_buf)):
|
||||
exp_ring = exp_buf[j]
|
||||
buf_ring = buf[j]
|
||||
self.assertEqual(len(exp_ring), len(buf_ring))
|
||||
for k in xrange(len(exp_ring)):
|
||||
# Asserting the X, Y of each point are almost equal (due to floating point imprecision)
|
||||
self.assertAlmostEqual(exp_ring[k][0], buf_ring[k][0], 9)
|
||||
self.assertAlmostEqual(exp_ring[k][1], buf_ring[k][1], 9)
|
||||
|
||||
def suite():
|
||||
s = unittest.TestSuite()
|
||||
s.addTest(unittest.makeSuite(GeosTest2))
|
||||
s.addTest(unittest.makeSuite(GEOSTest))
|
||||
return s
|
||||
|
||||
def run(verbosity=2):
|
||||
|
Loading…
x
Reference in New Issue
Block a user