Loading django/contrib/gis/db/backends/base/features.py +2 −1 Original line number Diff line number Diff line Loading @@ -26,9 +26,10 @@ class BaseSpatialFeatures(object): supports_real_shape_operations = True # Can geometry fields be null? supports_null_geometries = True # Can the `distance`/`length` functions be applied on geodetic coordinate systems? # Can the the function be applied on geodetic coordinate systems? supports_distance_geodetic = True supports_length_geodetic = True supports_perimeter_geodetic = False # Is the database able to count vertices on polygons (with `num_points`)? supports_num_points_poly = True Loading django/contrib/gis/db/backends/oracle/features.py +1 −0 Original line number Diff line number Diff line Loading @@ -7,3 +7,4 @@ class DatabaseFeatures(BaseSpatialFeatures, OracleDatabaseFeatures): supports_add_srs_entry = False supports_geometry_field_introspection = False supports_geometry_field_unique_index = False supports_perimeter_geodetic = True django/contrib/gis/db/models/functions.py +14 −6 Original line number Diff line number Diff line Loading @@ -206,6 +206,9 @@ class Difference(OracleToleranceMixin, GeoFuncWithGeoParam): class DistanceResultMixin(object): def source_is_geography(self): return self.get_source_fields()[0].geography and self.srid == 4326 def convert_value(self, value, expression, connection, context): if value is None: return None Loading Loading @@ -236,9 +239,7 @@ class Distance(DistanceResultMixin, OracleToleranceMixin, GeoFuncWithGeoParam): def as_postgresql(self, compiler, connection): geo_field = GeometryField(srid=self.srid) # Fake field to get SRID info src_field = self.get_source_fields()[0] geography = src_field.geography and self.srid == 4326 if geography: if self.source_is_geography(): # Set parameters as geography if base field is geography for pos, expr in enumerate( self.source_expressions[self.geom_param_pos + 1:], start=self.geom_param_pos + 1): Loading Loading @@ -297,9 +298,7 @@ class Length(DistanceResultMixin, OracleToleranceMixin, GeoFunc): def as_postgresql(self, compiler, connection): geo_field = GeometryField(srid=self.srid) # Fake field to get SRID info src_field = self.get_source_fields()[0] geography = src_field.geography and self.srid == 4326 if geography: if self.source_is_geography(): self.source_expressions.append(Value(self.spheroid)) elif geo_field.geodetic(connection): # Geometry fields with geodetic (lon/lat) coordinates need length_spheroid Loading Loading @@ -346,11 +345,20 @@ class Perimeter(DistanceResultMixin, OracleToleranceMixin, GeoFunc): arity = 1 def as_postgresql(self, compiler, connection): geo_field = GeometryField(srid=self.srid) # Fake field to get SRID info if geo_field.geodetic(connection) and not self.source_is_geography(): raise NotImplementedError("ST_Perimeter cannot use a non-projected non-geography field.") dim = min(f.dim for f in self.get_source_fields()) if dim > 2: self.function = connection.ops.perimeter3d return super(Perimeter, self).as_sql(compiler, connection) def as_sqlite(self, compiler, connection): geo_field = GeometryField(srid=self.srid) # Fake field to get SRID info if geo_field.geodetic(connection): raise NotImplementedError("Perimeter cannot use a non-projected field.") return super(Perimeter, self).as_sql(compiler, connection) class PointOnSurface(OracleToleranceMixin, GeoFunc): arity = 1 Loading tests/gis_tests/distapp/tests.py +18 −0 Original line number Diff line number Diff line Loading @@ -444,6 +444,8 @@ Distance_Sphere(geom1, geom2) | N/A | OK (meter Distance_Spheroid(geom1, geom2, spheroid) | N/A | OK (meters) | N/A ST_Perimeter(geom1) | OK | :-( (degrees) | OK ================================ Distance functions on Spatialite Loading @@ -457,6 +459,8 @@ ST_Distance(geom1, geom2, use_ellipsoid=True) | N/A | OK ( ST_Distance(geom1, geom2, use_ellipsoid=False) | N/A | OK (meters), less accurate, quick Perimeter(geom1) | OK | :-( (degrees) ''' # NOQA Loading Loading @@ -688,6 +692,20 @@ class DistanceFunctionsTests(TestCase): for city in qs: self.assertEqual(0, city.perim.m) @skipUnlessDBFeature("has_Perimeter_function") def test_perimeter_geodetic(self): # Currently only Oracle supports calculating the perimeter on geodetic # geometries (without being transformed). qs1 = CensusZipcode.objects.annotate(perim=Perimeter('poly')) if connection.features.supports_perimeter_geodetic: self.assertAlmostEqual(qs1[0].perim.m, 18406.3818954314, 3) else: with self.assertRaises(NotImplementedError): list(qs1) # But should work fine when transformed to projected coordinates qs2 = CensusZipcode.objects.annotate(perim=Perimeter(Transform('poly', 32140))).filter(name='77002') self.assertAlmostEqual(qs2[0].perim.m, 18404.355, 3) @skipUnlessDBFeature("supports_null_geometries", "has_Area_function", "has_Distance_function") def test_measurement_null_fields(self): """ Loading Loading
django/contrib/gis/db/backends/base/features.py +2 −1 Original line number Diff line number Diff line Loading @@ -26,9 +26,10 @@ class BaseSpatialFeatures(object): supports_real_shape_operations = True # Can geometry fields be null? supports_null_geometries = True # Can the `distance`/`length` functions be applied on geodetic coordinate systems? # Can the the function be applied on geodetic coordinate systems? supports_distance_geodetic = True supports_length_geodetic = True supports_perimeter_geodetic = False # Is the database able to count vertices on polygons (with `num_points`)? supports_num_points_poly = True Loading
django/contrib/gis/db/backends/oracle/features.py +1 −0 Original line number Diff line number Diff line Loading @@ -7,3 +7,4 @@ class DatabaseFeatures(BaseSpatialFeatures, OracleDatabaseFeatures): supports_add_srs_entry = False supports_geometry_field_introspection = False supports_geometry_field_unique_index = False supports_perimeter_geodetic = True
django/contrib/gis/db/models/functions.py +14 −6 Original line number Diff line number Diff line Loading @@ -206,6 +206,9 @@ class Difference(OracleToleranceMixin, GeoFuncWithGeoParam): class DistanceResultMixin(object): def source_is_geography(self): return self.get_source_fields()[0].geography and self.srid == 4326 def convert_value(self, value, expression, connection, context): if value is None: return None Loading Loading @@ -236,9 +239,7 @@ class Distance(DistanceResultMixin, OracleToleranceMixin, GeoFuncWithGeoParam): def as_postgresql(self, compiler, connection): geo_field = GeometryField(srid=self.srid) # Fake field to get SRID info src_field = self.get_source_fields()[0] geography = src_field.geography and self.srid == 4326 if geography: if self.source_is_geography(): # Set parameters as geography if base field is geography for pos, expr in enumerate( self.source_expressions[self.geom_param_pos + 1:], start=self.geom_param_pos + 1): Loading Loading @@ -297,9 +298,7 @@ class Length(DistanceResultMixin, OracleToleranceMixin, GeoFunc): def as_postgresql(self, compiler, connection): geo_field = GeometryField(srid=self.srid) # Fake field to get SRID info src_field = self.get_source_fields()[0] geography = src_field.geography and self.srid == 4326 if geography: if self.source_is_geography(): self.source_expressions.append(Value(self.spheroid)) elif geo_field.geodetic(connection): # Geometry fields with geodetic (lon/lat) coordinates need length_spheroid Loading Loading @@ -346,11 +345,20 @@ class Perimeter(DistanceResultMixin, OracleToleranceMixin, GeoFunc): arity = 1 def as_postgresql(self, compiler, connection): geo_field = GeometryField(srid=self.srid) # Fake field to get SRID info if geo_field.geodetic(connection) and not self.source_is_geography(): raise NotImplementedError("ST_Perimeter cannot use a non-projected non-geography field.") dim = min(f.dim for f in self.get_source_fields()) if dim > 2: self.function = connection.ops.perimeter3d return super(Perimeter, self).as_sql(compiler, connection) def as_sqlite(self, compiler, connection): geo_field = GeometryField(srid=self.srid) # Fake field to get SRID info if geo_field.geodetic(connection): raise NotImplementedError("Perimeter cannot use a non-projected field.") return super(Perimeter, self).as_sql(compiler, connection) class PointOnSurface(OracleToleranceMixin, GeoFunc): arity = 1 Loading
tests/gis_tests/distapp/tests.py +18 −0 Original line number Diff line number Diff line Loading @@ -444,6 +444,8 @@ Distance_Sphere(geom1, geom2) | N/A | OK (meter Distance_Spheroid(geom1, geom2, spheroid) | N/A | OK (meters) | N/A ST_Perimeter(geom1) | OK | :-( (degrees) | OK ================================ Distance functions on Spatialite Loading @@ -457,6 +459,8 @@ ST_Distance(geom1, geom2, use_ellipsoid=True) | N/A | OK ( ST_Distance(geom1, geom2, use_ellipsoid=False) | N/A | OK (meters), less accurate, quick Perimeter(geom1) | OK | :-( (degrees) ''' # NOQA Loading Loading @@ -688,6 +692,20 @@ class DistanceFunctionsTests(TestCase): for city in qs: self.assertEqual(0, city.perim.m) @skipUnlessDBFeature("has_Perimeter_function") def test_perimeter_geodetic(self): # Currently only Oracle supports calculating the perimeter on geodetic # geometries (without being transformed). qs1 = CensusZipcode.objects.annotate(perim=Perimeter('poly')) if connection.features.supports_perimeter_geodetic: self.assertAlmostEqual(qs1[0].perim.m, 18406.3818954314, 3) else: with self.assertRaises(NotImplementedError): list(qs1) # But should work fine when transformed to projected coordinates qs2 = CensusZipcode.objects.annotate(perim=Perimeter(Transform('poly', 32140))).filter(name='77002') self.assertAlmostEqual(qs2[0].perim.m, 18404.355, 3) @skipUnlessDBFeature("supports_null_geometries", "has_Area_function", "has_Distance_function") def test_measurement_null_fields(self): """ Loading