django-olwidget
is a portable Django application that uses
olwidget.js to easily create editable and informational
maps using GeoDjango, inside and outside of Django’s admin.
A quick guide to the olwidget
module contents:
And of course, customization of all types: options
Installation of the olwidget
requires a couple of steps – these should be
familiar to users of other reusable Django apps:
Install django-olwidget
using your preferred method for python modules.
The folder olwidget
in the django-olwidget
directory must end up in
your python path. You could do this by copying or symlinking it into your
path, or with:
python setup.py install
The preferred method is to install from using pip with virtualenv. The requirements file entry for the stable version:
django-olwidget
To use the development version, the requirements is:
-e git://github.com/yourcelf/olwidget.git#egg=django-olwidget
Copy or link the static/olwidget
directory into to your project’s
static files directory. If you wish to name the directory something other
than olwidget
, define OLWIDGET_STATIC_URL
with the URL for the
media files in your settings file. (Note: In previous versions, this was
OLWIDGET_MEDIA_URL
; this was changed in keeping with Django 1.3’s
staticfiles support).
Include 'olwidget'
in your project’s settings INSTALLED_APPS
list.
(Optional) If you want to use Yahoo or CloudMade map layers,
YAHOO_APP_ID
or CLOUDMADE_API_KEY
in your settings file.
olwidget
uses an OpenStreetMaps layer by default, which requires no
key. If you use Google layers and require an API key, specify
GOOGLE_API_KEY
(no key is required for low request volumes).
olwidget
includes a test project demonstrating some of the olwidget
app’s
functionality; this can be found in the django-olwidget/test_project
directory. To use it, modify the settings.py
directory to reflect your
database settings. For convenience, a shell script, reset_testolwidget.sh
,
is included to set up the database using postgres
, template_postgis
,and the database user and password specified in the script.
olwidget
defines several widget types. If all you need is a single-layer
map, or if you want to display maps outside of the context of a form, this is
what you want. EditableMap and InfoMap are single-layer widgets for
editing or displaying map data. Map, EditableLayer, and InfoLayer
are the widget counterparts to the fields above which make display of
multi-layer maps outside of forms possible.
EditableMap
is a widget type for editing single layers. Constructor:
olwidget.widgets.EditableMap(options=None, template=None)
options
template
An example form definition that uses an editable map:
from django import forms
from olwidget.widgets import EditableMap
class MyForm(forms.Form):
location = forms.CharField(widget=EditableMap())
In a template:
<head> {{ form.media }} </head>
<body>... {{ form }} ...</body>
InfoMap
is used for displaying read-only single-layer maps with clickable
information popups over geometries. Unlike the other types, you probably want
to use this widget without a Form. Constructor:
olwidget.widgets.InfoMap(info, options=None, template=None)
info
[geometry, attr]
pairs. attr
can be either a string
containing html, or a dict containing html
and style
keys. The
html is displayed when the geometry is clicked.options
template
An example info map:
from olwidget.widgets import InfoMap
map = InfoMap([
[mymodel.point, "<p>This is where I had my first kiss.</p>"],
[othermodel.polygon, "<p>This is my home town.</p>"],
[othermodel.point, {
'html': "<p>Special style for this point.</p>",
'style': {'fill_color': '#00FF00'},
}],
...
])
In a template:
<head> {{ map.media }} </head>
<body>... {{ map }} ...</body>
Use these widgets together to display multi-layer maps outside of forms.
Map constructor:
olwidget.widgets.Map(vector_layers=None, options=None, template=None, layer_names=None)
vector_layers
EditableLayer
or InfoLayer
) to
display on the map.options
template
layer_names
EditableLayer constructor:
olwidget.widgets.EditableLayer(options=None, template=None)
options
template
InfoLayer constructor:
olwidget.widgets.InfoLayer(info=None, options=None, template=None)
info
geometry
, html
] pairs which specify geometries and the
html contents of popups when those geometries are clicked. html
can
also be a dict such as { html: "...", style: {}}
. The style
parameter is used for individual styling of the geometry within the layer.options
An example of a widget with two info layers:
mymap = Map([
InfoLayer([["POINT (0 0)", "the origin"]], {'name': 'origin'}),
InfoLayer([["POINT (1 0)", "one degree off"]], {'name': 'a bit off'}),
], { overlay_style: {'fill_color': '#ffffff'} })
In a template:
<head> ... {{ mymap.media }} ... </head>
<body> ... {{ mymap }} ... </body>
MapModelForm
is an extension of the built-in ModelForm type which
adds
support for maps. MapModelForm
subclasses can possess two extra parameters
in their inner Meta
class – an optional maps
parameter which specifies
which fields to use with which maps, and an options
parameter that specifies
global map options.
The following is a simple example using a separate map for each field, and the same appearance for all maps:
# models.py
class MyModel(models.Model):
geom1 = models.PointField()
geom2 = models.LineStringField()
geom3 = models.GeometryCollectionField()
# forms.py
from olwidget.forms import MapModelForm
from models import MyModel
class MyForm(MapModelForm):
class Meta:
model = MyModel
options = { 'layers': ['google.streets'] }
To edit multiple fields in a single map, specify the maps
parameter. The
following will construct a form with 2 maps, the first editing geom1
and
geom2
fields and using Google Streets as a base layer, and the second
editing geom3
and using default options:
class MyForm(MapModelForm):
class Meta:
model = MyModel
maps = (
(('geom1', 'geom2'), { 'layers': ['google.streets'] }),
(('geom3', ), None),
)
To define options for particular fields, override the field definition.
from olwidget.forms import MapModelForm
from olwidget.fields import EditableLayerField
class MyForm(MapModelForm):
geom1 = EditableLayerField({'overlay_style': { 'fill_color': "#ff0000" }})
class Meta:
model = MyModel
Using the form in a template is the same as before.
<head> {{ form.media }} </head>
<body> {{ form }} </body>
Multi-layer maps are possible in forms using the MapField
type, which is a
container field for any number of layer fields. The layer fields are
EditableLayerField
or InfoLayerField
types, which allow editing or
display of vector data on the map.
MapField constructor:
olwidget.fields.MapField(fields=None, options=None, layer_names=None, template=None)
fields
EditableLayerField
or
InfoLayerField
) which should appear on the map.options
layer_names
form.cleaned_data
will not use
these names, and will instead contain a list of the values in each layer
using the MapField’s declared name.template
The name of a custom template to render the map. It will receive the context:
{'id': html id for the map,
'layer_js': an array of javascript invocations from each layer,
'layer_html': an array of html data from each layer,
'map_opts': a JSON string of options for the map.
}
EditableLayerField constructor:
olwidget.fields.EditableLayerField(options=None)
options
Map
defaults.InfoLayerField constructor:
olwidget.fields.InfoLayerField(info=None, options=None)
info
[geometry, html]
pairs for clickable popups. See InfoLayer
for more.options
Map
defaults.The following is an example that constructs a map widget with 3 fields, two of them editable. It uses both layer-specific options and global map options:
from django import forms
from olwidget.fields import MapField, EditableLayerField, InfoLayerField
class MyForm(forms.Form):
country = MapField([
EditableLayerField({'geometry': 'polygon', 'name': 'boundary'}),
EditableLayerField({'geometry': 'point', 'name': 'capital'}),
InfoLayerField([["Point (0 0)", "Of interest"]], {'name': "Points of interest"}),
], {
'overlay_style': {
'fill_color': '#00ff00',
},
})
In a template:
<head>... {{ form.media }} ...</head>
<body>... {{ form }} ...</body>
olwidget
has several advantages over the built-in geodjango admin map
implementation, including greater map customization, support for more geometry
types, the ability to edit multiple fields using one map, and the option to
include a map in admin changelist pages, on top of basic usability like
undo/redo and the ability to delete individual vertices.
To use olwidget
for admin, simply use olwidget.admin.GeoModelAdmin
or a
subclass of it as the ModelAdmin type for your model.
Example using olwidget
in admin:
# admin.py
from django.contrib import admin
from olwidget.admin import GeoModelAdmin
from myapp import Restaurant, Owner
# Use the default map
admin.site.register(Restaurant, GeoModelAdmin)
# Customize the map
class MyGeoAdmin(GeoModelAdmin):
options = {
'layers': ['google.streets'],
'default_lat': 44,
'default_lon': -72,
}
admin.site.register(Owner, MyGeoAdmin)
To edit multiple fields using a single map, specify a maps
parameter (with
the same syntax as that used in MapModelForm) with a list of all geometry
fields and which maps they should use and the options those maps should use,
like so:
# model:
class Country(models.Model):
capital = models.PointField()
perimiter = models.PolygonField()
biggest_river = models.LineStringField()
# admin.py
class CountryAdmin(GeoModelAdmin):
options = {
default_lat: -72,
default_lon: 43,
}
maps = (
(('capital', 'perimiter'), { 'layers': ['google.streets'] }),
(('biggest_river',), {'overlay_style': {'stroke_color': "#0000ff"}}),
)
This will tell GeoModelAdmin to construct 2 maps, the first editing capital
and perimiter
fields, and the second editing biggest_river
, with
specific options for each map. Both maps will share the global options
parameter, but can override it by specifying options.
To show a clickable map on the admin changelist page, use the list_map
property to specify which fields to display in the changelist map:
# an example model:
class Tree(models.Model):
location = models.PointField()
root_spread = models.PolygonField()
# admin.py
from django.contrib import admin
from olwidget.admin import GeoModelAdmin
from myapp import Tree
class TreeGeoAdmin(GeoModelAdmin):
list_map = ['location']
admin.site.register(Tree, TreeGeoAdmin)
Options can be set for the changelist map using the list_map_options
property:
class TreeGeoAdmin(GeoModelAdmin):
list_map = ['location']
list_map_options = {
# group nearby points into clusters
'cluster': True,
'cluster_display': 'list',
}
This results in a map like this:
Maps are both important user interface elements, and powerful persuasive data
displays. Consequently, it is necessary to support a high degree of
customization around the appearance of a map. olwidget
does this primarily
through the use of OpenLayers’ style framework. All of olwidget
‘s types accept
an optional options
dict which controls the appearance of the map and
layers.
Layers inherit their styles from both their default parameters, and from those specified for a map:
default layer options < map options < layer options
By contrast, maps only inherit from their default options, and not from layers:
default map options < map options
This allows the map to hold defaults for all layers, but let the layers override them. The following is a list of all available options. Some are specific to map display, and others specific to layer display.
The base default options dict can be defined in your settings file as
OLWIDGET_DEFAULT_OPTIONS
.
layers
(list; default ['osm.mapnik']
)A list of map base layers to include. Choices include:
'osm.mapnik'
, 'osm.osmarender'
'google.streets'
, 'google.physical'
, 'google.satellite'
, 'google.hybrid'
,'ve.road'
, 've.shaded'
, 've.aerial'
, 've.hybrid'
,'wms.map'
, 'wms.nasa'
, 'wms.blank'
(blank map)'yahoo.map'
, 'yahoo.satellie'
, 'yahoo.hybrid'
'cloudmade.<num>'
(where <num>
is the number for a cloudmade
style).Remember to include GOOGLE_API_KEY
, YAHOO_APP_ID
, or
CLOUDMADE_API_KEY
in your settings.py
if you use any of those
layers.
Custom layer types from other providers can be used by listing their
JavaScript construction string in the OLWIDGET_CUSTOM_LAYER_TYPES
setting option. Here’s an example:
OLWIDGET_CUSTOM_LAYER_TYPES = {
'opengeo_osm': """OpenLayers.Layer.WMS(
'OpenStreetMap (OpenGeo)',
'http://maps.opengeo.org/geowebcache/service/wms',
{
layers: 'openstreetmap',
format: 'image/png',
bgColor: '#A1BDC4',
},
{wrapDateLine: true}
)"""
}
We can then access this layer type as opengeo_osm
.
default_lat
(float; default 0)default_lon
(float; default 0)default_zoom
(int; default 4
)zoom_to_data_extent
(True
/False
; default True
)True
, the map will zoom to the extent of its vector data instead of
default_zoom
, default_lat
, and default_lon
. If no vector data
is present, the map will use the defaults.map_div_class
(string; default ''
)map_div_style
(dict, default {width: '600px', height: '400px'}
)map_options
(dict)A dict containing options for the OpenLayers Map constructor. Properties may include:
units
: (string) default 'm'
(meters)projection
: (string) default "EPSG:900913"
(the projection used
by Google, OSM, Yahoo, and VirtualEarth).display_projection
: (string) default "EPSG:4326"
(the latitude
and longitude we’re all familiar with).max_resolution
: (float) default 156543.0339
. Value should be
expressed in the projection specified in projection
.max_extent
: default [-20037508.34, -20037508.34, 20037508.34,
20037508.34]
. Values should be expressed in the projection specified
in projection
.controls
: (array of strings) default ['LayerSwitcher',
'Navigation', 'PanZoom', 'Attribution']
The strings should be class names for map controls,
which will be instantiated without arguments.Any additional parameters available to the OpenLayers.Map.Constructor may be included, and will be passed after converting mixedCaseSyntax to lowercase_with_underscores.
popups_outside
(boolean; default false
)popup_direction
(string; default auto
)The direction from the clicked geometry that a popup will extend. This may be one of:
tr
– top righttl
– top leftbr
– bottom rightbl
– bottom leftauto
– automatically choose direction.Layer options can also be specified at the map level. Any options passed to a layer override the corresponding options from the map.
name
(string; defaults to "data"
)overlay_style
(dict)A dict of style definitions for the geometry overlays. For more on overlay styling, consult the OpenLayers styling documentation. Options include:
fill_color
: (string) HTML color valuefill_opacity
: (float) opacity of overlays from 0 to 1stroke_color
: (string) HTML color valuestroke_opacity
: (float) opacity of strokes from 0 to 1stroke_width
: (int) width in pixels of lines and bordersstroke_linecap
: (string) Default is round
. Options are butt
,
round
, square
.stroke_dashstyle
: (string) Default is solid
. Options are
dot
, dash
, dashdot
, longdash
, longdashdot
, solid
.cursor
: (string) Cursor to be used when mouse is over a feature.
Default is an empty string.point_radius
: (integer) radius of points in pixelsexternal_graphic
: (string) URL of external graphic to use in place of
vector overlaysgraphic_height
: (int) height in pixels of external graphicgraphic_width
: (int) width in pixels of external graphicgraphic_x_offset
: (int) x offset in pixels of external graphicgraphic_y_offset
: (int) y offset in pixels of external graphicgraphic_opacity
: (float) opacity of external graphic from 0 to 1.graphic_name
: (string) Name of symbol to be used for a point mark.display
: (string) Can be set to none
to hide features from
rendering.overlay_style_context
(dict)overlay_style
. See
this example for a
javascript usage example. Note that javascript functions can’t be
specified directly from python in an options
dict, as the serializer
will interpret them as strings. Instead, they must be specified manually
in a template.geometry
(Array or string; default 'point'
)'point'
,
'linestring'
, and 'polygon'
. To allow multiple geometries, use an
array such as ['point', 'linestring', 'polygon']
.isCollection
(boolean, default false
)hide_textarea
(boolean; default true
)editable
(boolean, default true
)InfoLayer
types.cluster
(boolean; default false
)cluster_display
(string; default 'paginate'
)The way HTML from clustered points is handled.
'list'
– constructs an unordered list of contents'paginate'
– adds a pagination control to the popup to click through
the different points’ HTML.