Added `reverse` and `force_rhr` methods to `GeoQuerySet`. Refs #12416.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@12349 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Justin Bronn 2010-01-29 02:20:58 +00:00
parent 1733518095
commit 5b21033847
7 changed files with 66 additions and 3 deletions

View File

@ -50,6 +50,7 @@ class BaseSpatialOperations(object):
perimeter3d = False perimeter3d = False
point_on_surface = False point_on_surface = False
polygonize = False polygonize = False
reverse = False
scale = False scale = False
snap_to_grid = False snap_to_grid = False
sym_difference = False sym_difference = False

View File

@ -88,6 +88,7 @@ class OracleOperations(DatabaseOperations, BaseSpatialOperations):
num_points = 'SDO_UTIL.GETNUMVERTICES' num_points = 'SDO_UTIL.GETNUMVERTICES'
perimeter = length perimeter = length
point_on_surface = 'SDO_GEOM.SDO_POINTONSURFACE' point_on_surface = 'SDO_GEOM.SDO_POINTONSURFACE'
reverse = 'SDO_UTIL.REVERSE_LINESTRING'
sym_difference = 'SDO_GEOM.SDO_XOR' sym_difference = 'SDO_GEOM.SDO_XOR'
transform = 'SDO_CS.TRANSFORM' transform = 'SDO_CS.TRANSFORM'
union = 'SDO_GEOM.SDO_UNION' union = 'SDO_GEOM.SDO_UNION'

View File

@ -252,6 +252,7 @@ class PostGISOperations(DatabaseOperations, BaseSpatialOperations):
self.envelope = prefix + 'Envelope' self.envelope = prefix + 'Envelope'
self.extent = prefix + 'Extent' self.extent = prefix + 'Extent'
self.extent3d = prefix + 'Extent3D' self.extent3d = prefix + 'Extent3D'
self.force_rhr = prefix + 'ForceRHR'
self.geohash = GEOHASH self.geohash = GEOHASH
self.geojson = GEOJSON self.geojson = GEOJSON
self.gml = prefix + 'AsGML' self.gml = prefix + 'AsGML'
@ -268,6 +269,7 @@ class PostGISOperations(DatabaseOperations, BaseSpatialOperations):
self.perimeter3d = prefix + 'Perimeter3D' self.perimeter3d = prefix + 'Perimeter3D'
self.point_on_surface = prefix + 'PointOnSurface' self.point_on_surface = prefix + 'PointOnSurface'
self.polygonize = prefix + 'Polygonize' self.polygonize = prefix + 'Polygonize'
self.reverse = prefix + 'Reverse'
self.scale = prefix + 'Scale' self.scale = prefix + 'Scale'
self.snap_to_grid = prefix + 'SnapToGrid' self.snap_to_grid = prefix + 'SnapToGrid'
self.svg = prefix + 'AsSVG' self.svg = prefix + 'AsSVG'

View File

@ -36,6 +36,9 @@ class GeoManager(Manager):
def extent3d(self, *args, **kwargs): def extent3d(self, *args, **kwargs):
return self.get_query_set().extent3d(*args, **kwargs) return self.get_query_set().extent3d(*args, **kwargs)
def force_rhr(self, *args, **kwargs):
return self.get_query_set().force_rhr(*args, **kwargs)
def geojson(self, *args, **kwargs): def geojson(self, *args, **kwargs):
return self.get_query_set().geojson(*args, **kwargs) return self.get_query_set().geojson(*args, **kwargs)
@ -69,6 +72,9 @@ class GeoManager(Manager):
def point_on_surface(self, *args, **kwargs): def point_on_surface(self, *args, **kwargs):
return self.get_query_set().point_on_surface(*args, **kwargs) return self.get_query_set().point_on_surface(*args, **kwargs)
def reverse(self, *args, **kwargs):
return self.get_query_set().reverse(*args, **kwargs)
def scale(self, *args, **kwargs): def scale(self, *args, **kwargs):
return self.get_query_set().scale(*args, **kwargs) return self.get_query_set().scale(*args, **kwargs)

View File

