Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge branch 'master' into schema-alteration

  • Loading branch information...
commit b31eea069cf9bc801c82a975307a9173527d78f2 2 parents 76d93a5 + f54a888
@andrewgodwin andrewgodwin authored
Showing with 2,460 additions and 1,346 deletions.
  1. +1 −1  .gitignore
  2. +2 −1  AUTHORS
  3. +1 −1  django/conf/global_settings.py
  4. +2 −2 django/contrib/admin/options.py
  5. +0 −1  django/contrib/admindocs/tests/__init__.py
  6. +1 −1  django/contrib/admindocs/views.py
  7. +1 −1  django/contrib/auth/forms.py
  8. +8 −5 django/contrib/auth/hashers.py
  9. +0 −15 django/contrib/auth/tests/__init__.py
  10. +21 −0 django/contrib/auth/tests/test_forms.py
  11. +1 −1  django/contrib/auth/tests/test_handlers.py
  12. +2 −2 django/contrib/auth/tests/test_hashers.py
  13. +1 −1  django/contrib/auth/tests/test_management.py
  14. +1 −1  django/contrib/comments/views/utils.py
  15. +1 −1  django/contrib/contenttypes/views.py
  16. +0 −6 django/contrib/flatpages/tests/__init__.py
  17. +0 −2  django/contrib/formtools/tests/__init__.py
  18. +1 −1  django/contrib/formtools/tests/urls.py
  19. +1 −1  django/contrib/formtools/tests/wizard/test_forms.py
  20. +12 −1 django/contrib/gis/db/models/fields.py
  21. +2 −2 django/contrib/gis/db/models/sql/compiler.py
  22. +4 −1 django/contrib/gis/forms/__init__.py
  23. +33 −2 django/contrib/gis/forms/fields.py
  24. +112 −0 django/contrib/gis/forms/widgets.py
  25. +4 −3 django/contrib/gis/gdal/__init__.py
  26. +0 −28 django/contrib/gis/gdal/tests/__init__.py
  27. +9 −10 django/contrib/gis/gdal/tests/test_driver.py
  28. +33 −35 django/contrib/gis/gdal/tests/test_ds.py
  29. +8 −10 django/contrib/gis/gdal/tests/test_envelope.py
  30. +9 −10 django/contrib/gis/gdal/tests/test_geom.py
  31. +7 −9 django/contrib/gis/gdal/tests/test_srs.py
  32. +15 −11 django/contrib/gis/geoip/tests.py
  33. +15 −9 django/contrib/gis/geos/__init__.py
  34. +0 −28 django/contrib/gis/geos/tests/__init__.py
  35. +23 −21 django/contrib/gis/geos/tests/test_geos.py
  36. +13 −14 django/contrib/gis/geos/tests/test_geos_mutation.py
  37. +7 −9 django/contrib/gis/geos/tests/test_io.py
  38. +0 −12 django/contrib/gis/geos/tests/test_mutable_list.py
  39. +1 −1  django/contrib/gis/sitemaps/views.py
  40. +371 −0 django/contrib/gis/static/gis/js/OLMapWidget.js
  41. +1 −1  django/contrib/gis/templates/gis/admin/openlayers.js
  42. +17 −0 django/contrib/gis/templates/gis/openlayers-osm.html
  43. +34 −0 django/contrib/gis/templates/gis/openlayers.html
  44. +2 −95 django/contrib/gis/tests/__init__.py
  45. +20 −11 django/contrib/gis/tests/distapp/tests.py
  46. +14 −5 django/contrib/gis/tests/geo3d/tests.py
  47. +10 −7 django/contrib/gis/tests/geoadmin/tests.py
  48. +6 −1 django/contrib/gis/tests/geoapp/test_feeds.py
  49. +8 −3 django/contrib/gis/tests/geoapp/test_regress.py
  50. +6 −1 django/contrib/gis/tests/geoapp/test_sitemaps.py
  51. +17 −10 django/contrib/gis/tests/geoapp/tests.py
  52. +8 −3 django/contrib/gis/tests/geogapp/tests.py
  53. +9 −3 django/contrib/gis/tests/inspectapp/tests.py
  54. +13 −7 django/contrib/gis/tests/layermap/tests.py
  55. +10 −5 django/contrib/gis/tests/relatedapp/tests.py
  56. +173 −16 django/contrib/gis/tests/test_geoforms.py
  57. +0 −1  django/contrib/gis/tests/test_spatialrefsys.py
  58. +9 −0 django/contrib/gis/tests/utils.py
  59. +1 −1  django/contrib/gis/utils/layermapping.py
  60. +0 −5 django/contrib/messages/tests/__init__.py
  61. +1 −2  django/contrib/messages/views.py
  62. +0 −4 django/contrib/sitemaps/tests/__init__.py
  63. +1 −1  django/contrib/staticfiles/management/commands/collectstatic.py
  64. +2 −6 django/core/files/images.py
  65. +1 −1  django/core/files/storage.py
  66. +1 −1  django/core/management/__init__.py
  67. +2 −2 django/core/management/base.py
  68. +1 −1  django/core/management/commands/createcachetable.py
  69. +2 −2 django/core/management/commands/runserver.py
  70. +2 −6 django/core/management/validation.py
  71. +1 −3 django/core/serializers/base.py
  72. +5 −5 django/db/models/base.py
  73. +2 −2 django/db/models/fields/__init__.py
  74. +1 −1  django/db/models/options.py
  75. +1 −1  django/db/models/query.py
  76. +1 −1  django/db/models/related.py
  77. +2 −2 django/db/models/sql/aggregates.py
  78. +16 −16 django/db/models/sql/compiler.py
  79. +6 −6 django/db/models/sql/query.py
  80. +6 −6 django/db/models/sql/subqueries.py
  81. +4 −12 django/forms/fields.py
  82. +1 −1  django/forms/forms.py
  83. +1 −1  django/forms/formsets.py
  84. +1 −1  django/forms/widgets.py
  85. +1 −1  django/http/multipartparser.py
  86. +1 −1  django/template/base.py
  87. +2 −2 django/template/defaulttags.py
  88. +7 −0 django/test/_doctest.py
  89. +289 −0 django/test/runner.py
  90. +15 −200 django/test/simple.py
  91. +10 −0 django/test/testcases.py
  92. +1 −1  django/utils/dateformat.py
  93. +4 −1 django/utils/html.py
  94. +148 −0 django/utils/image.py
  95. +1 −1  django/utils/log.py
  96. +4 −1 django/utils/translation/trans_real.py
  97. +1 −1  django/utils/tree.py
  98. +1 −1  django/utils/unittest/loader.py
  99. +1 −1  docs/faq/contributing.txt
  100. +1 −2  docs/index.txt
  101. +31 −12 docs/internals/committers.txt
  102. +16 −1 docs/internals/deprecation.txt
  103. +42 −28 docs/intro/tutorial04.txt
  104. +38 −44 docs/intro/tutorial05.txt
  105. +1 −1  docs/ref/class-based-views/base.txt
  106. +4 −3 docs/ref/contrib/admin/admindocs.txt
  107. +165 −0 docs/ref/contrib/gis/forms-api.txt
  108. +1 −0  docs/ref/contrib/gis/index.txt
  109. +9 −48 docs/ref/contrib/gis/testing.txt
  110. +40 −1 docs/ref/django-admin.txt
  111. +9 −5 docs/ref/forms/fields.txt
  112. +3 −0  docs/ref/models/fields.txt
  113. +2 −2 docs/ref/models/querysets.txt
  114. +6 −1 docs/ref/settings.txt
  115. +2 −2 docs/ref/utils.txt
  116. +1 −1  docs/releases/1.2.4.txt
  117. +1 −1  docs/releases/1.3.txt
  118. +71 −0 docs/releases/1.6.txt
  119. +2 −1  docs/topics/auth/customizing.txt
  120. +5 −0 docs/topics/auth/default.txt
  121. +3 −3 docs/topics/auth/passwords.txt
  122. +22 −18 docs/topics/db/transactions.txt
  123. +2 −0  docs/topics/i18n/timezones.txt
  124. +3 −1 docs/topics/i18n/translation.txt
  125. +1 −1  docs/topics/serialization.txt
  126. +44 −33 docs/topics/testing/advanced.txt
  127. +0 −81 docs/topics/testing/doctests.txt
  128. +3 −74 docs/topics/testing/index.txt
  129. +63 −79 docs/topics/testing/overview.txt
  130. +4 −6 extras/csrf_migration_helper.py
  131. +3 −3 tests/admin_scripts/tests.py
  132. +14 −2 tests/admin_views/tests.py
  133. +7 −2 tests/backends/tests.py
  134. +7 −13 tests/file_storage/tests.py
  135. +4 −0 tests/generic_relations/models.py
  136. +6 −1 tests/generic_relations/tests.py
  137. +0 −11 tests/generic_views/tests.py
  138. +0 −6 tests/i18n/tests.py
  139. +6 −11 tests/model_fields/models.py
  140. +6 −2 tests/model_fields/test_imagefield.py
  141. +0 −5 tests/model_fields/tests.py
  142. +3 −9 tests/model_forms/models.py
  143. +56 −38 tests/runtests.py
  144. +1 −1  tests/serializers_regress/models.py
  145. +0 −9 tests/template_tests/tests.py
  146. 0  tests/{test_runner/invalid_app/models → test_discovery_sample}/__init__.py
  147. +7 −0 tests/test_discovery_sample/pattern_tests.py
  148. 0  tests/{test_runner/deprecation_app → test_discovery_sample/tests}/__init__.py
  149. +7 −0 tests/test_discovery_sample/tests/tests.py
  150. +22 −0 tests/test_discovery_sample/tests_sample.py
  151. 0  tests/test_discovery_sample2/__init__.py
  152. +7 −0 tests/test_discovery_sample2/tests.py
  153. +68 −0 tests/test_runner/test_discover_runner.py
  154. +13 −12 tests/test_runner/tests.py
  155. 0  tests/test_runner_deprecation_app/__init__.py
  156. 0  tests/{test_runner/deprecation_app → test_runner_deprecation_app}/models.py
  157. +0 −2  tests/{test_runner/deprecation_app → test_runner_deprecation_app}/tests.py
  158. 0  tests/{test_runner/invalid_app → test_runner_invalid_app}/__init__.py
  159. 0  tests/test_runner_invalid_app/models/__init__.py
  160. 0  tests/{test_runner/invalid_app → test_runner_invalid_app}/tests/__init__.py
  161. +0 −34 tests/utils_tests/tests.py
  162. +0 −6 tests/validation/tests.py
