Kaydet (Commit) 2ebfda38 authored tarafından Claude Paroz's avatar Claude Paroz

Fixed #25004 -- Updated OpenLayers-based widget to OpenLayers 3

Thanks Tim Graham for the review.
üst f996f736
...@@ -80,13 +80,22 @@ class OpenLayersWidget(BaseGeometryWidget): ...@@ -80,13 +80,22 @@ class OpenLayersWidget(BaseGeometryWidget):
template_name = 'gis/openlayers.html' template_name = 'gis/openlayers.html'
class Media: class Media:
css = {
'all': (
'https://cdnjs.cloudflare.com/ajax/libs/ol3/3.20.1/ol.css',
'gis/css/ol3.css',
)
}
js = ( js = (
'https://cdnjs.cloudflare.com/ajax/libs/openlayers/2.13.1/OpenLayers.js', 'https://cdnjs.cloudflare.com/ajax/libs/ol3/3.20.1/ol.js',
'gis/js/OLMapWidget.js', 'gis/js/OLMapWidget.js',
) )
def serialize(self, value):
return value.json if value else ''
class OSMWidget(BaseGeometryWidget): class OSMWidget(OpenLayersWidget):
""" """
An OpenLayers/OpenStreetMap-based widget. An OpenLayers/OpenStreetMap-based widget.
""" """
...@@ -95,12 +104,6 @@ class OSMWidget(BaseGeometryWidget): ...@@ -95,12 +104,6 @@ class OSMWidget(BaseGeometryWidget):
default_lat = 47 default_lat = 47
map_srid = 3857 map_srid = 3857
class Media:
js = (
'https://cdnjs.cloudflare.com/ajax/libs/openlayers/2.13.1/OpenLayers.js',
'gis/js/OLMapWidget.js',
)
def __init__(self, attrs=None): def __init__(self, attrs=None):
super(OSMWidget, self).__init__() super(OSMWidget, self).__init__()
for key in ('default_lon', 'default_lat'): for key in ('default_lon', 'default_lat'):
......
.switch-type {
background-repeat: no-repeat;
cursor: pointer;
top: 0.5em;
width: 22px;
height: 20px;
}
.type-Point {
background-image: url("../img/draw_point_off.png");
right: 5px;
}
.type-Point.type-active {
background-image: url("../img/draw_point_on.png");
}
.type-LineString {
background-image: url("../img/draw_line_off.png");
right: 30px;
}
.type-LineString.type-active {
background-image: url("../img/draw_line_on.png");
}
.type-Polygon {
background-image: url("../img/draw_polygon_off.png");
right: 55px;
}
.type-Polygon.type-active {
background-image: url("../img/draw_polygon_on.png");
}
{% extends "gis/openlayers.html" %} {% extends "gis/openlayers.html" %}
{% load l10n %} {% 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 }} {% block options %}{{ block.super }}
options['scale_text'] = true;
options['mouse_position'] = true;
options['default_lon'] = {{ default_lon|unlocalize }}; options['default_lon'] = {{ default_lon|unlocalize }};
options['default_lat'] = {{ default_lat|unlocalize }}; options['default_lat'] = {{ default_lat|unlocalize }};
options['base_layer'] = new OpenLayers.Layer.OSM("OpenStreetMap (Mapnik)"); {% endblock %}
{% block base_layer %}
var base_layer = new ol.layer.Tile({source: new ol.source.OSM()});
{% endblock %} {% endblock %}
{% load i18n l10n static %} {% load i18n l10n %}
<style type="text/css">{% block map_css %}{% get_current_language_bidi as LANGUAGE_BIDI %} <style type="text/css">{% block map_css %}{% get_current_language_bidi as LANGUAGE_BIDI %}
#{{ id }}_map { width: {{ map_width }}px; height: {{ map_height }}px; } #{{ id }}_map { width: {{ map_width }}px; height: {{ map_height }}px; }
#{{ id }}_map .aligned label { float: inherit; } #{{ id }}_map .aligned label { float: inherit; }
#{{ id }}_div_map { position: relative; vertical-align: top; float: {{ LANGUAGE_BIDI|yesno:"right,left" }}; } #{{ id }}_div_map { position: relative; vertical-align: top; float: {{ LANGUAGE_BIDI|yesno:"right,left" }}; }
{% if not display_raw %}#{{ id }} { display: none; }{% endif %} {% if not display_raw %}#{{ id }} { display: none; }{% endif %}
.olControlEditingToolbar .olControlModifyFeatureItemActive { {% endblock %}
background-image: url("{% static "admin/img/gis/move_vertex_on.svg" %}");
background-repeat: no-repeat;
}
.olControlEditingToolbar .olControlModifyFeatureItemInactive {
background-image: url("{% static "admin/img/gis/move_vertex_off.svg" %}");
background-repeat: no-repeat;
}{% endblock %}
</style> </style>
<div id="{{ id }}_div_map"> <div id="{{ id }}_div_map">
<div id="{{ id }}_map"></div> <div id="{{ id }}_map"></div>
<span class="clear_features"><a href="javascript:{{ module }}.clearFeatures()">{% trans "Delete all Features" %}</a></span> {% if not disabled %}<span class="clear_features"><a href="javascript:{{ module }}.clearFeatures()">{% trans "Delete all Features" %}</a></span>{% endif %}
{% if display_raw %}<p>{% trans "Debugging window (serialized value)" %}</p>{% endif %} {% if display_raw %}<p>{% trans "Debugging window (serialized value)" %}</p>{% endif %}
<textarea id="{{ id }}" class="vSerializedField required" cols="150" rows="10" name="{{ name }}">{{ serialized }}</textarea> <textarea id="{{ id }}" class="vSerializedField required" cols="150" rows="10" name="{{ name }}">{{ serialized }}</textarea>
<script type="text/javascript"> <script type="text/javascript">
{% block map_options %}var map_options = {};{% endblock %} {% block map_options %}var map_options = {};{% endblock %}
{% block base_layer %}
var base_layer = new ol.layer.Tile({
source: new ol.source.XYZ({
attributions: "NASA Worldview",
maxZoom: 8,
url: "https://map1{a-c}.vis.earthdata.nasa.gov/wmts-webmerc/" +
"BlueMarble_ShadedRelief_Bathymetry/default/%7BTime%7D/" +
"GoogleMapsCompatible_Level8/{z}/{y}/{x}.jpg"
})
});
{% endblock %}
{% block options %}var options = { {% block options %}var options = {
base_layer: base_layer,
geom_name: '{{ geom_type }}', geom_name: '{{ geom_type }}',
id: '{{ id }}', id: '{{ id }}',
map_id: '{{ id }}_map', map_id: '{{ id }}_map',
......
...@@ -164,18 +164,25 @@ Widget classes ...@@ -164,18 +164,25 @@ Widget classes
isn't suitable for production use since it offers no guaranteed uptime isn't suitable for production use since it offers no guaranteed uptime
and runs on a slow server. and runs on a slow server.
.. _tailored to your needs: http://docs.openlayers.org/library/deploying.html Also, the widget nows uses OpenLayers 3 instead of OpenLayers 2.
.. _tailored to your needs: http://openlayers.org/en/latest/doc/tutorials/custom-builds.html
``OSMWidget`` ``OSMWidget``
.. class:: OSMWidget .. class:: OSMWidget
This widget uses an OpenStreetMap base layer (Mapnik) to display geographic This widget uses an OpenStreetMap base layer to display geographic objects
objects on. on. ``template_name`` is ``gis/openlayers-osm.html``.
``template_name`` is ``gis/openlayers-osm.html``.
The :class:`OpenLayersWidget` note about JavaScript file hosting above also The :class:`OpenLayersWidget` note about JavaScript file hosting above also
applies here. See also this `FAQ answer`_ about ``https`` access to map applies here. See also this `FAQ answer`_ about ``https`` access to map
tiles. tiles.
.. versionchanged:: 1.11
OpenLayers 2.x has been dropped in favor of OpenLayers 3. If you extend
the ``gis/openlayers-osm.html`` template, please review your custom
template.
.. _FAQ answer: https://help.openstreetmap.org/questions/10920/how-to-embed-a-map-in-my-https-site .. _FAQ answer: https://help.openstreetmap.org/questions/10920/how-to-embed-a-map-in-my-https-site
...@@ -165,7 +165,8 @@ Minor features ...@@ -165,7 +165,8 @@ Minor features
* The OpenLayers-based form widgets now use ``OpenLayers.js`` from * The OpenLayers-based form widgets now use ``OpenLayers.js`` from
``https://cdnjs.cloudflare.com`` which is more suitable for production use ``https://cdnjs.cloudflare.com`` which is more suitable for production use
than the the old ``http://openlayers.org`` source. than the the old ``http://openlayers.org`` source. They are also updated to
use OpenLayers 3.
* PostGIS migrations can now change field dimensions. * PostGIS migrations can now change field dimensions.
...@@ -469,6 +470,11 @@ Backwards incompatible changes in 1.11 ...@@ -469,6 +470,11 @@ Backwards incompatible changes in 1.11
* The ``GEOSGeometry`` equality operator now also compares SRID. * The ``GEOSGeometry`` equality operator now also compares SRID.
* The OpenLayers-based form widgets now use OpenLayers 3, and the
``gis/openlayers.html`` and ``gis/openlayers-osm.html`` templates have been
updated. Check your project if you subclass these widgets or extend the
templates.
Database backend API Database backend API
-------------------- --------------------
......
...@@ -7,42 +7,58 @@ QUnit.module('gis.OLMapWidget'); ...@@ -7,42 +7,58 @@ QUnit.module('gis.OLMapWidget');
QUnit.test('MapWidget.featureAdded', function(assert) { QUnit.test('MapWidget.featureAdded', function(assert) {
var options = {id: 'id_point', map_id: 'id_point_map', geom_name: 'Point'}; var options = {id: 'id_point', map_id: 'id_point_map', geom_name: 'Point'};
var widget = new MapWidget(options); var widget = new MapWidget(options);
assert.equal(widget.layers.vector.features.length, 1); assert.equal(widget.featureCollection.getLength(), 1);
widget.serializeFeatures();
assert.equal( assert.equal(
widget.layers.vector.features[0].geometry.toString(), document.getElementById('id_point').value,
'POINT(7.8177 47.397)', '{"type":"Point","coordinates":[7.8177,47.397]}',
'Point addded to vector layer' 'Point added to vector layer'
); );
}); });
QUnit.test('MapWidget.map_srid', function(assert) { QUnit.test('MapWidget.map_srid', function(assert) {
var options = {id: 'id_point', map_id: 'id_point_map', geom_name: 'Point'}; var options = {id: 'id_point', map_id: 'id_point_map', geom_name: 'Point'};
var widget = new MapWidget(options); var widget = new MapWidget(options);
assert.equal(widget.options.map_srid, 4326, 'SRID 4326'); assert.equal(widget.map.getView().getProjection().getCode(), 'EPSG:3857', 'SRID 3857');
}); });
QUnit.test('MapWidget.defaultCenter', function(assert) { QUnit.test('MapWidget.defaultCenter', function(assert) {
var options = {id: 'id_point', map_id: 'id_point_map', geom_name: 'Point'}; var options = {id: 'id_point', map_id: 'id_point_map', geom_name: 'Point'};
var widget = new MapWidget(options); var widget = new MapWidget(options);
assert.equal(widget.defaultCenter().toString(), 'lon=0,lat=0', 'Default center at 0, 0'); assert.equal(widget.defaultCenter().toString(), '0,0', 'Default center at 0, 0');
options.default_lat = 47.08; options.default_lat = 47.08;
options.default_lon = 6.81; options.default_lon = 6.81;
widget = new MapWidget(options); widget = new MapWidget(options);
assert.equal( assert.equal(
widget.defaultCenter().toString(), widget.defaultCenter().toString(),
'lon=6.81,lat=47.08', '6.81,47.08',
'Default center at 6.81, 47.08' 'Default center at 6.81, 47.08'
); );
assert.equal(widget.map.getView().getZoom(), 12);
}); });
QUnit.test('MapWidget.getControls', function(assert) { QUnit.test('MapWidget.interactions', function(assert) {
var options = {id: 'id_point', map_id: 'id_point_map', geom_name: 'Point'}; var options = {id: 'id_point', map_id: 'id_point_map', geom_name: 'Point'};
var widget = new MapWidget(options); var widget = new MapWidget(options);
widget.getControls(widget.layers.vector); assert.equal(Object.keys(widget.interactions).length, 2);
assert.equal(widget.controls.length, 3); assert.equal(widget.interactions.draw.getActive(), false, "Draw is inactive with an existing point");
assert.equal(widget.controls[0].displayClass, 'olControlNavigation', 'Navigation control'); assert.equal(widget.interactions.modify.getActive(), true, "Modify is active with an existing point");
assert.equal(widget.controls[1].displayClass, 'olControlDrawFeaturePoint', 'Draw control'); });
assert.equal(widget.controls[2].displayClass, 'olControlModifyFeature', 'Modify control');
QUnit.test('MapWidget.clearFeatures', function(assert) {
var options = {id: 'id_point', map_id: 'id_point_map', geom_name: 'Point'};
var widget = new MapWidget(options);
var initial_value = document.getElementById('id_point').value;
widget.clearFeatures();
assert.equal(document.getElementById('id_point').value, "");
document.getElementById('id_point').value = initial_value;
});
QUnit.test('MapWidget.multipolygon', function(assert) {
var options = {id: 'id_multipolygon', map_id: 'id_multipolygon_map', geom_name: 'MultiPolygon'};
var widget = new MapWidget(options);
assert.ok(widget.options.is_collection);
assert.equal(widget.interactions.draw.getActive(), true, "Draw is active with no existing content");
}); });
QUnit.test('MapWidget.IsCollection', function(assert) { QUnit.test('MapWidget.IsCollection', function(assert) {
......
...@@ -77,12 +77,16 @@ ...@@ -77,12 +77,16 @@
<script src='../django/contrib/admin/static/admin/js/prepopulate.js' data-cover></script> <script src='../django/contrib/admin/static/admin/js/prepopulate.js' data-cover></script>
<script src='../django/contrib/admin/static/admin/js/urlify.js' data-cover></script> <script src='../django/contrib/admin/static/admin/js/urlify.js' data-cover></script>
<div id="id_point_map"> <div id="id_point_map" style="display:none;">
<textarea id="id_point" name="point" <textarea id="id_point" name="point" class="vSerializedField required"
class="vSerializedField required" style="display:none;" style="display:none;" rows="10" cols="150"
rows="10" cols="150">POINT (7.8177 47.397)</textarea> >{&quot;type&quot;: &quot;Point&quot;, &quot;coordinates&quot;: [7.8177, 47.397]}</textarea>
</div> </div>
<script src='https://cdnjs.cloudflare.com/ajax/libs/openlayers/2.13.1/OpenLayers.js'></script> <div id="id_multipolygon_map" style="display:none;">
<textarea id="id_multipolygon" name="multipolygon" class="vSerializedField required"
style="display:none;" rows="10" cols="150"></textarea>
</div>
<script src='https://cdnjs.cloudflare.com/ajax/libs/ol3/3.20.0/ol.js'></script>
<script src='../django/contrib/gis/static/gis/js/OLMapWidget.js' data-cover></script> <script src='../django/contrib/gis/static/gis/js/OLMapWidget.js' data-cover></script>
<script src='./gis/mapwidget.test.js'></script> <script src='./gis/mapwidget.test.js'></script>
......
...@@ -204,7 +204,7 @@ class SpecializedFieldTest(SimpleTestCase): ...@@ -204,7 +204,7 @@ class SpecializedFieldTest(SimpleTestCase):
self.assertIn('<textarea ', rendered) self.assertIn('<textarea ', rendered)
self.assertIn('required', rendered) self.assertIn('required', rendered)
self.assertIn(geom.wkt, rendered) self.assertIn(escape(geom.json), rendered)
# map_srid in operlayers.html template must not be localized. # map_srid in operlayers.html template must not be localized.
@override_settings(USE_L10N=True, USE_THOUSAND_SEPARATOR=True) @override_settings(USE_L10N=True, USE_THOUSAND_SEPARATOR=True)
...@@ -318,7 +318,7 @@ class OSMWidgetTest(SimpleTestCase): ...@@ -318,7 +318,7 @@ class OSMWidgetTest(SimpleTestCase):
form = PointForm(data={'p': geom}) form = PointForm(data={'p': geom})
rendered = form.as_p() rendered = form.as_p()
self.assertIn("OpenStreetMap (Mapnik)", rendered) self.assertIn("ol.source.OSM()", rendered)
self.assertIn("id: 'id_p',", rendered) self.assertIn("id: 'id_p',", rendered)
def test_default_lat_lon(self): def test_default_lat_lon(self):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment