【Admin后台管理】Geodjango后台显示地图并加载空间字段
原文作者:我辈李想
版权声明:文章原创,转载时请务必加上原文超链接、作者信息和本声明。
文章目录
- 前言
- 一、django+admin
- 二、geodjango+admin
- 三、报错处理
前言
在前面的博客中,我们已经介绍了Geodjango的环境搭建和数据库操作,django本身提供了amdin后台,其实geodjango也有后台页面,现在我们来试着使用admin显示地图并加载空间字段。
一、django+admin
首先我们应该对django使用admin后台有一定基础,可以简单实现后台增删改查数据。
models.py文件如下
from django.contrib.gis.db import models
from django.utils import timezone
class SarPic(models.Model):
...
ImageGeoJSON = models.PolygonField('成像地理信息', null=True, help_text='Metadata:ImageGeoJSON')
...
class Meta:
verbose_name = '数据'
verbose_name_plural = verbose_name
admin.py文件如下
from django.contrib import admin
from sarimage.models import SarPic
class SarPicAdmin(admin.ModelAdmin):
list_display = ['ProductID', 'create_time', 'alter_time']
list_per_page = 50
list_filter = ['ImagingTime', 'SatellitePlatform', 'missionDataTakeId', 'SensorMode']
# search_fields = ['ProductID', 'ImagingTime', 'SatelliteName', 'missionDataTakeId']
fieldsets = (
('影像图像', {
'fields': ('ImageGeoJSON',)
})
)
admin.site.register(SarPic, SarPicAdmin)
二、geodjango+admin
这里的变化为admin.ModelAdmin改为GeoModelAdmin(基于OpenLayers),除此之外还可以改成OSMGeoAdmin(基于OpenStreetMap)或LeafletGeoAdmin(基于Leaflet框架的第三方库django-leaflet)。
admin.py文件如下
from django.contrib import admin
from django.contrib.gis.admin import GeoModelAdmin
from sarimage.models import SarPic
class SarPicAdmin(GeoModelAdmin):
list_display = ['ProductID', 'create_time', 'alter_time']
list_per_page = 50
list_filter = ['ImagingTime', 'SatellitePlatform', 'missionDataTakeId', 'SensorMode']
# search_fields = ['ProductID', 'ImagingTime', 'SatelliteName', 'missionDataTakeId']
fieldsets = (
('影像图像', {
'fields': ('ImageGeoJSON',)
})
)
default_lon # 默认的中心经度。
default_lat # 默认的中心纬度。
default_zoom # 要使用的默认缩放级别。默认值为4。
extra_js # 要包含的任何额外javascript的URL序列。
map_template # 重写用于生成javascript Slippy映射的模板。默认是 'gis/admin/openlayers.html' .
map_width # 地图的宽度,以像素为单位。默认值为600。
map_height # 地图的高度,以像素为单位。默认为400。
openlayers_url # 链接到Openlayers JavaScript的URL。默认为 'https://cdnjs.cloudflare.com/ajax/libs/openlayers/2.13.1/OpenLayers.js' .
modifiable = False # 禁用管理中现有几何图形字段的编辑
admin.site.register(SarPic, SarPicAdmin)
三、报错处理
按照第二部分设置后,会报错 django.template.exceptions.TemplateDoesNotExist: gis/admin/openlayers.html。
我不知道其他人遇到怎么处理的,网络上也没找相关方案。主要原因是django的版本不同,django库的文件存在差异,部分版本缺少geodjango的template文件夹,这个需要我们自己补全。
templates必须放入我们项目的app中,我的项目app是sarimage,models.py是空间数据的类。
from django.contrib.gis.admin import GeoModelAdmin
我使用的是geodjango默认的GeoModelAdmin,所以我需要的是openlayers.html和openlayers.js文件。
openlayers.html文件内容如下
{% block extrastyle %}
{% load i18n static %}{% get_current_language_bidi as LANGUAGE_BIDI %}
<style type="text/css">
#{{ id }}_map { width: {{ map_width }}px; height: {{ map_height }}px; }
#{{ id }}_map .aligned label { float:inherit; }
#{{ id }}_admin_map { position: relative; vertical-align: top; z-index: 0; float: {{ LANGUAGE_BIDI|yesno:"right,left" }}; }
{% if not display_wkt %}#{{ id }} { display: none; }{% endif %}
.olControlEditingToolbar .olControlModifyFeatureItemActive {
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;
}
</style>
{% endblock %}
<span id="{{ id }}_admin_map">
<script>
//<![CDATA[
{% block openlayers %}{% include "gis/admin/openlayers.js" %}{% endblock %}
//]]>
</script>
<div id="{{ id }}_map" dir="{{ LANGUAGE_BIDI|yesno:'rtl,ltr,auto' }}"></div>
{% if editable %}
<a href="javascript:{{ module }}.clearFeatures()">{% translate "Delete all Features" %}</a>
{% endif %}
{% if display_wkt %}<p>{% translate "WKT debugging window:" %} </p>{% endif %}
<textarea id="{{ id }}" class="vWKTField required" cols="150" rows="10" name="{{ name }}">{{ wkt }}</textarea>
<script>{% block init_function %}{{ module }}.init();{% endblock %}</script>
</span>
openlayers.js文件内容如下
{% load l10n %}
OpenLayers.Projection.addTransform("EPSG:4326", "EPSG:3857", OpenLayers.Layer.SphericalMercator.projectForward);
{% block vars %}var {{ module }} = {};
{{ module }}.map = null; {{ module }}.controls = null; {{ module }}.panel = null; {{ module }}.re = new RegExp("^SRID=\\d+;(.+)", "i"); {{ module }}.layers = {};
{{ module }}.modifiable = {{ modifiable|yesno:"true,false" }};
{{ module }}.wkt_f = new OpenLayers.Format.WKT();
{{ module }}.is_collection = {{ is_collection|yesno:"true,false" }};
{{ module }}.collection_type = '{{ collection_type }}';
{{ module }}.is_generic = {{ is_generic|yesno:"true,false" }};
{{ module }}.is_linestring = {{ is_linestring|yesno:"true,false" }};
{{ module }}.is_polygon = {{ is_polygon|yesno:"true,false" }};
{{ module }}.is_point = {{ is_point|yesno:"true,false" }};
{% endblock %}
{{ module }}.get_ewkt = function(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.
// EWKT is only exposed to OL if there's a validation error in the admin.
var match = {{ module }}.re.exec(wkt);
if (match){wkt = match[1];}
return {{ module }}.wkt_f.read(wkt);
};
{{ module }}.write_wkt = function(feat){
if ({{ module }}.is_collection){ {{ module }}.num_geom = feat.geometry.components.length;}
else { {{ module }}.num_geom = 1;}
document.getElementById('{{ id }}').value = {{ module }}.get_ewkt(feat);
};
{{ module }}.add_wkt = function(event){
// This function will sync the contents of the `vector` layer with the
// WKT in the text field.
if ({{ module }}.is_collection){
var feat = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.{{ geom_type }}());
for (var i = 0; i < {{ module }}.layers.vector.features.length; i++){
feat.geometry.addComponents([{{ module }}.layers.vector.features[i].geometry]);
}
{{ module }}.write_wkt(feat);
} else {
// Make sure to remove any previously added features.
if ({{ module }}.layers.vector.features.length > 1){
old_feats = [{{ module }}.layers.vector.features[0]];
{{ module }}.layers.vector.removeFeatures(old_feats);
{{ module }}.layers.vector.destroyFeatures(old_feats);
}
{{ module }}.write_wkt(event.feature);
}
};
{{ module }}.modify_wkt = function(event){
if ({{ module }}.is_collection){
if ({{ module }}.is_point){
{{ module }}.add_wkt(event);
return;
} else {
// When modifying the selected components are added to the
// vector layer so we only increment to the `num_geom` value.
var feat = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.{{ geom_type }}());
for (var i = 0; i < {{ module }}.num_geom; i++){
feat.geometry.addComponents([{{ module }}.layers.vector.features[i].geometry]);
}
{{ module }}.write_wkt(feat);
}
} else {
{{ module }}.write_wkt(event.feature);
}
};
// Function to clear vector features and purge wkt from div
{{ module }}.deleteFeatures = function(){
{{ module }}.layers.vector.removeFeatures({{ module }}.layers.vector.features);
{{ module }}.layers.vector.destroyFeatures();
};
{{ module }}.clearFeatures = function (){
{{ module }}.deleteFeatures();
document.getElementById('{{ id }}').value = '';
{% localize off %}
{{ module }}.map.setCenter(new OpenLayers.LonLat({{ default_lon }}, {{ default_lat }}), {{ default_zoom }});
{% endlocalize %}
};
// Add Select control
{{ module }}.addSelectControl = function(){
var select = new OpenLayers.Control.SelectFeature({{ module }}.layers.vector, {'toggle' : true, 'clickout' : true});
{{ module }}.map.addControl(select);
select.activate();
};
{{ module }}.enableDrawing = function(){
{{ module }}.map.getControlsByClass('OpenLayers.Control.DrawFeature')[0].activate();
};
{{ module }}.enableEditing = function(){
{{ module }}.map.getControlsByClass('OpenLayers.Control.ModifyFeature')[0].activate();
};
// Create an array of controls based on geometry type
{{ module }}.getControls = function(lyr){
{{ module }}.panel = new OpenLayers.Control.Panel({'displayClass': 'olControlEditingToolbar'});
{{ module }}.controls = [new OpenLayers.Control.Navigation()];
if (!{{ module }}.modifiable && lyr.features.length) return;
if ({{ module }}.is_linestring || {{ module }}.is_generic){
{{ module }}.controls.push(new OpenLayers.Control.DrawFeature(lyr, OpenLayers.Handler.Path, {'displayClass': 'olControlDrawFeaturePath'}));
}
if ({{ module }}.is_polygon || {{ module }}.is_generic){
{{ module }}.controls.push(new OpenLayers.Control.DrawFeature(lyr, OpenLayers.Handler.Polygon, {'displayClass': 'olControlDrawFeaturePolygon'}));
}
if ({{ module }}.is_point || {{ module }}.is_generic){
{{ module }}.controls.push(new OpenLayers.Control.DrawFeature(lyr, OpenLayers.Handler.Point, {'displayClass': 'olControlDrawFeaturePoint'}));
}
if ({{ module }}.modifiable){
{{ module }}.controls.push(new OpenLayers.Control.ModifyFeature(lyr, {'displayClass': 'olControlModifyFeature'}));
}
};
{{ module }}.init = function(){
{% block map_options %}// The options hash, w/ zoom, resolution, and projection settings.
var options = {
{% autoescape off %}{% for item in map_options.items %} '{{ item.0 }}' : {{ item.1 }}{% if not forloop.last %},{% endif %}
{% endfor %}{% endautoescape %} };{% endblock %}
// The admin map for this geometry field.
{% block map_creation %}
{{ module }}.map = new OpenLayers.Map('{{ id }}_map', options);
// Base Layer
{{ module }}.layers.base = {% block base_layer %}new OpenLayers.Layer.WMS("{{ wms_name }}", "{{ wms_url }}", {layers: '{{ wms_layer }}'{{ wms_options|safe }}});{% endblock %}
{{ module }}.map.addLayer({{ module }}.layers.base);
{% endblock %}
{% block extra_layers %}{% endblock %}
{% if is_linestring %}OpenLayers.Feature.Vector.style["default"]["strokeWidth"] = 3; // Default too thin for linestrings. {% endif %}
{{ module }}.layers.vector = new OpenLayers.Layer.Vector(" {{ field_name }}");
{{ module }}.map.addLayer({{ module }}.layers.vector);
// Read WKT from the text field.
var wkt = document.getElementById('{{ id }}').value;
if (wkt){
// After reading into geometry, immediately write back to
// WKT <textarea> as EWKT (so that SRID is included).
var admin_geom = {{ module }}.read_wkt(wkt);
{{ module }}.write_wkt(admin_geom);
if ({{ module }}.is_collection){
// If geometry collection, add each component individually so they may be
// edited individually.
for (var i = 0; i < {{ module }}.num_geom; i++){
{{ module }}.layers.vector.addFeatures([new OpenLayers.Feature.Vector(admin_geom.geometry.components[i].clone())]);
}
} else {
{{ module }}.layers.vector.addFeatures([admin_geom]);
}
// Zooming to the bounds.
{{ module }}.map.zoomToExtent(admin_geom.geometry.getBounds());
if ({{ module }}.is_point){
{{ module }}.map.zoomTo({{ point_zoom }});
}
} else {
{% localize off %}
{{ module }}.map.setCenter(new OpenLayers.LonLat({{ default_lon }}, {{ default_lat }}), {{ default_zoom }});
{% endlocalize %}
}
// This allows editing of the geographic fields -- the modified WKT is
// written back to the content field (as EWKT, so that the ORM will know
// to transform back to original SRID).
{{ module }}.layers.vector.events.on({"featuremodified" : {{ module }}.modify_wkt});
{{ module }}.layers.vector.events.on({"featureadded" : {{ module }}.add_wkt});
{% block controls %}
// Map controls:
// Add geometry specific panel of toolbar controls
{{ module }}.getControls({{ module }}.layers.vector);
{{ module }}.panel.addControls({{ module }}.controls);
{{ module }}.map.addControl({{ module }}.panel);
{{ module }}.addSelectControl();
// Then add optional visual controls
{% if mouse_position %}{{ module }}.map.addControl(new OpenLayers.Control.MousePosition());{% endif %}
{% if scale_text %}{{ module }}.map.addControl(new OpenLayers.Control.Scale());{% endif %}
{% if layerswitcher %}{{ module }}.map.addControl(new OpenLayers.Control.LayerSwitcher());{% endif %}
// Then add optional behavior controls
{% if not scrollable %}{{ module }}.map.getControlsByClass('OpenLayers.Control.Navigation')[0].disableZoomWheel();{% endif %}
{% endblock %}
if (wkt){
if ({{ module }}.modifiable){
{{ module }}.enableEditing();
}
} else {
{{ module }}.enableDrawing();
}
};