View
2  .gitignore
@@ -5,4 +5,4 @@ MANIFEST
dist/
docs/_build/
tests/coverage_html/
-tests/.coverage
+tests/.coverage
View
3  AUTHORS
@@ -36,6 +36,7 @@ The PRIMARY AUTHORS are (and/or have been):
* Preston Holmes
* Simon Charette
* Donald Stufft
+ * Marc Tamlyn
More information on the main contributors to Django can be found in
docs/internals/committers.txt.
@@ -121,6 +122,7 @@ answer newbie questions, and generally made Django that much better:
Chris Cahoon <chris.cahoon@gmail.com>
Juan Manuel Caicedo <juan.manuel.caicedo@gmail.com>
Trevor Caira <trevor@caira.com>
+ Aaron Cannon <cannona@fireantproductions.com>
Brett Cannon <brett@python.org>
Ricardo Javier Cárdenes Medina <ricardo.cardenes@gmail.com>
Jeremy Carbaugh <jcarbaugh@gmail.com>
@@ -539,7 +541,6 @@ answer newbie questions, and generally made Django that much better:
Aaron Swartz <http://www.aaronsw.com/>
Ville Säävuori <http://www.unessa.net/>
Mart Sõmermaa <http://mrts.pri.ee/>
- Marc Tamlyn
Christian Tanzer <tanzer@swing.co.at>
Tyler Tarabula <tyler.tarabula@gmail.com>
Tyson Tate <tyson@fallingbullets.com>
View
2  django/conf/global_settings.py
@@ -576,7 +576,7 @@
###########
# The name of the class to use to run the test suite
-TEST_RUNNER = 'django.test.simple.DjangoTestSuiteRunner'
+TEST_RUNNER = 'django.test.runner.DiscoverRunner'
############
# FIXTURES #
View
4 django/contrib/admin/options.py
@@ -37,7 +37,7 @@
HORIZONTAL, VERTICAL = 1, 2
# returns the <ul> class for a given radio_admin field
-get_ul_class = lambda x: 'radiolist%s' % ((x == HORIZONTAL) and ' inline' or '')
+get_ul_class = lambda x: 'radiolist%s' % (' inline' if x == HORIZONTAL else '')
class IncorrectLookupParameters(Exception):
@@ -189,7 +189,7 @@ def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
kwargs['widget'] = widgets.AdminRadioSelect(attrs={
'class': get_ul_class(self.radio_fields[db_field.name]),
})
- kwargs['empty_label'] = db_field.blank and _('None') or None
+ kwargs['empty_label'] = _('None') if db_field.blank else None
queryset = self.get_field_queryset(db, db_field, request)
if queryset is not None:
View
1  django/contrib/admindocs/tests/__init__.py
@@ -1 +0,0 @@
-from .test_fields import TestFieldType
View
2  django/contrib/admindocs/views.py
@@ -267,7 +267,7 @@ def model_detail(request, app_label, model_name):
return render_to_response('admin_doc/model_detail.html', {
'root_path': urlresolvers.reverse('admin:index'),
'name': '%s.%s' % (opts.app_label, opts.object_name),
- 'summary': _("Fields on %s objects") % opts.object_name,
+ 'summary': _("Attributes on %s objects") % opts.object_name,
'description': model.__doc__,
'fields': fields,
}, context_instance=RequestContext(request))
View
2  django/contrib/auth/forms.py
@@ -171,7 +171,7 @@ def __init__(self, request=None, *args, **kwargs):
# Set the label for the "username" field.
UserModel = get_user_model()
self.username_field = UserModel._meta.get_field(UserModel.USERNAME_FIELD)
- if not self.fields['username'].label:
+ if self.fields['username'].label is None:
self.fields['username'].label = capfirst(self.username_field.verbose_name)
def clean(self):
View
13 django/contrib/auth/hashers.py
@@ -9,7 +9,7 @@
from django.test.signals import setting_changed
from django.utils import importlib
from django.utils.datastructures import SortedDict
-from django.utils.encoding import force_bytes, force_str
+from django.utils.encoding import force_bytes, force_str, force_text
from django.core.exceptions import ImproperlyConfigured
from django.utils.crypto import (
pbkdf2, constant_time_compare, get_random_string)
@@ -263,13 +263,13 @@ class BCryptSHA256PasswordHasher(BasePasswordHasher):
Secure password hashing using the bcrypt algorithm (recommended)
This is considered by many to be the most secure algorithm but you
- must first install the py-bcrypt library. Please be warned that
+ must first install the bcrypt library. Please be warned that
this library depends on native C code and might cause portability
issues.
"""
algorithm = "bcrypt_sha256"
digest = hashlib.sha256
- library = ("py-bcrypt", "bcrypt")
+ library = ("bcrypt", "bcrypt")
rounds = 12
def salt(self):
@@ -291,7 +291,7 @@ def encode(self, password, salt):
password = force_bytes(password)
data = bcrypt.hashpw(password, salt)
- return "%s$%s" % (self.algorithm, data)
+ return "%s$%s" % (self.algorithm, force_text(data))
def verify(self, password, encoded):
algorithm, data = encoded.split('$', 1)
@@ -307,6 +307,9 @@ def verify(self, password, encoded):
else:
password = force_bytes(password)
+ # Ensure that our data is a bytestring
+ data = force_bytes(data)
+
return constant_time_compare(data, bcrypt.hashpw(password, data))
def safe_summary(self, encoded):
@@ -326,7 +329,7 @@ class BCryptPasswordHasher(BCryptSHA256PasswordHasher):
Secure password hashing using the bcrypt algorithm
This is considered by many to be the most secure algorithm but you
- must first install the py-bcrypt library. Please be warned that
+ must first install the bcrypt library. Please be warned that
this library depends on native C code and might cause portability
issues.
View
15 django/contrib/auth/tests/__init__.py
@@ -1,16 +1 @@
-from django.contrib.auth.tests.test_custom_user import *
-from django.contrib.auth.tests.test_auth_backends import *
-from django.contrib.auth.tests.test_basic import *
-from django.contrib.auth.tests.test_context_processors import *
-from django.contrib.auth.tests.test_decorators import *
-from django.contrib.auth.tests.test_forms import *
-from django.contrib.auth.tests.test_remote_user import *
-from django.contrib.auth.tests.test_management import *
-from django.contrib.auth.tests.test_models import *
-from django.contrib.auth.tests.test_handlers import *
-from django.contrib.auth.tests.test_hashers import *
-from django.contrib.auth.tests.test_signals import *
-from django.contrib.auth.tests.test_tokens import *
-from django.contrib.auth.tests.test_views import *
-
# The password for the fixture data users is 'password'
View
21 django/contrib/auth/tests/test_forms.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
import os
+
+from django.contrib.auth import get_user_model
from django.contrib.auth.models import User
from django.contrib.auth.forms import (UserCreationForm, AuthenticationForm,
PasswordChangeForm, SetPasswordForm, UserChangeForm, PasswordResetForm,
@@ -13,6 +15,7 @@
from django.utils.encoding import force_text
from django.utils._os import upath
from django.utils import translation
+from django.utils.text import capfirst
from django.utils.translation import ugettext as _
@@ -146,6 +149,24 @@ class CustomAuthenticationForm(AuthenticationForm):
form = CustomAuthenticationForm()
self.assertEqual(form['username'].label, "Name")
+ def test_username_field_label_not_set(self):
+
+ class CustomAuthenticationForm(AuthenticationForm):
+ username = CharField()
+
+ form = CustomAuthenticationForm()
+ UserModel = get_user_model()
+ username_field = UserModel._meta.get_field(UserModel.USERNAME_FIELD)
+ self.assertEqual(form.fields['username'].label, capfirst(username_field.verbose_name))
+
+ def test_username_field_label_empty_string(self):
+
+ class CustomAuthenticationForm(AuthenticationForm):
+ username = CharField(label='')
+
+ form = CustomAuthenticationForm()
+ self.assertEqual(form.fields['username'].label, "")
+
@skipIfCustomUser
@override_settings(USE_TZ=False, PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
View
2  django/contrib/auth/tests/test_handlers.py
@@ -2,7 +2,7 @@
from django.contrib.auth.handlers.modwsgi import check_password, groups_for_user
from django.contrib.auth.models import User, Group
-from django.contrib.auth.tests import CustomUser
+from django.contrib.auth.tests.test_custom_user import CustomUser
from django.contrib.auth.tests.utils import skipIfCustomUser
from django.test import TransactionTestCase
from django.test.utils import override_settings
View
4 django/contrib/auth/tests/test_hashers.py
@@ -92,7 +92,7 @@ def test_crypt(self):
self.assertFalse(check_password('lètmeiz', encoded))
self.assertEqual(identify_hasher(encoded).algorithm, "crypt")
- @skipUnless(bcrypt, "py-bcrypt not installed")
+ @skipUnless(bcrypt, "bcrypt not installed")
def test_bcrypt_sha256(self):
encoded = make_password('lètmein', hasher='bcrypt_sha256')
self.assertTrue(is_password_usable(encoded))
@@ -108,7 +108,7 @@ def test_bcrypt_sha256(self):
self.assertTrue(check_password(password, encoded))
self.assertFalse(check_password(password[:72], encoded))
- @skipUnless(bcrypt, "py-bcrypt not installed")
+ @skipUnless(bcrypt, "bcrypt not installed")
def test_bcrypt(self):
encoded = make_password('lètmein', hasher='bcrypt')
self.assertTrue(is_password_usable(encoded))
View
2  django/contrib/auth/tests/test_management.py
@@ -5,7 +5,7 @@
from django.contrib.auth.management import create_permissions
from django.contrib.auth.management.commands import changepassword
from django.contrib.auth.models import User
-from django.contrib.auth.tests import CustomUser
+from django.contrib.auth.tests.test_custom_user import CustomUser
from django.contrib.auth.tests.utils import skipIfCustomUser
from django.core.management import call_command
from django.core.management.base import CommandError
View
2  django/contrib/comments/views/utils.py
@@ -37,7 +37,7 @@ def next_redirect(request, fallback, **get_kwargs):
else:
anchor = ''
- joiner = ('?' in next) and '&' or '?'
+ joiner = '&' if '?' in next else '?'
next += joiner + urlencode(get_kwargs) + anchor
return HttpResponseRedirect(next)
View
2  django/contrib/contenttypes/views.py
@@ -75,7 +75,7 @@ def shortcut(request, content_type_id, object_id):
# If all that malarkey found an object domain, use it. Otherwise, fall back
# to whatever get_absolute_url() returned.
if object_domain is not None:
- protocol = request.is_secure() and 'https' or 'http'
+ protocol = 'https' if request.is_secure() else 'http'
return http.HttpResponseRedirect('%s://%s%s'
% (protocol, object_domain, absurl))
else:
View
6 django/contrib/flatpages/tests/__init__.py
@@ -1,6 +0,0 @@
-from django.contrib.flatpages.tests.test_csrf import *
-from django.contrib.flatpages.tests.test_forms import *
-from django.contrib.flatpages.tests.test_models import *
-from django.contrib.flatpages.tests.test_middleware import *
-from django.contrib.flatpages.tests.test_templatetags import *
-from django.contrib.flatpages.tests.test_views import *
View
2  django/contrib/formtools/tests/__init__.py
@@ -1,2 +0,0 @@
-from django.contrib.formtools.tests.tests import *
-from django.contrib.formtools.tests.wizard import *
View
2  django/contrib/formtools/tests/urls.py
@@ -5,7 +5,7 @@
from __future__ import absolute_import
from django.conf.urls import patterns, url
-from django.contrib.formtools.tests import TestFormPreview
+from django.contrib.formtools.tests.tests import TestFormPreview
from django.contrib.formtools.tests.forms import TestForm
View
2  django/contrib/formtools/tests/wizard/test_forms.py
@@ -17,7 +17,7 @@
class DummyRequest(http.HttpRequest):
def __init__(self, POST=None):
super(DummyRequest, self).__init__()
- self.method = POST and "POST" or "GET"
+ self.method = "POST" if POST else "GET"
if POST is not None:
self.POST.update(POST)
self.session = {}
View
13 django/contrib/gis/db/models/fields.py
@@ -44,6 +44,7 @@ class GeometryField(Field):
# The OpenGIS Geometry name.
geom_type = 'GEOMETRY'
+ form_class = forms.GeometryField
# Geodetic units.
geodetic_units = ('Decimal Degree', 'degree')
@@ -201,11 +202,14 @@ def db_type(self, connection):
return connection.ops.geo_db_type(self)
def formfield(self, **kwargs):
- defaults = {'form_class' : forms.GeometryField,
+ defaults = {'form_class' : self.form_class,
'geom_type' : self.geom_type,
'srid' : self.srid,
}
defaults.update(kwargs)
+ if (self.dim > 2 and not 'widget' in kwargs and
+ not getattr(defaults['form_class'].widget, 'supports_3d', False)):
+ defaults['widget'] = forms.Textarea
return super(GeometryField, self).formfield(**defaults)
def get_db_prep_lookup(self, lookup_type, value, connection, prepared=False):
@@ -267,28 +271,35 @@ def get_placeholder(self, value, connection):
# The OpenGIS Geometry Type Fields
class PointField(GeometryField):
geom_type = 'POINT'
+ form_class = forms.PointField
description = _("Point")
class LineStringField(GeometryField):
geom_type = 'LINESTRING'
+ form_class = forms.LineStringField
description = _("Line string")
class PolygonField(GeometryField):
geom_type = 'POLYGON'
+ form_class = forms.PolygonField
description = _("Polygon")
class MultiPointField(GeometryField):
geom_type = 'MULTIPOINT'
+ form_class = forms.MultiPointField
description = _("Multi-point")
class MultiLineStringField(GeometryField):
geom_type = 'MULTILINESTRING'
+ form_class = forms.MultiLineStringField
description = _("Multi-line string")
class MultiPolygonField(GeometryField):
geom_type = 'MULTIPOLYGON'
+ form_class = forms.MultiPolygonField
description = _("Multi polygon")
class GeometryCollectionField(GeometryField):
geom_type = 'GEOMETRYCOLLECTION'
+ form_class = forms.GeometryCollectionField
description = _("Geometry collection")
View
4 django/contrib/gis/db/models/sql/compiler.py
@@ -121,7 +121,7 @@ def get_default_columns(self, with_aliases=False, col_aliases=None,
"""
result = []
if opts is None:
- opts = self.query.model._meta
+ opts = self.query.get_meta()
aliases = set()
only_load = self.deferred_to_columns()
seen = self.query.included_inherited_models.copy()
@@ -247,7 +247,7 @@ def _field_column(self, field, table_alias=None, column=None):
used. If `column` is specified, it will be used instead of the value
in `field.column`.
"""
- if table_alias is None: table_alias = self.query.model._meta.db_table
+ if table_alias is None: table_alias = self.query.get_meta().db_table
return "%s.%s" % (self.quote_name_unless_alias(table_alias),
self.connection.ops.quote_name(column or field.column))
View
5 django/contrib/gis/forms/__init__.py
@@ -1,2 +1,5 @@
from django.forms import *
-from django.contrib.gis.forms.fields import GeometryField
+from .fields import (GeometryField, GeometryCollectionField, PointField,
+ MultiPointField, LineStringField, MultiLineStringField, PolygonField,
+ MultiPolygonField)
+from .widgets import BaseGeometryWidget, OpenLayersWidget, OSMWidget
View
35 django/contrib/gis/forms/fields.py
@@ -9,6 +9,7 @@
# While this couples the geographic forms to the GEOS library,
# it decouples from database (by not importing SpatialBackend).
from django.contrib.gis.geos import GEOSException, GEOSGeometry, fromstr
+from .widgets import OpenLayersWidget
class GeometryField(forms.Field):
@@ -17,7 +18,8 @@ class GeometryField(forms.Field):
accepted by GEOSGeometry is accepted by this form. By default,
this includes WKT, HEXEWKB, WKB (in a buffer), and GeoJSON.
"""
- widget = forms.Textarea
+ widget = OpenLayersWidget
+ geom_type = 'GEOMETRY'
default_error_messages = {
'required' : _('No geometry value provided.'),
@@ -31,12 +33,13 @@ def __init__(self, **kwargs):
# Pop out attributes from the database field, or use sensible
# defaults (e.g., allow None).
self.srid = kwargs.pop('srid', None)
- self.geom_type = kwargs.pop('geom_type', 'GEOMETRY')
+ self.geom_type = kwargs.pop('geom_type', self.geom_type)
if 'null' in kwargs:
kwargs.pop('null', True)
warnings.warn("Passing 'null' keyword argument to GeometryField is deprecated.",
DeprecationWarning, stacklevel=2)
super(GeometryField, self).__init__(**kwargs)
+ self.widget.attrs['geom_type'] = self.geom_type
def to_python(self, value):
"""
@@ -98,3 +101,31 @@ def _has_changed(self, initial, data):
else:
# Check for change of state of existence
return bool(initial) != bool(data)
+
+
+class GeometryCollectionField(GeometryField):
+ geom_type = 'GEOMETRYCOLLECTION'
+
+
+class PointField(GeometryField):
+ geom_type = 'POINT'
+
+
+class MultiPointField(GeometryField):
+ geom_type = 'MULTIPOINT'
+
+
+class LineStringField(GeometryField):
+ geom_type = 'LINESTRING'
+
+
+class MultiLineStringField(GeometryField):
+ geom_type = 'MULTILINESTRING'
+
+
+class PolygonField(GeometryField):
+ geom_type = 'POLYGON'
+
+
+class MultiPolygonField(GeometryField):
+ geom_type = 'MULTIPOLYGON'
View
112 django/contrib/gis/forms/widgets.py
@@ -0,0 +1,112 @@
+from __future__ import unicode_literals
+
+import logging
+
+from django.conf import settings
+from django.contrib.gis import gdal
+from django.contrib.gis.geos import GEOSGeometry, GEOSException
+from django.forms.widgets import Widget
+from django.template import loader
+from django.utils import six
+from django.utils import translation
+
+logger = logging.getLogger('django.contrib.gis')
+
+
+class BaseGeometryWidget(Widget):
+ """
+ The base class for rich geometry widgets.
+ Renders a map using the WKT of the geometry.
+ """
+ geom_type = 'GEOMETRY'
+ map_srid = 4326
+ map_width = 600
+ map_height = 400
+ display_wkt = False
+
+ supports_3d = False
+ template_name = '' # set on subclasses
+
+ def __init__(self, attrs=None):
+ self.attrs = {}
+ for key in ('geom_type', 'map_srid', 'map_width', 'map_height', 'display_wkt'):
+ self.attrs[key] = getattr(self, key)
+ if attrs:
+ self.attrs.update(attrs)
+
+ def render(self, name, value, attrs=None):
+ # If a string reaches here (via a validation error on another
+ # field) then just reconstruct the Geometry.
+ if isinstance(value, six.string_types):
+ try:
+ value = GEOSGeometry(value)
+ except (GEOSException, ValueError) as err:
+ logger.error(
+ "Error creating geometry from value '%s' (%s)" % (
+ value, err)
+ )
+ value = None
+
+ wkt = ''
+ if value:
+ # Check that srid of value and map match
+ if value.srid != self.map_srid:
+ try:
+ ogr = value.ogr
+ ogr.transform(self.map_srid)
+ wkt = ogr.wkt
+ except gdal.OGRException as err:
+ logger.error(
+ "Error transforming geometry from srid '%s' to srid '%s' (%s)" % (
+ value.srid, self.map_srid, err)
+ )
+ else:
+ wkt = value.wkt
+
+ context = self.build_attrs(attrs,
+ name=name,
+ module='geodjango_%s' % name.replace('-','_'), # JS-safe
+ wkt=wkt,
+ geom_type=gdal.OGRGeomType(self.attrs['geom_type']),
+ STATIC_URL=settings.STATIC_URL,
+ LANGUAGE_BIDI=translation.get_language_bidi(),
+ )
+ return loader.render_to_string(self.template_name, context)
+
+
+class OpenLayersWidget(BaseGeometryWidget):
+ template_name = 'gis/openlayers.html'
+ class Media:
+ js = (
+ 'http://openlayers.org/api/2.11/OpenLayers.js',
+ 'gis/js/OLMapWidget.js',
+ )
+
+
+class OSMWidget(BaseGeometryWidget):
+ """
+ An OpenLayers/OpenStreetMap-based widget.
+ """
+ template_name = 'gis/openlayers-osm.html'
+ default_lon = 5
+ default_lat = 47
+
+ class Media:
+ js = (
+ 'http://openlayers.org/api/2.11/OpenLayers.js',
+ 'http://www.openstreetmap.org/openlayers/OpenStreetMap.js',
+ 'gis/js/OLMapWidget.js',
+ )
+
+ @property
+ def map_srid(self):
+ # Use the official spherical mercator projection SRID on versions
+ # of GDAL that support it; otherwise, fallback to 900913.
+ if gdal.HAS_GDAL and gdal.GDAL_VERSION >= (1, 7):
+ return 3857
+ else:
+ return 900913
+
+ def render(self, name, value, attrs=None):
+ return super(self, OSMWidget).render(name, value,
+ {'default_lon': self.default_lon, 'default_lat': self.default_lat})
View
7 django/contrib/gis/gdal/__init__.py
@@ -31,6 +31,9 @@
to a non-existant file location (e.g., `GDAL_LIBRARY_PATH='/null/path'`;
setting to None/False/'' will not work as a string must be given).
"""
+from django.contrib.gis.gdal.error import check_err, OGRException, OGRIndexError, SRSException
+from django.contrib.gis.gdal.geomtype import OGRGeomType
+
# Attempting to import objects that depend on the GDAL library. The
# HAS_GDAL flag will be set to True if the library is present on
# the system.
@@ -41,7 +44,7 @@
from django.contrib.gis.gdal.srs import SpatialReference, CoordTransform
from django.contrib.gis.gdal.geometries import OGRGeometry
HAS_GDAL = True
-except Exception:
+except OGRException:
HAS_GDAL = False
try:
@@ -50,5 +53,3 @@
# No ctypes, but don't raise an exception.
pass
-from django.contrib.gis.gdal.error import check_err, OGRException, OGRIndexError, SRSException
-from django.contrib.gis.gdal.geomtype import OGRGeomType
View
28 django/contrib/gis/gdal/tests/__init__.py
@@ -1,28 +0,0 @@
-"""
-Module for executing all of the GDAL tests. None
-of these tests require the use of the database.
-"""
-from __future__ import absolute_import
-
-from django.utils.unittest import TestSuite, TextTestRunner
-
-# Importing the GDAL test modules.
-from . import test_driver, test_ds, test_envelope, test_geom, test_srs
-
-test_suites = [test_driver.suite(),
- test_ds.suite(),
- test_envelope.suite(),
- test_geom.suite(),
- test_srs.suite(),
- ]
-
-def suite():
- "Builds a test suite for the GDAL tests."
- s = TestSuite()
- for test_suite in test_suites:
- s.addTest(test_suite)
- return s
-
-def run(verbosity=1):
- "Runs the GDAL tests."
- TextTestRunner(verbosity=verbosity).run(suite())
View
19 django/contrib/gis/gdal/tests/test_driver.py
@@ -1,5 +1,10 @@
-import unittest
-from django.contrib.gis.gdal import Driver, OGRException
+from django.contrib.gis.gdal import HAS_GDAL
+from django.utils import unittest
+from django.utils.unittest import skipUnless
+
+if HAS_GDAL:
+ from django.contrib.gis.gdal import Driver, OGRException
+
valid_drivers = ('ESRI Shapefile', 'MapInfo File', 'TIGER', 'S57', 'DGN',
'Memory', 'CSV', 'GML', 'KML')
@@ -12,6 +17,8 @@
'sHp' : 'ESRI Shapefile',
}
+
+@skipUnless(HAS_GDAL, "GDAL is required")
class DriverTest(unittest.TestCase):
def test01_valid_driver(self):
@@ -30,11 +37,3 @@ def test03_aliases(self):
for alias, full_name in aliases.items():
dr = Driver(alias)
self.assertEqual(full_name, str(dr))
-
-def suite():
- s = unittest.TestSuite()
- s.addTest(unittest.makeSuite(DriverTest))
- return s
-
-def run(verbosity=2):
- unittest.TextTestRunner(verbosity=verbosity).run(suite())
View
68 django/contrib/gis/gdal/tests/test_ds.py
@@ -1,32 +1,38 @@
import os
-import unittest
-from django.contrib.gis.gdal import DataSource, Envelope, OGRGeometry, OGRException, OGRIndexError, GDAL_VERSION
-from django.contrib.gis.gdal.field import OFTReal, OFTInteger, OFTString
-from django.contrib.gis.geometry.test_data import get_ds_file, TestDS, TEST_DATA
-
-
-# List of acceptable data sources.
-ds_list = (TestDS('test_point', nfeat=5, nfld=3, geom='POINT', gtype=1, driver='ESRI Shapefile',
- fields={'dbl' : OFTReal, 'int' : OFTInteger, 'str' : OFTString,},
- extent=(-1.35011,0.166623,-0.524093,0.824508), # Got extent from QGIS
- srs_wkt='GEOGCS["GCS_WGS_1984",DATUM["WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]',
- field_values={'dbl' : [float(i) for i in range(1, 6)], 'int' : list(range(1, 6)), 'str' : [str(i) for i in range(1, 6)]},
- fids=range(5)),
- TestDS('test_vrt', ext='vrt', nfeat=3, nfld=3, geom='POINT', gtype='Point25D', driver='VRT',
- fields={'POINT_X' : OFTString, 'POINT_Y' : OFTString, 'NUM' : OFTString}, # VRT uses CSV, which all types are OFTString.
- extent=(1.0, 2.0, 100.0, 523.5), # Min/Max from CSV
- field_values={'POINT_X' : ['1.0', '5.0', '100.0'], 'POINT_Y' : ['2.0', '23.0', '523.5'], 'NUM' : ['5', '17', '23']},
- fids=range(1,4)),
- TestDS('test_poly', nfeat=3, nfld=3, geom='POLYGON', gtype=3,
- driver='ESRI Shapefile',
- fields={'float' : OFTReal, 'int' : OFTInteger, 'str' : OFTString,},
- extent=(-1.01513,-0.558245,0.161876,0.839637), # Got extent from QGIS
- srs_wkt='GEOGCS["GCS_WGS_1984",DATUM["WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]'),
- )
-
-bad_ds = (TestDS('foo'),
- )
+from django.contrib.gis.gdal import HAS_GDAL
+from django.contrib.gis.geometry.test_data import get_ds_file, TestDS, TEST_DATA
+from django.utils import unittest
+from django.utils.unittest import skipUnless
+
+if HAS_GDAL:
+ from django.contrib.gis.gdal import DataSource, Envelope, OGRGeometry, OGRException, OGRIndexError, GDAL_VERSION
+ from django.contrib.gis.gdal.field import OFTReal, OFTInteger, OFTString
+
+ # List of acceptable data sources.
+ ds_list = (
+ TestDS('test_point', nfeat=5, nfld=3, geom='POINT', gtype=1, driver='ESRI Shapefile',
+ fields={'dbl' : OFTReal, 'int' : OFTInteger, 'str' : OFTString,},
+ extent=(-1.35011,0.166623,-0.524093,0.824508), # Got extent from QGIS
+ srs_wkt='GEOGCS["GCS_WGS_1984",DATUM["WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]',
+ field_values={'dbl' : [float(i) for i in range(1, 6)], 'int' : list(range(1, 6)), 'str' : [str(i) for i in range(1, 6)]},
+ fids=range(5)),
+ TestDS('test_vrt', ext='vrt', nfeat=3, nfld=3, geom='POINT', gtype='Point25D', driver='VRT',
+ fields={'POINT_X' : OFTString, 'POINT_Y' : OFTString, 'NUM' : OFTString}, # VRT uses CSV, which all types are OFTString.
+ extent=(1.0, 2.0, 100.0, 523.5), # Min/Max from CSV
+ field_values={'POINT_X' : ['1.0', '5.0', '100.0'], 'POINT_Y' : ['2.0', '23.0', '523.5'], 'NUM' : ['5', '17', '23']},
+ fids=range(1,4)),
+ TestDS('test_poly', nfeat=3, nfld=3, geom='POLYGON', gtype=3,
+ driver='ESRI Shapefile',
+ fields={'float' : OFTReal, 'int' : OFTInteger, 'str' : OFTString,},
+ extent=(-1.01513,-0.558245,0.161876,0.839637), # Got extent from QGIS
+ srs_wkt='GEOGCS["GCS_WGS_1984",DATUM["WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]'),
+ )
+
+bad_ds = (TestDS('foo'),)
+
+
+@skipUnless(HAS_GDAL, "GDAL is required")
class DataSourceTest(unittest.TestCase):
def test01_valid_shp(self):
@@ -236,11 +242,3 @@ def test07_integer_overflow(self):
feat = ds[0][0]
# Reference value obtained using `ogrinfo`.
self.assertEqual(676586997978, feat.get('ALAND10'))
-
-def suite():
- s = unittest.TestSuite()
- s.addTest(unittest.makeSuite(DataSourceTest))
- return s
-
-def run(verbosity=2):
- unittest.TextTestRunner(verbosity=verbosity).run(suite())
View
18 django/contrib/gis/gdal/tests/test_envelope.py
@@ -1,5 +1,9 @@
-from django.contrib.gis.gdal import Envelope, OGRException
+from django.contrib.gis.gdal import HAS_GDAL
from django.utils import unittest
+from django.utils.unittest import skipUnless
+
+if HAS_GDAL:
+ from django.contrib.gis.gdal import Envelope, OGRException
class TestPoint(object):
@@ -7,11 +11,13 @@ def __init__(self, x, y):
self.x = x
self.y = y
+
+@skipUnless(HAS_GDAL, "GDAL is required")
class EnvelopeTest(unittest.TestCase):
def setUp(self):
self.e = Envelope(0, 0, 5, 5)
-
+
def test01_init(self):
"Testing Envelope initilization."
e1 = Envelope((0, 0, 5, 5))
@@ -85,11 +91,3 @@ def test08_expand_to_include_point(self):
self.assertEqual((-1, 0, 5, 5), self.e)
self.e.expand_to_include(TestPoint(10, 10))
self.assertEqual((-1, 0, 10, 10), self.e)
-
-def suite():
- s = unittest.TestSuite()
- s.addTest(unittest.makeSuite(EnvelopeTest))
- return s
-
-def run(verbosity=2):
- unittest.TextTestRunner(verbosity=verbosity).run(suite())
View
19 django/contrib/gis/gdal/tests/test_geom.py
@@ -5,12 +5,19 @@
except ImportError:
import pickle
-from django.contrib.gis.gdal import (OGRGeometry, OGRGeomType, OGRException,
- OGRIndexError, SpatialReference, CoordTransform, GDAL_VERSION)
+from django.contrib.gis.gdal import HAS_GDAL
from django.contrib.gis.geometry.test_data import TestDataMixin
from django.utils.six.moves import xrange
from django.utils import unittest
+from django.utils.unittest import skipUnless
+if HAS_GDAL:
+ from django.contrib.gis.gdal import (OGRGeometry, OGRGeomType,
+ OGRException, OGRIndexError, SpatialReference, CoordTransform,
+ GDAL_VERSION)
+
+
+@skipUnless(HAS_GDAL, "GDAL is required")
class OGRGeomTest(unittest.TestCase, TestDataMixin):
"This tests the OGR Geometry."
@@ -476,11 +483,3 @@ def test19_equivalence_regression(self):
"Testing equivalence methods with non-OGRGeometry instances."
self.assertNotEqual(None, OGRGeometry('POINT(0 0)'))
self.assertEqual(False, OGRGeometry('LINESTRING(0 0, 1 1)') == 3)
-
-def suite():
- s = unittest.TestSuite()
- s.addTest(unittest.makeSuite(OGRGeomTest))
- return s
-
-def run(verbosity=2):
- unittest.TextTestRunner(verbosity=verbosity).run(suite())
View
16 django/contrib/gis/gdal/tests/test_srs.py
@@ -1,5 +1,9 @@
-from django.contrib.gis.gdal import SpatialReference, CoordTransform, OGRException, SRSException
+from django.contrib.gis.gdal import HAS_GDAL
from django.utils import unittest
+from django.utils.unittest import skipUnless
+
+if HAS_GDAL:
+ from django.contrib.gis.gdal import SpatialReference, CoordTransform, OGRException, SRSException
class TestSRS:
@@ -46,6 +50,8 @@ def __init__(self, wkt, **kwargs):
bad_srlist = ('Foobar', 'OOJCS["NAD83 / Texas South Central",GEOGCS["NAD83",DATUM["North_American_Datum_1983",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6269"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4269"]],PROJECTION["Lambert_Conformal_Conic_2SP"],PARAMETER["standard_parallel_1",30.28333333333333],PARAMETER["standard_parallel_2",28.38333333333333],PARAMETER["latitude_of_origin",27.83333333333333],PARAMETER["central_meridian",-99],PARAMETER["false_easting",600000],PARAMETER["false_northing",4000000],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AUTHORITY["EPSG","32140"]]',)
+
+@skipUnless(HAS_GDAL, "GDAL is required")
class SpatialRefTest(unittest.TestCase):
def test01_wkt(self):
@@ -155,11 +161,3 @@ def test13_attr_value(self):
self.assertEqual('EPSG', s1['AUTHORITY'])
self.assertEqual(4326, int(s1['AUTHORITY', 1]))
self.assertEqual(None, s1['FOOBAR'])
-
-def suite():
- s = unittest.TestSuite()
- s.addTest(unittest.makeSuite(SpatialRefTest))
- return s
-
-def run(verbosity=2):
- unittest.TextTestRunner(verbosity=verbosity).run(suite())
View
26 django/contrib/gis/geoip/tests.py
@@ -3,16 +3,28 @@
import os
from django.conf import settings
-from django.contrib.gis.geos import GEOSGeometry
-from django.contrib.gis.geoip import GeoIP, GeoIPException
+from django.contrib.gis.geos import HAS_GEOS
+from django.contrib.gis.geoip import HAS_GEOIP
from django.utils import unittest
+from django.utils.unittest import skipUnless
from django.utils import six
+if HAS_GEOIP:
+ from . import GeoIP, GeoIPException
+
+if HAS_GEOS:
+ from ..geos import GEOSGeometry
+
+
# Note: Requires use of both the GeoIP country and city datasets.
# The GEOIP_DATA path should be the only setting set (the directory
# should contain links or the actual database files 'GeoIP.dat' and
# 'GeoLiteCity.dat'.
+
+
+@skipUnless(HAS_GEOIP and getattr(settings, "GEOIP_PATH", None),
+ "GeoIP is required along with the GEOIP_DATA setting.")
class GeoIPTest(unittest.TestCase):
def test01_init(self):
@@ -70,6 +82,7 @@ def test03_country(self):
self.assertEqual({'country_code' : 'US', 'country_name' : 'United States'},
g.country(query))
+ @skipUnless(HAS_GEOS, "Geos is required")
def test04_city(self):
"Testing GeoIP city querying methods."
g = GeoIP(country='<foo>')
@@ -105,12 +118,3 @@ def test05_unicode_response(self):
g = GeoIP()
d = g.city("www.osnabrueck.de")
self.assertEqual('Osnabrück', d['city'])
-
-
-def suite():
- s = unittest.TestSuite()
- s.addTest(unittest.makeSuite(GeoIPTest))
- return s
-
-def run(verbosity=1):
- unittest.TextTestRunner(verbosity=verbosity).run(suite())
View
24 django/contrib/gis/geos/__init__.py
@@ -3,12 +3,18 @@
for more details:
http://geodjango.org/docs/geos.html
"""
-from django.contrib.gis.geos.geometry import GEOSGeometry, wkt_regex, hex_regex
-from django.contrib.gis.geos.point import Point
-from django.contrib.gis.geos.linestring import LineString, LinearRing
-from django.contrib.gis.geos.polygon import Polygon
-from django.contrib.gis.geos.collections import GeometryCollection, MultiPoint, MultiLineString, MultiPolygon
-from django.contrib.gis.geos.error import GEOSException, GEOSIndexError
-from django.contrib.gis.geos.io import WKTReader, WKTWriter, WKBReader, WKBWriter
-from django.contrib.gis.geos.factory import fromfile, fromstr
-from django.contrib.gis.geos.libgeos import geos_version, geos_version_info, GEOS_PREPARE
+try:
+ from .libgeos import geos_version, geos_version_info, GEOS_PREPARE
+ HAS_GEOS = True
+except ImportError:
+ HAS_GEOS = False
+
+if HAS_GEOS:
+ from .geometry import GEOSGeometry, wkt_regex, hex_regex
+ from .point import Point
+ from .linestring import LineString, LinearRing
+ from .polygon import Polygon
+ from .collections import GeometryCollection, MultiPoint, MultiLineString, MultiPolygon
+ from .error import GEOSException, GEOSIndexError
+ from .io import WKTReader, WKTWriter, WKBReader, WKBWriter
+ from .factory import fromfile, fromstr
View
28 django/contrib/gis/geos/tests/__init__.py
@@ -1,28 +0,0 @@
-"""
-GEOS Testing module.
-"""
-from __future__ import absolute_import
-
-from django.utils.unittest import TestSuite, TextTestRunner
-from . import test_geos, test_io, test_geos_mutation, test_mutable_list
-
-test_suites = [
- test_geos.suite(),
- test_io.suite(),
- test_geos_mutation.suite(),
- test_mutable_list.suite(),
- ]
-
-def suite():
- "Builds a test suite for the GEOS tests."
- s = TestSuite()
- for suite in test_suites:
- s.addTest(suite)
- return s
-
-def run(verbosity=1):
- "Runs the GEOS tests."
- TextTestRunner(verbosity=verbosity).run(suite())
-
-if __name__ == '__main__':
- run(2)
View
44 django/contrib/gis/geos/tests/test_geos.py
@@ -6,20 +6,28 @@
from binascii import a2b_hex, b2a_hex
from io import BytesIO
+from django.contrib.gis.gdal import HAS_GDAL
+
from django.contrib.gis import memoryview
-from django.contrib.gis.geos import (GEOSException, GEOSIndexError, GEOSGeometry,
- GeometryCollection, Point, MultiPoint, Polygon, MultiPolygon, LinearRing,
- LineString, MultiLineString, fromfile, fromstr, geos_version_info)
-from django.contrib.gis.geos.base import gdal, numpy, GEOSBase
-from django.contrib.gis.geos.libgeos import GEOS_PREPARE
from django.contrib.gis.geometry.test_data import TestDataMixin
from django.utils.encoding import force_bytes
from django.utils import six
from django.utils.six.moves import xrange
from django.utils import unittest
+from django.utils.unittest import skipUnless
+
+from .. import HAS_GEOS
+
+if HAS_GEOS:
+ from .. import (GEOSException, GEOSIndexError, GEOSGeometry,
+ GeometryCollection, Point, MultiPoint, Polygon, MultiPolygon, LinearRing,
+ LineString, MultiLineString, fromfile, fromstr, geos_version_info,
+ GEOS_PREPARE)
+ from ..base import gdal, numpy, GEOSBase
+@skipUnless(HAS_GEOS, "Geos is required.")
class GEOSTest(unittest.TestCase, TestDataMixin):
@property
@@ -198,7 +206,7 @@ def test_ewkt(self):
self.assertEqual(srid, poly.shell.srid)
self.assertEqual(srid, fromstr(poly.ewkt).srid) # Checking export
- @unittest.skipUnless(gdal.HAS_GDAL, "gdal is required")
+ @skipUnless(HAS_GDAL, "GDAL is required.")
def test_json(self):
"Testing GeoJSON input/output (via GDAL)."
for g in self.geometries.json_geoms:
@@ -662,6 +670,7 @@ def test_srid(self):
p3 = fromstr(p1.hex, srid=-1) # -1 is intended.
self.assertEqual(-1, p3.srid)
+ @skipUnless(HAS_GDAL, "GDAL is required.")
def test_custom_srid(self):
""" Test with a srid unknown from GDAL """
pnt = Point(111200, 220900, srid=999999)
@@ -851,7 +860,7 @@ def test_collections_of_collections(self):
# And, they should be equal.
self.assertEqual(gc1, gc2)
- @unittest.skipUnless(gdal.HAS_GDAL, "gdal is required")
+ @skipUnless(HAS_GDAL, "GDAL is required.")
def test_gdal(self):
"Testing `ogr` and `srs` properties."
g1 = fromstr('POINT(5 23)')
@@ -878,7 +887,7 @@ def test_copy(self):
self.assertNotEqual(poly._ptr, cpy1._ptr)
self.assertNotEqual(poly._ptr, cpy2._ptr)
- @unittest.skipUnless(gdal.HAS_GDAL, "gdal is required to transform geometries")
+ @skipUnless(HAS_GDAL, "GDAL is required to transform geometries")
def test_transform(self):
"Testing `transform` method."
orig = GEOSGeometry('POINT (-104.609 38.255)', 4326)
@@ -903,7 +912,7 @@ def test_transform(self):
self.assertAlmostEqual(trans.x, p.x, prec)
self.assertAlmostEqual(trans.y, p.y, prec)
- @unittest.skipUnless(gdal.HAS_GDAL, "gdal is required to transform geometries")
+ @skipUnless(HAS_GDAL, "GDAL is required to transform geometries")
def test_transform_3d(self):
p3d = GEOSGeometry('POINT (5 23 100)', 4326)
p3d.transform(2774)
@@ -912,6 +921,7 @@ def test_transform_3d(self):
else:
self.assertIsNone(p3d.z)
+ @skipUnless(HAS_GDAL, "GDAL is required.")
def test_transform_noop(self):
""" Testing `transform` method (SRID match) """
# transform() should no-op if source & dest SRIDs match,
@@ -962,6 +972,7 @@ def test_transform_nosrid(self):
g = GEOSGeometry('POINT (-104.609 38.255)', srid=-1)
self.assertRaises(GEOSException, g.transform, 2774, clone=True)
+ @skipUnless(HAS_GDAL, "GDAL is required.")
def test_transform_nogdal(self):
""" Testing `transform` method (GDAL not available) """
old_has_gdal = gdal.HAS_GDAL
@@ -1016,7 +1027,7 @@ def get_geoms(lst, srid=None):
self.assertEqual(geom, tmpg)
if not no_srid: self.assertEqual(geom.srid, tmpg.srid)
- @unittest.skipUnless(GEOS_PREPARE, "geos >= 3.1.0 is required")
+ @skipUnless(HAS_GEOS and GEOS_PREPARE, "geos >= 3.1.0 is required")
def test_prepared(self):
"Testing PreparedGeometry support."
# Creating a simple multipolygon and getting a prepared version.
@@ -1043,7 +1054,7 @@ def test_line_merge(self):
for geom, merged in zip(ref_geoms, ref_merged):
self.assertEqual(merged, geom.merged)
- @unittest.skipUnless(GEOS_PREPARE, "geos >= 3.1.0 is required")
+ @skipUnless(HAS_GEOS and GEOS_PREPARE, "geos >= 3.1.0 is required")
def test_valid_reason(self):
"Testing IsValidReason support"
@@ -1058,7 +1069,7 @@ def test_valid_reason(self):
self.assertIsInstance(g.valid_reason, six.string_types)
self.assertTrue(g.valid_reason.startswith("Too few points in geometry component"))
- @unittest.skipUnless(geos_version_info()['version'] >= '3.2.0', "geos >= 3.2.0 is required")
+ @skipUnless(HAS_GEOS and geos_version_info()['version'] >= '3.2.0', "geos >= 3.2.0 is required")
def test_linearref(self):
"Testing linear referencing"
@@ -1091,12 +1102,3 @@ def test_geos_version(self):
self.assertTrue(m, msg="Unable to parse the version string '%s'" % v_init)
self.assertEqual(m.group('version'), v_geos)
self.assertEqual(m.group('capi_version'), v_capi)
-
-
-def suite():
- s = unittest.TestSuite()
- s.addTest(unittest.makeSuite(GEOSTest))
- return s
-
-def run(verbosity=2):
- unittest.TextTestRunner(verbosity=verbosity).run(suite())
View
27 django/contrib/gis/geos/tests/test_geos_mutation.py
@@ -2,15 +2,23 @@
# Modified from original contribution by Aryeh Leib Taurog, which was
# released under the New BSD license.
-from django.contrib.gis.geos import *
-from django.contrib.gis.geos.error import GEOSIndexError
from django.utils import unittest
+from django.utils.unittest import skipUnless
+
+from .. import HAS_GEOS
+
+if HAS_GEOS:
+ from .. import *
+ from ..error import GEOSIndexError
+
def getItem(o,i): return o[i]
def delItem(o,i): del o[i]
def setItem(o,i,v): o[i] = v
-def api_get_distance(x): return x.distance(Point(-200,-200))
+if HAS_GEOS:
+ def api_get_distance(x): return x.distance(Point(-200,-200))
+
def api_get_buffer(x): return x.buffer(10)
def api_get_geom_typeid(x): return x.geom_typeid
def api_get_num_coords(x): return x.num_coords
@@ -29,6 +37,8 @@ def api_get_length(x): return x.length
if hasattr(val, '__call__')
and name.startswith('api_get_') ]
+
+@skipUnless(HAS_GEOS, "Geos is required.")
class GEOSMutationTest(unittest.TestCase):
"""
Tests Pythonic Mutability of Python GEOS geometry wrappers
@@ -122,14 +132,3 @@ def test06_Collection(self):
lsa = MultiPoint(*map(Point,((5,5),(3,-2),(8,1))))
for f in geos_function_tests:
self.assertEqual(f(lsa), f(mp), 'MultiPoint ' + f.__name__)
-
-def suite():
- s = unittest.TestSuite()
- s.addTest(unittest.makeSuite(GEOSMutationTest))
- return s
-
-def run(verbosity=2):
- unittest.TextTestRunner(verbosity=verbosity).run(suite())
-
-if __name__ == '__main__':
- run()
View
16 django/contrib/gis/geos/tests/test_io.py
@@ -4,10 +4,16 @@
import unittest
from django.contrib.gis import memoryview
-from django.contrib.gis.geos import GEOSGeometry, WKTReader, WKTWriter, WKBReader, WKBWriter, geos_version_info
from django.utils import six
+from django.utils.unittest import skipUnless
+from ..import HAS_GEOS
+if HAS_GEOS:
+ from .. import GEOSGeometry, WKTReader, WKTWriter, WKBReader, WKBWriter, geos_version_info
+
+
+@skipUnless(HAS_GEOS, "Geos is required.")
class GEOSIOTest(unittest.TestCase):
def test01_wktreader(self):
@@ -109,11 +115,3 @@ def test04_wkbwriter(self):
wkb_w.srid = True
self.assertEqual(hex3d_srid, wkb_w.write_hex(g))
self.assertEqual(wkb3d_srid, wkb_w.write(g))
-
-def suite():
- s = unittest.TestSuite()
- s.addTest(unittest.makeSuite(GEOSIOTest))
- return s
-
-def run(verbosity=2):
- unittest.TextTestRunner(verbosity=verbosity).run(suite())
View
12 django/contrib/gis/geos/tests/test_mutable_list.py
@@ -395,15 +395,3 @@ def test_12_arithmetic(self):
class ListMixinTestSingle(ListMixinTest):
listType = UserListB
-
-def suite():
- s = unittest.TestSuite()
- s.addTest(unittest.makeSuite(ListMixinTest))
- s.addTest(unittest.makeSuite(ListMixinTestSingle))
- return s
-
-def run(verbosity=2):
- unittest.TextTestRunner(verbosity=verbosity).run(suite())
-
-if __name__ == '__main__':
- run()
View
2  django/contrib/gis/sitemaps/views.py
@@ -20,7 +20,7 @@ def index(request, sitemaps):
"""
current_site = get_current_site(request)
sites = []
- protocol = request.is_secure() and 'https' or 'http'
+ protocol = 'https' if request.is_secure() else 'http'
for section, site in sitemaps.items():
if callable(site):
pages = site().paginator.num_pages
View
371 django/contrib/gis/static/gis/js/OLMapWidget.js
@@ -0,0 +1,371 @@
+(function() {
+/**
+ * Transforms an array of features to a single feature with the merged
+ * geometry of geom_type
+ */
+OpenLayers.Util.properFeatures = function(features, geom_type) {
+ if (features.constructor == Array) {
+ var geoms = [];
+ for (var i=0; i<features.length; i++) {
+ geoms.push(features[i].geometry);
+ }
+ var geom = new geom_type(geoms);
+ features = new OpenLayers.Feature.Vector(geom);
+ }
+ return features;
+}
+
+/**
+ * @requires OpenLayers/Format/WKT.js
+ */
+
+/**
+ * Class: OpenLayers.Format.DjangoWKT
+ * Class for reading Well-Known Text, with workarounds to successfully parse
+ * geometries and collections as returnes by django.contrib.gis.geos.
+ *
+ * Inherits from:
+ * - <OpenLayers.Format.WKT>
+ */
+
+OpenLayers.Format.DjangoWKT = OpenLayers.Class(OpenLayers.Format.WKT, {
+ initialize: function(options) {
+ OpenLayers.Format.WKT.prototype.initialize.apply(this, [options]);
+ this.regExes.justComma = /\s*,\s*/;
+ },
+
+ parse: {
+ 'point': function(str) {
+ var coords = OpenLayers.String.trim(str).split(this.regExes.spaces);
+ return new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(coords[0], coords[1])
+ );
+ },
+
+ 'multipoint': function(str) {
+ var point;
+ var points = OpenLayers.String.trim(str).split(this.regExes.justComma);
+ var components = [];
+ for(var i=0, len=points.length; i<len; ++i) {
+ point = points[i].replace(this.regExes.trimParens, '$1');
+ components.push(this.parse.point.apply(this, [point]).geometry);
+ }
+ return new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.MultiPoint(components)
+ );
+ },
+
+ 'linestring': function(str) {
+ var points = OpenLayers.String.trim(str).split(',');
+ var components = [];
+ for(var i=0, len=points.length; i<len; ++i) {
+ components.push(this.parse.point.apply(this, [points[i]]).geometry);
+ }
+ return new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.LineString(components)
+ );
+ },
+
+ 'multilinestring': function(str) {
+ var line;
+ var lines = OpenLayers.String.trim(str).split(this.regExes.parenComma);
+ var components = [];
+ for(var i=0, len=lines.length; i<len; ++i) {
+ line = lines[i].replace(this.regExes.trimParens, '$1');
+ components.push(this.parse.linestring.apply(this, [line]).geometry);
+ }
+ return new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.MultiLineString(components)
+ );
+ },
+
+ 'polygon': function(str) {
+ var ring, linestring, linearring;
+ var rings = OpenLayers.String.trim(str).split(this.regExes.parenComma);
+ var components = [];
+ for(var i=0, len=rings.length; i<len; ++i) {
+ ring = rings[i].replace(this.regExes.trimParens, '$1');
+ linestring = this.parse.linestring.apply(this, [ring]).geometry;
+ linearring = new OpenLayers.Geometry.LinearRing(linestring.components);
+ components.push(linearring);
+ }
+ return new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Polygon(components)
+ );
+ },
+
+ 'multipolygon': function(str) {
+ var polygon;
+ var polygons = OpenLayers.String.trim(str).split(this.regExes.doubleParenComma);
+ var components = [];
+ for(var i=0, len=polygons.length; i<len; ++i) {
+ polygon = polygons[i].replace(this.regExes.trimParens, '$1');
+ components.push(this.parse.polygon.apply(this, [polygon]).geometry);
+ }
+ return new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.MultiPolygon(components)
+ );
+ },
+
+ 'geometrycollection': function(str) {
+ // separate components of the collection with |
+ str = str.replace(/,\s*([A-Za-z])/g, '|$1');
+ var wktArray = OpenLayers.String.trim(str).split('|');
+ var components = [];
+ for(var i=0, len=wktArray.length; i<len; ++i) {
+ components.push(OpenLayers.Format.WKT.prototype.read.apply(this,[wktArray[i]]));
+ }
+ return components;
+ }
+ },
+
+ extractGeometry: function(geometry) {
+ var type = geometry.CLASS_NAME.split('.')[2].toLowerCase();
+ if (!this.extract[type]) {
+ return null;
+ }
+ if (this.internalProjection && this.externalProjection) {
+ geometry = geometry.clone();
+ geometry.transform(this.internalProjection, this.externalProjection);
+ }
+ var wktType = type == 'collection' ? 'GEOMETRYCOLLECTION' : type.toUpperCase();
+ var data = wktType + '(' + this.extract[type].apply(this, [geometry]) + ')';
+ return data;
+ },
+
+ /**
+ * Patched write: successfully writes WKT for geometries and
+ * geometrycollections.
+ */
+ write: function(features) {
+ var collection, geometry, type, data, isCollection;
+ isCollection = features.geometry.CLASS_NAME == "OpenLayers.Geometry.Collection";
+ var pieces = [];
+ if (isCollection) {
+ collection = features.geometry.components;
+ pieces.push('GEOMETRYCOLLECTION(');
+ for (var i=0, len=collection.length; i<len; ++i) {
+ if (i>0) {
+ pieces.push(',');
+ }
+ pieces.push(this.extractGeometry(collection[i]));
+ }
+ pieces.push(')');
+ } else {
+ pieces.push(this.extractGeometry(features.geometry));
+ }
+ return pieces.join('');
+ },
+
+ CLASS_NAME: "OpenLayers.Format.DjangoWKT"
+});
+
+function MapWidget(options) {
+ this.map = null;
+ this.controls = null;
+ this.panel = null;
+ this.layers = {};
+ this.wkt_f = new OpenLayers.Format.DjangoWKT();
+
+ // Mapping from OGRGeomType name to OpenLayers.Geometry name
+ if (options['geom_name'] == 'Unknown') options['geom_type'] = OpenLayers.Geometry;
+ else if (options['geom_name'] == 'GeometryCollection') options['geom_type'] = OpenLayers.Geometry.Collection;
+ else options['geom_type'] = eval('OpenLayers.Geometry' + options['geom_name']);
+
+ // Default options
+ this.options = {
+ color: 'ee9900',
+ default_lat: 0,
+ default_lon: 0,
+ default_zoom: 4,
+ is_collection: options['geom_type'] instanceof OpenLayers.Geometry.Collection,
+ layerswitcher: false,
+ map_options: {},
+ map_srid: 4326,
+ modifiable: true,
+ mouse_position: false,
+ opacity: 0.4,
+ point_zoom: 12,
+ scale_text: false,
+ scrollable: true
+ };
+
+ // Altering using user-provied options
+ for (var property in options) {
+ if (options.hasOwnProperty(property)) {
+ this.options[property] = options[property];
+ }
+ }
+
+ this.map = new OpenLayers.Map(this.options.map_id, this.options.map_options);
+ if (this.options.base_layer) this.layers.base = this.options.base_layer;
+ else this.layers.base = new OpenLayers.Layer.WMS('OpenLayers WMS', 'http://vmap0.tiles.osgeo.org/wms/vmap0', {layers: 'basic'});
+ this.map.addLayer(this.layers.base);
+
+ var defaults_style = {
+ 'fillColor': '#' + this.options.color,
+ 'fillOpacity': this.options.opacity,
+ 'strokeColor': '#' + this.options.color,
+ };
+ if (this.options.geom_name == 'LineString') {
+ defaults_style['strokeWidth'] = 3;
+ }
+ var styleMap = new OpenLayers.StyleMap({'default': OpenLayers.Util.applyDefaults(defaults_style, OpenLayers.Feature.Vector.style['default'])});
+ this.layers.vector = new OpenLayers.Layer.Vector(" " + this.options.name, {styleMap: styleMap});
+ this.map.addLayer(this.layers.vector);
+ wkt = document.getElementById(this.options.id).value;
+ if (wkt) {
+ var feat = OpenLayers.Util.properFeatures(this.read_wkt(wkt), this.options.geom_type);
+ this.write_wkt(feat);
+ if (this.options.is_collection) {
+ for (var i=0; i<this.num_geom; i++) {
+ this.layers.vector.addFeatures([new OpenLayers.Feature.Vector(feat.geometry.components[i].clone())]);
+ }
+ } else {
+ this.layers.vector.addFeatures([feat]);
+ }
+ this.map.zoomToExtent(feat.geometry.getBounds());
+ if (this.options.geom_name == 'Point') {
+ this.map.zoomTo(this.options.point_zoom);
+ }
+ } else {
+ this.map.setCenter(this.defaultCenter(), this.options.default_zoom);
+ }
+ this.layers.vector.events.on({'featuremodified': this.modify_wkt, scope: this});
+ this.layers.vector.events.on({'featureadded': this.add_wkt, scope: this});
+
+ this.getControls(this.layers.vector);
+ this.panel.addControls(this.controls);
+ this.map.addControl(this.panel);
+ this.addSelectControl();
+
+ if (this.options.mouse_position) {
+ this.map.addControl(new OpenLayers.Control.MousePosition());
+ }
+ if (this.options.scale_text) {
+ this.map.addControl(new OpenLayers.Control.Scale());
+ }
+ if (this.options.layerswitcher) {
+ this.map.addControl(new OpenLayers.Control.LayerSwitcher());
+ }
+ if (!this.options.scrollable) {
+ this.map.getControlsByClass('OpenLayers.Control.Navigation')[0].disableZoomWheel();
+ }
+ if (wkt) {
+ if (this.options.modifiable) {
+ this.enableEditing();
+ }
+ } else {
+ this.enableDrawing();
+ }
+}
+
+MapWidget.prototype.get_ewkt = function(feat) {
+ return "SRID=" + this.options.map_srid + ";" + this.wkt_f.write(feat);
+};
+
+MapWidget.prototype.read_wkt = function(wkt) {
+ var prefix = 'SRID=' + this.options.map_srid + ';'
+ if (wkt.indexOf(prefix) === 0) {
+ wkt = wkt.slice(prefix.length);
+ }
+ return this.wkt_f.read(wkt);
+};
+
+MapWidget.prototype.write_wkt = function(feat) {
+ feat = OpenLayers.Util.properFeatures(feat, this.options.geom_type);
+ if (this.options.is_collection) {
+ this.num_geom = feat.geometry.components.length;
+ } else {
+ this.num_geom = 1;
+ }
+ document.getElementById(this.options.id).value = this.get_ewkt(feat);
+};
+
+MapWidget.prototype.add_wkt = function(event) {
+ if (this.options.is_collection) {
+ var feat = new OpenLayers.Feature.Vector(new this.options.geom_type());
+ for (var i=0; i<this.layers.vector.features.length; i++) {
+ feat.geometry.addComponents([this.layers.vector.features[i].geometry]);
+ }
+ this.write_wkt(feat);
+ } else {
+ if (this.layers.vector.features.length > 1) {
+ old_feats = [this.layers.vector.features[0]];
+ this.layers.vector.removeFeatures(old_feats);
+ this.layers.vector.destroyFeatures(old_feats);
+ }
+ this.write_wkt(event.feature);
+ }
+};
+
+MapWidget.prototype.modify_wkt = function(event) {
+ if (this.options.is_collection) {
+ if (this.options.geom_name == 'MultiPoint') {
+ this.add_wkt(event);
+ return;
+ } else {
+ var feat = new OpenLayers.Feature.Vector(new this.options.geom_type());
+ for (var i=0; i<this.num_geom; i++) {
+ feat.geometry.addComponents([this.layers.vector.features[i].geometry]);
+ }
+ this.write_wkt(feat);
+ }
+ } else {
+ this.write_wkt(event.feature);
+ }
+};
+
+MapWidget.prototype.deleteFeatures = function() {
+ this.layers.vector.removeFeatures(this.layers.vector.features);
+ this.layers.vector.destroyFeatures();
+};
+
+MapWidget.prototype.clearFeatures = function() {
+ this.deleteFeatures();
+ document.getElementById(this.options.id).value = '';
+ this.map.setCenter(this.defaultCenter(), this.options.default_zoom);
+};
+
+MapWidget.prototype.defaultCenter = function() {
+ var center = new OpenLayers.LonLat(this.options.default_lon, this.options.default_lat);
+ if (this.options.map_srid) {
+ return center.transform(new OpenLayers.Projection("EPSG:4326"), this.map.getProjectionObject());
+ }
+ return center;
+};
+
+MapWidget.prototype.addSelectControl = function() {
+ var select = new OpenLayers.Control.SelectFeature(this.layers.vector, {'toggle': true, 'clickout': true});
+ this.map.addControl(select);
+ select.activate();
+};
+
+MapWidget.prototype.enableDrawing = function () {
+ this.map.getControlsByClass('OpenLayers.Control.DrawFeature')[0].activate();
+};
+
+MapWidget.prototype.enableEditing = function () {
+ this.map.getControlsByClass('OpenLayers.Control.ModifyFeature')[0].activate();
+};
+
+MapWidget.prototype.getControls = function(layer) {
+ this.panel = new OpenLayers.Control.Panel({'displayClass': 'olControlEditingToolbar'});
+ this.controls = [new OpenLayers.Control.Navigation()];
+ if (!this.options.modifiable && layer.features.length)
+ return;
+ if (this.options.geom_name == 'LineString' || this.options.geom_name == 'Unknown') {
+ this.controls.push(new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Path, {'displayClass': 'olControlDrawFeaturePath'}));
+ }
+ if (this.options.geom_name == 'Polygon' || this.options.geom_name == 'Unknown') {
+ this.controls.push(new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Polygon, {'displayClass': 'olControlDrawFeaturePolygon'}));
+ }
+ if (this.options.geom_name == 'Point' || this.options.geom_name == 'Unknown') {
+ this.controls.push(new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Point, {'displayClass': 'olControlDrawFeaturePoint'}));
+ }
+ if (this.options.modifiable) {
+ this.controls.push(new OpenLayers.Control.ModifyFeature(layer, {'displayClass': 'olControlModifyFeature'}));
+ }
+};
+window.MapWidget = MapWidget;
+})();
View
2  django/contrib/gis/templates/gis/admin/openlayers.js
@@ -12,7 +12,7 @@ OpenLayers.Projection.addTransform("EPSG:4326", "EPSG:3857", OpenLayers.Layer.Sp
{{ module }}.is_point = {{ is_point|yesno:"true,false" }};
{% endblock %}
{{ module }}.get_ewkt = function(feat){
- return 'SRID={{ srid }};' + {{ module }}.wkt_f.write(feat);
+ return 'SRID={{ srid|unlocalize }};' + {{ module }}.wkt_f.write(feat);
};
{{ module }}.read_wkt = function(wkt){
// OpenLayers cannot handle EWKT -- we make sure to strip it out.
View
17 django/contrib/gis/templates/gis/openlayers-osm.html
@@ -0,0 +1,17 @@
+{% extends "gis/openlayers.html" %}
+{% load l10n %}
+
+{% block map_options %}var map_options = {
+ maxExtend: new OpenLayers.Bounds(-20037508,-20037508,20037508,20037508),
+ maxResolution: 156543.0339,
+ numZoomLevels: 20,
+ units: 'm'
+};{% endblock %}
+
+{% block options %}{{ block.super }}
+options['scale_text'] = true;
+options['mouse_position'] = true;
+options['default_lon'] = {{ default_lon|unlocalize }};
+options['default_lat'] = {{ default_lat|unlocalize }};
+options['base_layer'] = new OpenLayers.Layer.OSM.Mapnik("OpenStreetMap (Mapnik)");
+{% endblock %}
View
34 django/contrib/gis/templates/gis/openlayers.html
@@ -0,0 +1,34 @@
+<style type="text/css">{% block map_css %}
+ #{{ id }}_map { width: {{ map_width }}px; height: {{ map_height }}px; }
+ #{{ id }}_map .aligned label { float: inherit; }
+ #{{ id }}_div_map { position: relative; vertical-align: top; float: {{ LANGUAGE_BIDI|yesno:"right,left" }}; }
+ {% if not display_wkt %}#{{ id }} { display: none; }{% endif %}
+ .olControlEditingToolbar .olControlModifyFeatureItemActive {
+ background-image: url("{{ STATIC_URL }}admin/img/gis/move_vertex_on.png");
+ background-repeat: no-repeat;
+ }
+ .olControlEditingToolbar .olControlModifyFeatureItemInactive {
+ background-image: url("{{ STATIC_URL }}admin/img/gis/move_vertex_off.png");
+ background-repeat: no-repeat;
+ }{% endblock %}
+</style>
+
+<div id="{{ id }}_div_map">
+ <div id="{{ id }}_map"></div>
+ <span class="clear_features"><a href="javascript:{{ module }}.clearFeatures()">Delete all Features</a></span>
+ {% if display_wkt %}<p> WKT debugging window:</p>{% endif %}
+ <textarea id="{{ id }}" class="vWKTField required" cols="150" rows="10" name="{{ name }}">{{ wkt }}</textarea>
+ <script type="text/javascript">
+ {% block map_options %}var map_options = {};{% endblock %}
+ {% block options %}var options = {
+ geom_name: '{{ geom_type }}',
+ id: '{{ id }}',
+ map_id: '{{ id }}_map',
+ map_options: map_options,
+ map_srid: {{ map_srid }},
+ name: '{{ name }}'
+ };
+ {% endblock %}
+ var {{ module }} = new MapWidget(options);
+ </script>
+</div>
View
97 django/contrib/gis/tests/__init__.py
@@ -1,13 +1,4 @@
-from django.conf import settings
-from django.test.simple import build_suite, DjangoTestSuiteRunner
-from django.utils import unittest
-
-from .test_geoforms import GeometryFieldTest
-from .test_measure import DistanceTest, AreaTest
-from .test_spatialrefsys import SpatialRefSysTest
-
-
-def geo_apps(namespace=True, runtests=False):
+def geo_apps():
"""
Returns a list of GeoDjango test applications that reside in
`django.contrib.gis.tests` that can be used with the current
@@ -36,88 +27,4 @@ def geo_apps(namespace=True, runtests=False):
# 3D apps use LayerMapping, which uses GDAL and require GEOS 3.1+.
if connection.ops.postgis and GEOS_PREPARE:
apps.append('geo3d')
- if runtests:
- return [('django.contrib.gis.tests', app) for app in apps]
- elif namespace:
- return ['django.contrib.gis.tests.%s' % app
- for app in apps]
- else:
- return apps
-
-
-def geodjango_suite(apps=True):
- """
- Returns a TestSuite consisting only of GeoDjango tests that can be run.
- """
- import sys
- from django.db.models import get_app
-
- suite = unittest.TestSuite()
-
- # Adding the GEOS tests.
- from django.contrib.gis.geos import tests as geos_tests
- suite.addTest(geos_tests.suite())
-
- # Adding GDAL tests, and any test suite that depends on GDAL, to the
- # suite if GDAL is available.
- from django.contrib.gis.gdal import HAS_GDAL
- if HAS_GDAL:
- from django.contrib.gis.gdal import tests as gdal_tests
- suite.addTest(gdal_tests.suite())
- else:
- sys.stderr.write('GDAL not available - no tests requiring GDAL will be run.\n')
-
- # Add GeoIP tests to the suite, if the library and data is available.
- from django.contrib.gis.geoip import HAS_GEOIP
- if HAS_GEOIP and hasattr(settings, 'GEOIP_PATH'):
- from django.contrib.gis.geoip import tests as geoip_tests
- suite.addTest(geoip_tests.suite())
-
- # Finally, adding the suites for each of the GeoDjango test apps.
- if apps:
- for app_name in geo_apps(namespace=False):
- suite.addTest(build_suite(get_app(app_name)))
-
- return suite
-
-
-class GeoDjangoTestSuiteRunner(DjangoTestSuiteRunner):
-
- def setup_test_environment(self, **kwargs):
- super(GeoDjangoTestSuiteRunner, self).setup_test_environment(**kwargs)
-
- # Saving original values of INSTALLED_APPS, ROOT_URLCONF, and SITE_ID.
- self.old_installed = getattr(settings, 'INSTALLED_APPS', None)
- self.old_root_urlconf = getattr(settings, 'ROOT_URLCONF', '')
- self.old_site_id = getattr(settings, 'SITE_ID', None)
-
- # Constructing the new INSTALLED_APPS, and including applications
- # within the GeoDjango test namespace.
- new_installed = [
- 'django.contrib.sites',
- 'django.contrib.sitemaps',
- 'django.contrib.gis',
- ]
-
- # Calling out to `geo_apps` to get GeoDjango applications supported
- # for testing.
- new_installed.extend(geo_apps())
- settings.INSTALLED_APPS = list(self.old_installed) + new_installed
-
- # SITE_ID needs to be set
- settings.SITE_ID = 1
-
- # ROOT_URLCONF needs to be set, else `AttributeErrors` are raised
- # when TestCases are torn down that have `urls` defined.
- settings.ROOT_URLCONF = ''
-
-
- def teardown_test_environment(self, **kwargs):
- super(GeoDjangoTestSuiteRunner, self).teardown_test_environment(**kwargs)
- settings.INSTALLED_APPS = self.old_installed
- settings.ROOT_URLCONF = self.old_root_urlconf
- settings.SITE_ID = self.old_site_id
-
-
- def build_suite(self, test_labels, extra_tests=None, **kwargs):
- return geodjango_suite()
+ return [('django.contrib.gis.tests', app) for app in apps]
View
31 django/contrib/gis/tests/distapp/tests.py
@@ -2,24 +2,33 @@