summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/gis_tests/data/rasters/textrasters.py6
-rw-r--r--tests/gis_tests/gis_migrations/migrations/0001_initial.py95
-rw-r--r--tests/gis_tests/gis_migrations/test_commands.py4
-rw-r--r--tests/gis_tests/gis_migrations/test_operations.py176
-rw-r--r--tests/gis_tests/models.py17
-rw-r--r--tests/gis_tests/rasterapp/__init__.py0
-rw-r--r--tests/gis_tests/rasterapp/models.py11
-rw-r--r--tests/gis_tests/rasterapp/test_rasterfield.py72
8 files changed, 272 insertions, 109 deletions
diff --git a/tests/gis_tests/data/rasters/textrasters.py b/tests/gis_tests/data/rasters/textrasters.py
index b12ce62afc..01b211b3ce 100644
--- a/tests/gis_tests/data/rasters/textrasters.py
+++ b/tests/gis_tests/data/rasters/textrasters.py
@@ -1,7 +1,11 @@
+"""
+Text-based test rasters
+"""
+
JSON_RASTER = """{
"srid": 4326,
"origin": [0, 0],
- "scale": [1, 1],
+ "scale": [-1, 1],
"skew": [0, 0],
"width": 5,
"height": 5,
diff --git a/tests/gis_tests/gis_migrations/migrations/0001_initial.py b/tests/gis_tests/gis_migrations/migrations/0001_initial.py
index bf2966ee79..6615fdf2c4 100644
--- a/tests/gis_tests/gis_migrations/migrations/0001_initial.py
+++ b/tests/gis_tests/gis_migrations/migrations/0001_initial.py
@@ -1,53 +1,70 @@
-from django.db import migrations, models
+from django.db import connection, migrations, models
from ...models import models as gis_models
+ops = [
+ migrations.CreateModel(
+ name='Neighborhood',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('name', models.CharField(max_length=100, unique=True)),
+ ('geom', gis_models.MultiPolygonField(srid=4326)),
+ ],
+ options={
+ 'required_db_features': ['gis_enabled'],
+ },
+ bases=(models.Model,),
+ ),
+ migrations.CreateModel(
+ name='Household',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('neighborhood', models.ForeignKey(to='gis_migrations.Neighborhood', to_field='id', null=True)),
+ ('address', models.CharField(max_length=100)),
+ ('zip_code', models.IntegerField(null=True, blank=True)),
+ ('geom', gis_models.PointField(srid=4326, geography=True)),
+ ],
+ options={
+ 'required_db_features': ['gis_enabled'],
+ },
+ bases=(models.Model,),
+ ),
+ migrations.CreateModel(
+ name='Family',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('name', models.CharField(max_length=100, unique=True)),
+ ],
+ options={
+ },
+ bases=(models.Model,),
+ ),
+ migrations.AddField(
+ model_name='household',
+ name='family',
+ field=models.ForeignKey(blank=True, to='gis_migrations.Family', null=True),
+ preserve_default=True,
+ )
+]
-class Migration(migrations.Migration):
- """
- Used for gis.specific migration tests.
- """
- operations = [
- migrations.CreateModel(
- name='Neighborhood',
- fields=[
- ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
- ('name', models.CharField(max_length=100, unique=True)),
- ('geom', gis_models.MultiPolygonField(srid=4326)),
- ],
- options={
- 'required_db_features': ['gis_enabled'],
- },
- bases=(models.Model,),
- ),
- migrations.CreateModel(
- name='Household',
- fields=[
- ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
- ('neighborhood', models.ForeignKey(to='gis_migrations.Neighborhood', to_field='id', null=True)),
- ('address', models.CharField(max_length=100)),
- ('zip_code', models.IntegerField(null=True, blank=True)),
- ('geom', gis_models.PointField(srid=4326, geography=True)),
- ],
- options={
- 'required_db_features': ['gis_enabled'],
- },
- bases=(models.Model,),
- ),
+if connection.features.gis_enabled and connection.features.supports_raster:
+ ops += [
migrations.CreateModel(
- name='Family',
+ name='Heatmap',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('name', models.CharField(max_length=100, unique=True)),
+ ('rast', gis_models.fields.RasterField(srid=4326)),
],
options={
},
bases=(models.Model,),
),
- migrations.AddField(
- model_name='household',
- name='family',
- field=models.ForeignKey(blank=True, to='gis_migrations.Family', null=True),
- preserve_default=True,
- ),
]
+
+
+class Migration(migrations.Migration):
+ """
+ Used for gis-specific migration tests.
+ """
+ operations = ops
diff --git a/tests/gis_tests/gis_migrations/test_commands.py b/tests/gis_tests/gis_migrations/test_commands.py
index b4fe597bca..77bbdf3349 100644
--- a/tests/gis_tests/gis_migrations/test_commands.py
+++ b/tests/gis_tests/gis_migrations/test_commands.py
@@ -39,12 +39,16 @@ class MigrateTests(TransactionTestCase):
self.assertTableExists("gis_migrations_neighborhood")
self.assertTableExists("gis_migrations_household")
self.assertTableExists("gis_migrations_family")
+ if connection.features.supports_raster:
+ self.assertTableExists("gis_migrations_heatmap")
# Unmigrate everything
call_command("migrate", "gis_migrations", "zero", verbosity=0)
# Make sure it's all gone
self.assertTableNotExists("gis_migrations_neighborhood")
self.assertTableNotExists("gis_migrations_household")
self.assertTableNotExists("gis_migrations_family")
+ if connection.features.supports_raster:
+ self.assertTableNotExists("gis_migrations_heatmap")
# Even geometry columns metadata
try:
GeoColumn = connection.ops.geometry_columns()
diff --git a/tests/gis_tests/gis_migrations/test_operations.py b/tests/gis_tests/gis_migrations/test_operations.py
index 7af8da74e5..680742f41d 100644
--- a/tests/gis_tests/gis_migrations/test_operations.py
+++ b/tests/gis_tests/gis_migrations/test_operations.py
@@ -1,14 +1,17 @@
from __future__ import unicode_literals
+from django.contrib.gis.db.models import fields
+from django.core.exceptions import ImproperlyConfigured
from django.db import connection, migrations, models
from django.db.migrations.migration import Migration
from django.db.migrations.state import ProjectState
-from django.test import TransactionTestCase, skipUnlessDBFeature
+from django.test import (
+ TransactionTestCase, skipIfDBFeature, skipUnlessDBFeature,
+)
from ..utils import mysql
if connection.features.gis_enabled:
- from django.contrib.gis.db.models import fields
try:
GeometryColumns = connection.ops.geometry_columns()
HAS_GEOMETRY_COLUMNS = True
@@ -16,13 +19,14 @@ if connection.features.gis_enabled:
HAS_GEOMETRY_COLUMNS = False
-@skipUnlessDBFeature("gis_enabled")
+@skipUnlessDBFeature('gis_enabled')
class OperationTests(TransactionTestCase):
- available_apps = ["gis_tests.gis_migrations"]
+ available_apps = ['gis_tests.gis_migrations']
def tearDown(self):
# Delete table after testing
- self.apply_operations('gis', self.current_state, [migrations.DeleteModel("Neighborhood")])
+ if hasattr(self, 'current_state'):
+ self.apply_operations('gis', self.current_state, [migrations.DeleteModel('Neighborhood')])
super(OperationTests, self).tearDown()
def get_table_description(self, table):
@@ -41,19 +45,19 @@ class OperationTests(TransactionTestCase):
with connection.schema_editor() as editor:
return migration.apply(project_state, editor)
- def set_up_test_model(self):
- operations = [migrations.CreateModel(
- "Neighborhood",
- [
- ("id", models.AutoField(primary_key=True)),
- ('name', models.CharField(max_length=100, unique=True)),
- ('geom', fields.MultiPolygonField(srid=4326)),
- ],
- )]
+ def set_up_test_model(self, force_raster_creation=False):
+ test_fields = [
+ ('id', models.AutoField(primary_key=True)),
+ ('name', models.CharField(max_length=100, unique=True)),
+ ('geom', fields.MultiPolygonField(srid=4326))
+ ]
+ if connection.features.supports_raster or force_raster_creation:
+ test_fields += [('rast', fields.RasterField(srid=4326))]
+ operations = [migrations.CreateModel('Neighborhood', test_fields)]
return self.apply_operations('gis', ProjectState(), operations)
def assertGeometryColumnsCount(self, expected_count):
- table_name = "gis_neighborhood"
+ table_name = 'gis_neighborhood'
if connection.features.uppercases_column_names:
table_name = table_name.upper()
self.assertEqual(
@@ -63,91 +67,137 @@ class OperationTests(TransactionTestCase):
expected_count
)
- def test_add_gis_field(self):
- """
- Tests the AddField operation with a GIS-enabled column.
- """
+ def assertSpatialIndexExists(self, table, column):
+ with connection.cursor() as cursor:
+ indexes = connection.introspection.get_indexes(cursor, table)
+ self.assertIn(column, indexes)
+
+ def alter_gis_model(self, migration_class, model_name, field_name,
+ blank=False, field_class=None):
project_state = self.set_up_test_model()
self.current_state = project_state
- operation = migrations.AddField(
- "Neighborhood",
- "path",
- fields.LineStringField(srid=4326),
- )
+ args = [model_name, field_name]
+ if field_class:
+ args.append(field_class(srid=4326, blank=blank))
+ operation = migration_class(*args)
new_state = project_state.clone()
- operation.state_forwards("gis", new_state)
+ operation.state_forwards('gis', new_state)
with connection.schema_editor() as editor:
- operation.database_forwards("gis", editor, project_state, new_state)
+ operation.database_forwards('gis', editor, project_state, new_state)
self.current_state = new_state
- self.assertColumnExists("gis_neighborhood", "path")
+
+ def test_add_geom_field(self):
+ """
+ Test the AddField operation with a geometry-enabled column.
+ """
+ self.alter_gis_model(migrations.AddField, 'Neighborhood',
+ 'path', False, fields.LineStringField)
+ self.assertColumnExists('gis_neighborhood', 'path')
# Test GeometryColumns when available
if HAS_GEOMETRY_COLUMNS:
self.assertGeometryColumnsCount(2)
+ # Test spatial indices when available
if self.has_spatial_indexes:
- with connection.cursor() as cursor:
- indexes = connection.introspection.get_indexes(cursor, "gis_neighborhood")
- self.assertIn('path', indexes)
+ self.assertSpatialIndexExists('gis_neighborhood', 'path')
- def test_add_blank_gis_field(self):
+ @skipUnlessDBFeature('supports_raster')
+ def test_add_raster_field(self):
+ """
+ Test the AddField operation with a raster-enabled column.
+ """
+ self.alter_gis_model(migrations.AddField, 'Neighborhood',
+ 'heatmap', False, fields.RasterField)
+ self.assertColumnExists('gis_neighborhood', 'heatmap')
+
+ # Test spatial indices when available
+ if self.has_spatial_indexes:
+ self.assertSpatialIndexExists('gis_neighborhood', 'heatmap')
+
+ @skipIfDBFeature('supports_raster')
+ def test_create_raster_model_on_db_without_raster_support(self):
+ """
+ Test creating a model with a raster field on a db without raster support.
+ """
+ msg = 'Raster fields require backends with raster support.'
+ with self.assertRaisesMessage(ImproperlyConfigured, msg):
+ self.set_up_test_model(True)
+
+ @skipIfDBFeature('supports_raster')
+ def test_add_raster_field_on_db_without_raster_support(self):
+ """
+ Test adding a raster field on a db without raster support.
+ """
+ msg = 'Raster fields require backends with raster support.'
+ with self.assertRaisesMessage(ImproperlyConfigured, msg):
+ self.alter_gis_model(
+ migrations.AddField, 'Neighborhood', 'heatmap',
+ False, fields.RasterField
+ )
+
+ def test_add_blank_geom_field(self):
"""
Should be able to add a GeometryField with blank=True.
"""
- project_state = self.set_up_test_model()
- self.current_state = project_state
- operation = migrations.AddField(
- "Neighborhood",
- "path",
- fields.LineStringField(blank=True, srid=4326),
- )
- new_state = project_state.clone()
- operation.state_forwards("gis", new_state)
- with connection.schema_editor() as editor:
- operation.database_forwards("gis", editor, project_state, new_state)
- self.current_state = new_state
- self.assertColumnExists("gis_neighborhood", "path")
+ self.alter_gis_model(migrations.AddField, 'Neighborhood',
+ 'path', True, fields.LineStringField)
+ self.assertColumnExists('gis_neighborhood', 'path')
# Test GeometryColumns when available
if HAS_GEOMETRY_COLUMNS:
self.assertGeometryColumnsCount(2)
+ # Test spatial indices when available
if self.has_spatial_indexes:
- with connection.cursor() as cursor:
- indexes = connection.introspection.get_indexes(cursor, "gis_neighborhood")
- self.assertIn('path', indexes)
+ self.assertSpatialIndexExists('gis_neighborhood', 'path')
- def test_remove_gis_field(self):
+ @skipUnlessDBFeature('supports_raster')
+ def test_add_blank_raster_field(self):
"""
- Tests the RemoveField operation with a GIS-enabled column.
+ Should be able to add a RasterField with blank=True.
"""
- project_state = self.set_up_test_model()
- self.current_state = project_state
- operation = migrations.RemoveField("Neighborhood", "geom")
- new_state = project_state.clone()
- operation.state_forwards("gis", new_state)
- with connection.schema_editor() as editor:
- operation.database_forwards("gis", editor, project_state, new_state)
- self.current_state = new_state
- self.assertColumnNotExists("gis_neighborhood", "geom")
+ self.alter_gis_model(migrations.AddField, 'Neighborhood',
+ 'heatmap', True, fields.RasterField)
+ self.assertColumnExists('gis_neighborhood', 'heatmap')
+
+ # Test spatial indices when available
+ if self.has_spatial_indexes:
+ self.assertSpatialIndexExists('gis_neighborhood', 'heatmap')
+
+ def test_remove_geom_field(self):
+ """
+ Test the RemoveField operation with a geometry-enabled column.
+ """
+ self.alter_gis_model(migrations.RemoveField, 'Neighborhood', 'geom')
+ self.assertColumnNotExists('gis_neighborhood', 'geom')
# Test GeometryColumns when available
if HAS_GEOMETRY_COLUMNS:
self.assertGeometryColumnsCount(0)
+ @skipUnlessDBFeature('supports_raster')
+ def test_remove_raster_field(self):
+ """
+ Test the RemoveField operation with a raster-enabled column.
+ """
+ self.alter_gis_model(migrations.RemoveField, 'Neighborhood', 'rast')
+ self.assertColumnNotExists('gis_neighborhood', 'rast')
+
def test_create_model_spatial_index(self):
self.current_state = self.set_up_test_model()
if not self.has_spatial_indexes:
- self.skipTest("No support for Spatial indexes")
+ self.skipTest('No support for Spatial indexes')
- with connection.cursor() as cursor:
- indexes = connection.introspection.get_indexes(cursor, "gis_neighborhood")
- self.assertIn('geom', indexes)
+ self.assertSpatialIndexExists('gis_neighborhood', 'geom')
+
+ if connection.features.supports_raster:
+ self.assertSpatialIndexExists('gis_neighborhood', 'rast')
@property
def has_spatial_indexes(self):
if mysql:
with connection.cursor() as cursor:
- return connection.introspection.supports_spatial_index(cursor, "gis_neighborhood")
+ return connection.introspection.supports_spatial_index(cursor, 'gis_neighborhood')
return True
diff --git a/tests/gis_tests/models.py b/tests/gis_tests/models.py
index cfb7f96656..f5c694e688 100644
--- a/tests/gis_tests/models.py
+++ b/tests/gis_tests/models.py
@@ -1,14 +1,18 @@
from django.core.exceptions import ImproperlyConfigured
+from django.db import models
+
+
+class DummyField(models.Field):
+ def __init__(self, dim=None, srid=None, geography=None, spatial_index=True, *args, **kwargs):
+ super(DummyField, self).__init__(*args, **kwargs)
try:
from django.contrib.gis.db import models
+ try:
+ models.RasterField()
+ except ImproperlyConfigured:
+ models.RasterField = DummyField
except ImproperlyConfigured:
- from django.db import models
-
- class DummyField(models.Field):
- def __init__(self, dim=None, srid=None, geography=None, *args, **kwargs):
- super(DummyField, self).__init__(*args, **kwargs)
-
models.GeoManager = models.Manager
models.GeometryField = DummyField
models.LineStringField = DummyField
@@ -16,3 +20,4 @@ except ImproperlyConfigured:
models.MultiPolygonField = DummyField
models.PointField = DummyField
models.PolygonField = DummyField
+ models.RasterField = DummyField
diff --git a/tests/gis_tests/rasterapp/__init__.py b/tests/gis_tests/rasterapp/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/gis_tests/rasterapp/__init__.py
diff --git a/tests/gis_tests/rasterapp/models.py b/tests/gis_tests/rasterapp/models.py
new file mode 100644
index 0000000000..d7cc4d15e2
--- /dev/null
+++ b/tests/gis_tests/rasterapp/models.py
@@ -0,0 +1,11 @@
+from ..models import models
+
+
+class RasterModel(models.Model):
+ rast = models.RasterField(null=True, srid=4326, spatial_index=True, blank=True)
+
+ class Meta:
+ required_db_features = ['supports_raster']
+
+ def __str__(self):
+ return str(self.id)
diff --git a/tests/gis_tests/rasterapp/test_rasterfield.py b/tests/gis_tests/rasterapp/test_rasterfield.py
new file mode 100644
index 0000000000..7b4413ae0d
--- /dev/null
+++ b/tests/gis_tests/rasterapp/test_rasterfield.py
@@ -0,0 +1,72 @@
+import json
+
+from django.contrib.gis.shortcuts import numpy
+from django.test import TransactionTestCase, skipUnlessDBFeature
+
+from ..data.rasters.textrasters import JSON_RASTER
+from .models import RasterModel
+
+
+@skipUnlessDBFeature('supports_raster')
+class RasterFieldTest(TransactionTestCase):
+ available_apps = ['gis_tests.rasterapp']
+
+ def test_field_null_value(self):
+ """
+ Test creating a model where the RasterField has a null value.
+ """
+ r = RasterModel.objects.create(rast=None)
+ r.refresh_from_db()
+ self.assertIsNone(r.rast)
+
+ def test_model_creation(self):
+ """
+ Test RasterField through a test model.
+ """
+ # Create model instance from JSON raster
+ r = RasterModel.objects.create(rast=JSON_RASTER)
+ r.refresh_from_db()
+ # Test raster metadata properties
+ self.assertEqual((5, 5), (r.rast.width, r.rast.height))
+ self.assertEqual([0.0, -1.0, 0.0, 0.0, 0.0, 1.0], r.rast.geotransform)
+ self.assertIsNone(r.rast.bands[0].nodata_value)
+ # Compare srs
+ self.assertEqual(r.rast.srs.srid, 4326)
+ # Compare pixel values
+ band = r.rast.bands[0].data()
+ # If numpy, convert result to list
+ if numpy:
+ band = band.flatten().tolist()
+ # Loop through rows in band data and assert single
+ # value is as expected.
+ self.assertEqual(
+ [
+ 0.0, 1.0, 2.0, 3.0, 4.0,
+ 5.0, 6.0, 7.0, 8.0, 9.0,
+ 10.0, 11.0, 12.0, 13.0, 14.0,
+ 15.0, 16.0, 17.0, 18.0, 19.0,
+ 20.0, 21.0, 22.0, 23.0, 24.0
+ ],
+ band
+ )
+
+ def test_implicit_raster_transformation(self):
+ """
+ Test automatic transformation of rasters with srid different from the
+ field srid.
+ """
+ # Parse json raster
+ rast = json.loads(JSON_RASTER)
+ # Update srid to another value
+ rast['srid'] = 3086
+ # Save model and get it from db
+ r = RasterModel.objects.create(rast=rast)
+ r.refresh_from_db()
+ # Confirm raster has been transformed to the default srid
+ self.assertEqual(r.rast.srs.srid, 4326)
+ # Confirm geotransform is in lat/lon
+ self.assertEqual(
+ r.rast.geotransform,
+ [-87.9298551266551, 9.459646421449934e-06, 0.0,
+ 23.94249275457565, 0.0, -9.459646421449934e-06]
+ )