Interactive Polygon Field Widget
A widget for GeoDjango PolygonField built on the Leaflet
open-source map library. It keeps the same UX elements as the Leaflet Point Field widget
(toolbar buttons, footer delete action) and draws polygons using pure Leaflet — no extra
JavaScript dependency.
Preview
Requirements
There is no requirement to use Leaflet open-source interactive map JavaScript library.
Key Features
Draw Polygon Action: Click the “Draw Polygon” button to enter drawing mode, then click the map to drop vertices. Close the ring by clicking the first vertex or double-clicking the map.
Editable Vertices: Once a polygon is created, each vertex is a draggable handle. Dragging a vertex reshapes the polygon and updates the field value.
Edit Coordinates: The “Edit Coordinates” dropdown exposes a textarea showing the polygon as WKT.
You can paste a POLYGON((...)) (WKT) and press “Done” to render it.
Use My Location Action: Recenters the map on the user’s current location.
Location Search: A search bar (powered by leaflet-geosearch) lets users find a place by name/address; selecting a result pans/zooms the map there so the area can be drawn. Configurable via the geoSearch setting (default provider: OpenStreetMap / Nominatim, no API key required).
Settings
Default Settings
MAP_WIDGETS = {
"Leaflet": {
"PolygonField": {
"mapOptions": {"zoom": 12, "scrollWheelZoom": False, "zoomControl": True},
"tileLayer": {
"urlTemplate": "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
"options": {"maxZoom": 20},
},
"polygonOptions": {"color": "#3388ff", "weight": 3, "fillOpacity": 0.2},
"polygonFitZoom": 14,
"fitBoundsOnLoad": True,
"showZoomNavigation": True,
"mapCenterLocation": get_default_center_coordinates(),
}
}
}
mapOptions: Leaflet MapOptions passed to the Leaflet map initialization.
tileLayer: TileLayer source configuration. (Do not override the default settings unless you serve the map tiles from a different source than
openstreetmap.org.)polygonOptions: Leaflet Path styling for the rendered polygon (
color,weight,fillOpacity).geoSearch: Location search bar configuration.
enabled(defaultTrue) toggles the search input;provideris the leaflet-geosearch provider class name (default"OpenStreetMapProvider");providerOptionsis passed to the provider constructor (e.g.{"params": {"key": "YOUR_API_KEY"}}for keyed providers).polygonFitZoom: Maximum zoom used when fitting the map to the polygon bounds or centering on the user location. Default is 14.
fitBoundsOnLoad: Fit the map to the existing polygon bounds when the widget loads. Default is
True.showZoomNavigation: Enable/Disable zoom in/out UI buttons on the map. Default is
True.mapCenterLocation: Initial map center. Defaults to the project timezone based coordinates.
Note
More details about map widget settings usage can be found in the settings guide.
Usage
In the Django project settings file, the MAP_WIDGETS dictionary can be defined to customize the default settings.
MAP_WIDGETS = {
"Leaflet": {
"PolygonField": {
"mapOptions": {"scrollWheelZoom": True},
"polygonOptions": {"color": "#e74c3c", "fillOpacity": 0.3},
}
}
}
Django Admin
import mapwidgets
class ZoneAdmin(admin.ModelAdmin):
list_display = ("name",)
formfield_overrides = {
models.PolygonField: {"widget": mapwidgets.LeafletPolygonFieldWidget}
}
Django Forms
import mapwidgets
class ZoneForm(forms.ModelForm):
class Meta:
model = Zone
fields = ("name", "area")
widgets = {
"area": mapwidgets.LeafletPolygonFieldWidget,
}
Javascript Triggers
The widget fires jQuery events on document when the polygon changes. Each handler receives the
polygon GeoJSON, the widget wrapper selector, and the hidden Django input element.
$(document).on("leafletPolygonFieldWidget:polygonCreate", function (e, geojson, mapWrapSelector, djangoInput) { });
$(document).on("leafletPolygonFieldWidget:polygonChange", function (e, geojson, mapWrapSelector, djangoInput) { });
$(document).on("leafletPolygonFieldWidget:polygonDelete", function (e, value, mapWrapSelector, djangoInput) { });