Loading django/contrib/gis/db/models/fields.py +12 −1 Original line number Diff line number Diff line Loading @@ -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') Loading Loading @@ -201,11 +202,14 @@ class GeometryField(Field): 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): Loading Loading @@ -267,28 +271,35 @@ class GeometryField(Field): # 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") django/contrib/gis/forms/__init__.py +4 −1 Original line number Diff line number Diff line 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 django/contrib/gis/forms/fields.py +33 −2 Original line number Diff line number Diff line Loading @@ -9,6 +9,7 @@ from django.utils.translation import ugettext_lazy as _ # 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): Loading @@ -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.'), Loading @@ -31,12 +33,13 @@ class GeometryField(forms.Field): # 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): """ Loading Loading @@ -98,3 +101,31 @@ class GeometryField(forms.Field): 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' django/contrib/gis/forms/widgets.py 0 → 100644 +112 −0 Original line number Diff line number Diff line 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}) django/contrib/gis/static/gis/js/OLMapWidget.js 0 → 100644 +371 −0 Original line number Diff line number Diff line (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; })(); Loading
django/contrib/gis/db/models/fields.py +12 −1 Original line number Diff line number Diff line Loading @@ -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') Loading Loading @@ -201,11 +202,14 @@ class GeometryField(Field): 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): Loading Loading @@ -267,28 +271,35 @@ class GeometryField(Field): # 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")
django/contrib/gis/forms/__init__.py +4 −1 Original line number Diff line number Diff line 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
django/contrib/gis/forms/fields.py +33 −2 Original line number Diff line number Diff line Loading @@ -9,6 +9,7 @@ from django.utils.translation import ugettext_lazy as _ # 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): Loading @@ -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.'), Loading @@ -31,12 +33,13 @@ class GeometryField(forms.Field): # 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): """ Loading Loading @@ -98,3 +101,31 @@ class GeometryField(forms.Field): 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'
django/contrib/gis/forms/widgets.py 0 → 100644 +112 −0 Original line number Diff line number Diff line 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})
django/contrib/gis/static/gis/js/OLMapWidget.js 0 → 100644 +371 −0 Original line number Diff line number Diff line (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; })();