@ -2,7 +2,7 @@ from django.db import connections
from django.db.models.query import QuerySet, Q, ValuesQuerySet, ValuesListQuerySet from django.db.models.query import QuerySet, Q, ValuesQuerySet, ValuesListQuerySet
from django.contrib.gis.db.models import aggregates from django.contrib.gis.db.models import aggregates
from django.contrib.gis.db.models.fields import get_srid_info, GeometryField, PointField from django.contrib.gis.db.models.fields import get_srid_info, GeometryField, PointField, LineStringField
from django.contrib.gis.db.models.sql import AreaField, DistanceField, GeomField, GeoQuery, GeoWhereNode from django.contrib.gis.db.models.sql import AreaField, DistanceField, GeomField, GeoQuery, GeoWhereNode
from django.contrib.gis.geometry.backend import Geometry from django.contrib.gis.geometry.backend import Geometry
from django.contrib.gis.measure import Area, Distance from django.contrib.gis.measure import Area, Distance
@ -119,6 +119,15 @@ class GeoQuerySet(QuerySet):
""" """
return self._spatial_aggregate(aggregates.Extent3D, **kwargs) return self._spatial_aggregate(aggregates.Extent3D, **kwargs)
def force_rhr(self, **kwargs):
"""
Returns a modified version of the Polygon/MultiPolygon in which
all of the vertices follow the Right-Hand-Rule. By default,
this is attached as the `force_rhr` attribute on each element
of the GeoQuerySet.
"""
return self._geom_attribute('force_rhr', **kwargs)
def geojson(self, precision=8, crs=False, bbox=False, **kwargs): def geojson(self, precision=8, crs=False, bbox=False, **kwargs):
""" """
Returns a GeoJSON representation of the geomtry field in a `geojson` Returns a GeoJSON representation of the geomtry field in a `geojson`
@ -244,6 +253,16 @@ class GeoQuerySet(QuerySet):
""" """
return self._geom_attribute('point_on_surface', **kwargs) return self._geom_attribute('point_on_surface', **kwargs)
def reverse(self, **kwargs):
"""
Reverses the coordinate order of the geometry, and attaches as a
`reverse` attribute on each element of this GeoQuerySet.
"""
s = {'select_field' : GeomField(),}
if connections[self.db].ops.oracle:
s['geo_field_type'] = LineStringField
return self._spatial_attribute('reverse', s, **kwargs)
def scale(self, x, y, z=0.0, **kwargs): def scale(self, x, y, z=0.0, **kwargs):
""" """
Scales the geometry to a new size by multiplying the ordinates Scales the geometry to a new size by multiplying the ordinates
@ -489,7 +508,8 @@ class GeoQuerySet(QuerySet):
# Performing setup for the spatial column, unless told not to. # Performing setup for the spatial column, unless told not to.
if settings.get('setup', True): if settings.get('setup', True):
default_args, geo_field = self._spatial_setup(att, desc=settings['desc'], field_name=field_name) default_args, geo_field = self._spatial_setup(att, desc=settings['desc'], field_name=field_name,
geo_field_type=settings.get('geo_field_type', None))
for k, v in default_args.iteritems(): settings['procedure_args'].setdefault(k, v) for k, v in default_args.iteritems(): settings['procedure_args'].setdefault(k, v)
else: else:
geo_field = settings['geo_field'] geo_field = settings['geo_field']

View File

@ -27,6 +27,12 @@ class State(models.Model):
objects = models.GeoManager() objects = models.GeoManager()
def __unicode__(self): return self.name def __unicode__(self): return self.name
class Track(models.Model):
name = models.CharField(max_length=30)
line = models.LineStringField()
objects = models.GeoManager()
def __unicode__(self): return self.name
if not spatialite: if not spatialite:
class Feature(models.Model): class Feature(models.Model):
name = models.CharField(max_length=20) name = models.CharField(max_length=20)

View File

@ -8,7 +8,7 @@ from django.contrib.gis.tests.utils import \
mysql, oracle, postgis, spatialite mysql, oracle, postgis, spatialite
from django.test import TestCase from django.test import TestCase
from models import Country, City, PennsylvaniaCity, State from models import Country, City, PennsylvaniaCity, State, Track
if not spatialite: if not spatialite:
from models import Feature, MinusOneSRID from models import Feature, MinusOneSRID
@ -687,6 +687,33 @@ class GeoModelTest(TestCase):
ref = fromstr('MULTIPOLYGON(((12.4 43.87,12.45 43.87,12.45 44.1,12.5 44.1,12.5 43.87,12.45 43.87,12.4 43.87)))') ref = fromstr('MULTIPOLYGON(((12.4 43.87,12.45 43.87,12.45 44.1,12.5 44.1,12.5 43.87,12.45 43.87,12.4 43.87)))')
self.failUnless(ref.equals_exact(Country.objects.snap_to_grid(0.05, 0.23, 0.5, 0.17).get(name='San Marino').snap_to_grid, tol)) self.failUnless(ref.equals_exact(Country.objects.snap_to_grid(0.05, 0.23, 0.5, 0.17).get(name='San Marino').snap_to_grid, tol))
@no_mysql
@no_spatialite
def test28_reverse(self):
"Testing GeoQuerySet.reverse()."
coords = [ (-95.363151, 29.763374), (-95.448601, 29.713803) ]
Track.objects.create(name='Foo', line=LineString(coords))
t = Track.objects.reverse().get(name='Foo')
coords.reverse()
self.assertEqual(tuple(coords), t.reverse.coords)
if oracle:
self.assertRaises(TypeError, State.objects.reverse)
@no_mysql
@no_oracle
@no_spatialite
def test29_force_rhr(self):
"Testing GeoQuerySet.force_rhr()."
rings = ( ( (0, 0), (5, 0), (0, 5), (0, 0) ),
( (1, 1), (1, 3), (3, 1), (1, 1) ),
)
rhr_rings = ( ( (0, 0), (0, 5), (5, 0), (0, 0) ),
( (1, 1), (3, 1), (1, 3), (1, 1) ),
)
State.objects.create(name='Foo', poly=Polygon(*rings))
s = State.objects.force_rhr().get(name='Foo')
self.assertEqual(rhr_rings, s.force_rhr.coords)
from test_feeds import GeoFeedTest from test_feeds import GeoFeedTest
from test_regress import GeoRegressionTests from test_regress import GeoRegressionTests
from test_sitemaps import GeoSitemapTest from test_sitemaps import GeoSitemapTest