2 |
3 | This program is free software: you can redistribute it and/or modify
4 | it under the terms of the GNU Lesser General Public License as published by
5 | the Free Software Foundation, either version 3 of the License, or
6 | (at your option) any later version.
7 |
8 | This program is distributed in the hope that it will be useful,
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | GNU General Public License for more details.
12 |
13 | You should have received a copy of the GNU Lesser General Public License
14 | along with this program. If not, see .
15 |
--------------------------------------------------------------------------------
/leaflet/static/leaflet/Control.MiniMap.css:
--------------------------------------------------------------------------------
1 | .leaflet-control-minimap {
2 | border:solid rgba(255, 255, 255, 1.0) 4px;
3 | box-shadow: 0 1px 5px rgba(0,0,0,0.65);
4 | border-radius: 3px;
5 | background: #f8f8f9;
6 | transition: all .2s;
7 | }
8 |
9 | .leaflet-control-minimap a {
10 | background-color: rgba(255, 255, 255, 1.0);
11 | background-repeat: no-repeat;
12 | z-index: 99999;
13 | transition: all .2s;
14 | border-radius: 3px 0px 0px 0px;
15 | }
16 |
17 | .leaflet-control-minimap a.minimized {
18 | -webkit-transform: rotate(180deg);
19 | transform: rotate(180deg);
20 | border-radius: 0px;
21 | }
22 |
23 | .leaflet-control-minimap-toggle-display {
24 | background-image: url("images/toggle.png");
25 | height: 19px;
26 | width: 19px;
27 | position: absolute;
28 | bottom: 0;
29 | right: 0;
30 | }
31 |
--------------------------------------------------------------------------------
/leaflet/templates/leaflet/admin/widget.html:
--------------------------------------------------------------------------------
1 | {% extends "leaflet/widget.html" %}
2 | {% load i18n %}
3 | {% load static from staticfiles %}
4 |
5 |
6 | {% block map_css %}
7 | {{ block.super }}
8 |
9 | /* Fixes for Django base.css */
10 | .module .leaflet-draw ul {
11 | margin-left: 0px;
12 | padding-left: 0px;
13 | }
14 | .module .leaflet-draw ul li {
15 | list-style-type: none;
16 | }
17 | {% endblock map_css %}
18 |
19 |
20 | {% block vars %}
21 | {{ block.super }}
22 |
23 | {% include "leaflet/_leaflet_draw_i18n.js" %}
24 | L.Control.ResetView.TITLE = "{% trans "Reset view" %}";
25 | L.Control.ResetView.ICON = "url({% static "leaflet/images/reset-view.png" %})";
26 | {% endblock vars %}
27 |
28 | {% block map %}
29 |
30 | {{ block.super }}
31 |
32 | {% endblock map %}
33 |
--------------------------------------------------------------------------------
/example/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import os
3 | import sys
4 |
5 | if __name__ == "__main__":
6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mushrooms.settings")
7 | try:
8 | from django.core.management import execute_from_command_line
9 | except ImportError:
10 | # The above import may fail for some other reason. Ensure that the
11 | # issue is really that Django is missing to avoid masking other
12 | # exceptions on Python 2.
13 | try:
14 | import django
15 | except ImportError:
16 | raise ImportError(
17 | "Couldn't import Django. Are you sure it's installed and "
18 | "available on your PYTHONPATH environment variable? Did you "
19 | "forget to activate a virtual environment?"
20 | )
21 | raise
22 | execute_from_command_line(sys.argv)
23 |
--------------------------------------------------------------------------------
/example/mushrooms/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.10.2 on 2016-10-17 21:01
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 | import djgeojson.fields
7 |
8 |
9 | class Migration(migrations.Migration):
10 |
11 | initial = True
12 |
13 | dependencies = [
14 | ]
15 |
16 | operations = [
17 | migrations.CreateModel(
18 | name='MushroomSpot',
19 | fields=[
20 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
21 | ('title', models.CharField(max_length=256)),
22 | ('description', models.TextField()),
23 | ('picture', models.ImageField(upload_to=b'')),
24 | ('geom', djgeojson.fields.PolygonField()),
25 | ],
26 | ),
27 | ]
28 |
--------------------------------------------------------------------------------
/docs/installation.rst:
--------------------------------------------------------------------------------
1 | Installation
2 | ============
3 |
4 | Last stable version:
5 |
6 | ::
7 |
8 | pip install django-leaflet
9 |
10 |
11 | Last development version (master branch):
12 |
13 | ::
14 |
15 | pip install -e git+https://github.com/makinacorpus/django-leaflet.git#egg=django-leaflet
16 |
17 |
18 | Configuration
19 | -------------
20 |
21 | * Add ``leaflet`` to your ``INSTALLED_APPS``
22 |
23 | * Add the HTML header::
24 |
25 | {% load leaflet_tags %}
26 |
27 |
28 | ...
29 | {% leaflet_js %}
30 | {% leaflet_css %}
31 |
32 |
33 | * Add the map in your page, providing a name::
34 |
35 | ...
36 |
37 | ...
38 | {% leaflet_map "yourmap" %}
39 | ...
40 |
41 |
42 | * Your map shows up!
43 |
44 | Example
45 | -------
46 |
47 | Check out the `example project `_
48 | for a complete integration!
49 |
--------------------------------------------------------------------------------
/leaflet/locale/it/LC_MESSAGES/django.po:
--------------------------------------------------------------------------------
1 | # SOME DESCRIPTIVE TITLE.
2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3 | # This file is distributed under the same license as the PACKAGE package.
4 | # FIRST AUTHOR , YEAR.
5 | #
6 | msgid ""
7 | msgstr ""
8 | "Project-Id-Version: PACKAGE VERSION\n"
9 | "Report-Msgid-Bugs-To: \n"
10 | "POT-Creation-Date: 2013-07-05 16:25+0200\n"
11 | "PO-Revision-Date: 2013-08-23 16:19+0100\n"
12 | "Last-Translator: Mathieu Leplatre \n"
13 | "Language-Team: LANGUAGE \n"
14 | "Language: \n"
15 | "MIME-Version: 1.0\n"
16 | "Content-Type: text/plain; charset=UTF-8\n"
17 | "Content-Transfer-Encoding: 8bit\n"
18 | "Plural-Forms: nplurals=2; plural=(n > 1)\n"
19 |
20 | msgid "Reset view"
21 | msgstr "Ripristina vista"
22 |
23 | msgid "OSM"
24 | msgstr "OSM"
25 |
26 | msgid "Background"
27 | msgstr "Sfondo"
28 |
29 | msgid "Spatial extent should be a tuple (minx, miny, maxx, maxy)"
30 | msgstr ""
31 |
32 |
--------------------------------------------------------------------------------
/example/mushrooms/templates/index.html:
--------------------------------------------------------------------------------
1 | {% load leaflet_tags %}
2 |
3 |
4 | {% leaflet_js %}
5 | {% leaflet_css %}
6 |
9 |
29 |
30 |
31 | Mushrooms Spots
32 | {% leaflet_map "main" %}
33 |
34 |
35 |
--------------------------------------------------------------------------------
/leaflet/forms/fields.py:
--------------------------------------------------------------------------------
1 | from __future__ import unicode_literals
2 |
3 | import django
4 |
5 | from .widgets import LeafletWidget
6 |
7 | if django.VERSION >= (1, 6, 0):
8 | from django.contrib.gis.forms.fields import GeometryField as BaseGeometryField
9 | else:
10 | from .backport import GeometryField as BaseGeometryField
11 |
12 |
13 | class GeometryField(BaseGeometryField):
14 | widget = LeafletWidget
15 | geom_type = 'GEOMETRY'
16 |
17 | def __init__(self, *args, **kwargs):
18 | super(GeometryField, self).__init__(*args, **kwargs)
19 | self.widget.geom_type = self.geom_type
20 |
21 |
22 | class GeometryCollectionField(GeometryField):
23 | geom_type = 'GEOMETRYCOLLECTION'
24 |
25 |
26 | class PointField(GeometryField):
27 | geom_type = 'POINT'
28 |
29 |
30 | class MultiPointField(GeometryField):
31 | geom_type = 'MULTIPOINT'
32 |
33 |
34 | class LineStringField(GeometryField):
35 | geom_type = 'LINESTRING'
36 |
37 |
38 | class MultiLineStringField(GeometryField):
39 | geom_type = 'MULTILINESTRING'
40 |
41 |
42 | class PolygonField(GeometryField):
43 | geom_type = 'POLYGON'
44 |
45 |
46 | class MultiPolygonField(GeometryField):
47 | geom_type = 'MULTIPOLYGON'
48 |
--------------------------------------------------------------------------------
/leaflet/tests/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Django Leaflet Tests
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | 0
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/leaflet/templates/leaflet/js.html:
--------------------------------------------------------------------------------
1 | {% load i18n %}
2 | {% load static from staticfiles %}
3 | {% if DEBUG %}
4 |
5 | {% else %}
6 |
7 | {% endif %}
8 | {% if SRID %}
9 |
10 |
11 |
12 | {% endif %}
13 | {% if PLUGINS_JS %}
14 | {% for js in PLUGINS_JS %}
15 |
16 | {% endfor %}
17 | {% endif %}
18 |
19 |
27 |
--------------------------------------------------------------------------------
/leaflet/templates/leaflet/_leaflet_map.html:
--------------------------------------------------------------------------------
1 | {% load i18n %}
2 | {% load static from staticfiles %}
3 | {% if creatediv %}{% endif %}
4 |
26 |
--------------------------------------------------------------------------------
/leaflet/tests/test.extras.js:
--------------------------------------------------------------------------------
1 | var assert = chai.assert;
2 |
3 |
4 | describe('Test Leaflet Extras', function() {
5 |
6 | describe('L.Control.ResetView', function() {
7 |
8 | var map,
9 | control,
10 | button;
11 |
12 | before(function() {
13 | map = L.map('map').fitWorld();
14 |
15 | control = new L.Control.ResetView(L.latLngBounds([[1, 1], [3, 3]]));
16 | control.addTo(map);
17 |
18 | button = control._container.getElementsByTagName('a')[0];
19 | });
20 |
21 | after(function() {
22 | map.removeControl(control);
23 | map.remove();
24 | });
25 |
26 | it("should reset view on button click", function(done) {
27 | var callback = sinon.spy();
28 | map.on('viewreset', callback);
29 | happen.click(button);
30 | assert.isTrue(callback.called);
31 | done();
32 | });
33 |
34 | });
35 |
36 |
37 | describe('L.Map.DjangoMap', function() {
38 |
39 | it("should not fail with minimal options", function(done) {
40 | var map = new L.Map.DjangoMap('map', {djoptions: {layers: []}});
41 | map.remove();
42 | done();
43 | });
44 |
45 | });
46 | });
47 |
--------------------------------------------------------------------------------
/leaflet/static/leaflet/leaflet.ie.css:
--------------------------------------------------------------------------------
1 | .leaflet-vml-shape {
2 | width: 1px;
3 | height: 1px;
4 | }
5 | .lvml {
6 | behavior: url(#default#VML);
7 | display: inline-block;
8 | position: absolute;
9 | }
10 |
11 | .leaflet-control {
12 | display: inline;
13 | }
14 |
15 | .leaflet-popup-tip {
16 | width: 21px;
17 | _width: 27px;
18 | margin: 0 auto;
19 | _margin-top: -3px;
20 |
21 | filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678);
22 | -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)";
23 | }
24 | .leaflet-popup-tip-container {
25 | margin-top: -1px;
26 | }
27 | .leaflet-popup-content-wrapper, .leaflet-popup-tip {
28 | border: 1px solid #999;
29 | }
30 | .leaflet-popup-content-wrapper {
31 | zoom: 1;
32 | }
33 |
34 | .leaflet-control-zoom,
35 | .leaflet-control-layers {
36 | border: 3px solid #999;
37 | }
38 | .leaflet-control-layers-toggle {
39 | }
40 | .leaflet-control-attribution,
41 | .leaflet-control-layers,
42 | .leaflet-control-scale-line {
43 | background: white;
44 | }
45 | .leaflet-zoom-box {
46 | filter: alpha(opacity=50);
47 | }
48 | .leaflet-control-attribution {
49 | border-top: 1px solid #bbb;
50 | border-left: 1px solid #bbb;
51 | }
52 |
--------------------------------------------------------------------------------
/example/mushrooms/urls.py:
--------------------------------------------------------------------------------
1 | """mushrooms URL Configuration
2 |
3 | The `urlpatterns` list routes URLs to views. For more information please see:
4 | https://docs.djangoproject.com/en/1.10/topics/http/urls/
5 | Examples:
6 | Function views
7 | 1. Add an import: from my_app import views
8 | 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home')
9 | Class-based views
10 | 1. Add an import: from other_app.views import Home
11 | 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home')
12 | Including another URLconf
13 | 1. Import the include() function: from django.conf.urls import url, include
14 | 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
15 | """
16 | from django.conf import settings
17 | from django.conf.urls import url
18 | from django.conf.urls.static import static
19 | from django.contrib import admin
20 | from django.views.generic import TemplateView
21 | from djgeojson.views import GeoJSONLayerView
22 |
23 | from .models import MushroomSpot
24 |
25 |
26 | urlpatterns = [
27 | url(r'^admin/', admin.site.urls),
28 | url(r'^$', TemplateView.as_view(template_name='index.html'), name='home'),
29 | url(r'^data.geojson$', GeoJSONLayerView.as_view(model=MushroomSpot, properties=('title', 'description', 'picture_url')), name='data')
30 | ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
31 |
--------------------------------------------------------------------------------
/leaflet/templates/leaflet/widget.html:
--------------------------------------------------------------------------------
1 | {% load leaflet_tags l10n %}
2 | {% load static from staticfiles %}
3 |
4 |
9 |
10 |
37 |
38 | {% if not target_map %}
39 | {% block map %}
40 | {% leaflet_map id_map callback=id_map_callback loadevent=loadevent settings_overrides=settings_overrides %}
41 | {% endblock map %}
42 | {% endif %}
43 |
44 | {% if display_raw %} Geometry:
{% endif %}
45 |
46 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 |
2 | import os
3 | from setuptools import setup, find_packages
4 | import sys
5 |
6 | here = os.path.abspath(os.path.dirname(__file__))
7 | import codecs
8 |
9 | requires = ['Django']
10 | if sys.version_info < (2, 7):
11 | requires += ['ordereddict']
12 |
13 | setup(
14 | name='django-leaflet',
15 | version='0.24.0.dev0',
16 | author='Mathieu Leplatre',
17 | author_email='mathieu.leplatre@makina-corpus.com',
18 | url='https://github.com/makinacorpus/django-leaflet',
19 | download_url="http://pypi.python.org/pypi/django-leaflet/",
20 | description="Use Leaflet in your django projects",
21 | long_description=codecs.open(
22 | os.path.join(
23 | here, 'README.rst'), 'r', 'utf-8').read() + '\n\n' +
24 | codecs.open(
25 | os.path.join(here, 'CHANGES'),
26 | 'r', 'utf-8').read(),
27 | license='LPGL, see LICENSE file.',
28 | install_requires=requires,
29 | extras_require={
30 | 'docs': ['sphinx', 'sphinx-autobuild'],
31 | },
32 | packages=find_packages(),
33 | include_package_data=True,
34 | zip_safe=False,
35 | classifiers=['Topic :: Utilities',
36 | 'Natural Language :: English',
37 | 'Operating System :: OS Independent',
38 | 'Intended Audience :: Developers',
39 | 'Environment :: Web Environment',
40 | 'Framework :: Django',
41 | 'Development Status :: 5 - Production/Stable',
42 | 'Programming Language :: Python :: 2.7',
43 | 'Programming Language :: Python :: 3.3',
44 | 'Programming Language :: Python :: 3.4',
45 | 'Programming Language :: Python :: 3.5'],
46 | )
47 |
--------------------------------------------------------------------------------
/leaflet/static/leaflet/draw/images/spritesheet.svg:
--------------------------------------------------------------------------------
1 |
2 |
28 |
--------------------------------------------------------------------------------
/leaflet/admin.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from __future__ import unicode_literals
3 |
4 | from django.contrib.admin import ModelAdmin
5 | from django.core.exceptions import ImproperlyConfigured
6 |
7 | try:
8 | from djgeojson.fields import GeoJSONField
9 | except ImportError:
10 | GeoJSONField = type(object)
11 | try:
12 | from django.contrib.gis.db.models import GeometryField
13 | except (ImportError, ImproperlyConfigured):
14 | # When GEOS is not installed
15 | GeometryField = type(object)
16 |
17 | from .forms.widgets import LeafletWidget
18 |
19 |
20 | class LeafletGeoAdminMixin(object):
21 | widget = LeafletWidget
22 | map_template = 'leaflet/admin/widget.html'
23 | modifiable = True
24 | map_width = '100%'
25 | map_height = '400px'
26 | display_raw = False
27 | settings_overrides = {}
28 |
29 | def formfield_for_dbfield(self, db_field, **kwargs):
30 | """
31 | Overloaded from ModelAdmin to set Leaflet widget
32 | in form field init params.
33 | """
34 | is_geometry = isinstance(db_field, (GeometryField, GeoJSONField))
35 | is_editable = is_geometry and (db_field.dim < 3 or
36 | self.widget.supports_3d)
37 |
38 | if is_editable:
39 | kwargs.pop('request', None) # unsupported for form field
40 | # Setting the widget with the newly defined widget.
41 | kwargs['widget'] = self._get_map_widget(db_field)
42 | return db_field.formfield(**kwargs)
43 | else:
44 | return super(LeafletGeoAdminMixin, self).formfield_for_dbfield(db_field, **kwargs)
45 |
46 | def _get_map_widget(self, db_field):
47 | """
48 | Overriden LeafletWidget with LeafletGeoAdmin params.
49 | """
50 | class LeafletMap(self.widget):
51 | template_name = self.map_template
52 | include_media = True
53 | geom_type = db_field.geom_type
54 | modifiable = self.modifiable
55 | map_width = self.map_width
56 | map_height = self.map_height
57 | display_raw = self.display_raw
58 | settings_overrides = self.settings_overrides
59 | return LeafletMap
60 |
61 |
62 | class LeafletGeoAdmin(LeafletGeoAdminMixin, ModelAdmin):
63 | pass
64 |
--------------------------------------------------------------------------------
/leaflet/tests/test.forms.js:
--------------------------------------------------------------------------------
1 | var assert = chai.assert;
2 |
3 |
4 | describe('Test Leaflet Forms', function() {
5 |
6 | describe('L.FieldStore', function() {
7 |
8 | afterEach(function () {
9 | document.getElementById('formfield').value = '';
10 | });
11 |
12 | it("should serialize and store", function () {
13 | var store = new L.FieldStore("formfield", {
14 | collection_type: "featureGroup"
15 | });
16 | store.save(L.polyline([[1, 2], [3, 4]]));
17 | assert.equal(store.formfield.value, '{"type":"LineString","coordinates":[[2,1],[4,3]]}');
18 | });
19 | });
20 |
21 |
22 | describe('L.GeometryField', function() {
23 |
24 | it("should detect geometry type", function (done) {
25 | var field = new L.GeometryField({geom_type: 'GEOMETRY'});
26 | assert.isTrue(field.options.is_generic);
27 | field = new L.GeometryField({geom_type: 'POLYGON'});
28 | assert.isTrue(field.options.is_polygon);
29 | field = new L.GeometryField({geom_type: 'LINESTRING'});
30 | assert.isTrue(field.options.is_linestring);
31 | field = new L.GeometryField({geom_type: 'POINT'});
32 | assert.isTrue(field.options.is_point);
33 | done();
34 | });
35 |
36 | describe('Events', function () {
37 |
38 | var map;
39 |
40 | beforeEach(function () {
41 | map = L.map('map').fitWorld();
42 | });
43 |
44 | afterEach(function () {
45 | map.remove();
46 | });
47 |
48 | it("should emit event when field is loaded", function (done) {
49 | var field = new L.GeometryField({
50 | geom_type: 'GEOMETRY',
51 | modifiable: true,
52 | fieldid: 'formfield'
53 | });
54 | map.on('map:loadfield', function (e) {
55 | assert.equal(e.target, map);
56 | assert.equal(e.field, field);
57 | assert.equal(e.fieldid, 'formfield');
58 | assert.isDefined(map.drawControlformfield);
59 | done();
60 | });
61 | field.addTo(map);
62 | });
63 | });
64 | });
65 | });
66 |
--------------------------------------------------------------------------------
/leaflet/utils.py:
--------------------------------------------------------------------------------
1 | from collections import Sequence
2 |
3 | from django.utils.functional import Promise
4 |
5 |
6 | class memoized_lazy_function(Promise):
7 | """
8 | Represents a lazy value which is calculated by calling
9 | func(*args, **kwargs) and then is memoized.
10 |
11 | >>> f = memoized_lazy_function(lambda a: print('.') or a, 'hello')
12 | >>> f()
13 | .
14 | 'hello'
15 | >>> f()
16 | 'hello'
17 | """
18 |
19 | def __init__(self, func, *args, **kwargs):
20 | self._func = func
21 | self._args = args
22 | self._kwargs = kwargs
23 |
24 | def __call__(self):
25 | if not hasattr(self, '_result'):
26 | self._result = self._func(*self._args, **self._kwargs)
27 | return self._result
28 |
29 |
30 | class ListWithLazyItems(Sequence):
31 | """
32 | Mimics a lazy list.
33 |
34 | It keeps items in lazy state and evaluates them when they're
35 | returned.
36 |
37 | An item is considered lazy when it is
38 | a `django.utils.functional.Promise` instance.
39 | """
40 |
41 | def __init__(self, iterable=()):
42 | if isinstance(iterable, ListWithLazyItems):
43 | iterable = iterable._list
44 | self._list = list(iterable)
45 |
46 | def __iter__(self):
47 | for item in self._list:
48 | yield self._resolve_lazy_item(item)
49 |
50 | def __len__(self):
51 | return len(self._list)
52 |
53 | def __getitem__(self, index):
54 | return self._resolve_lazy_item(self._list[index])
55 |
56 | def extend(self, iterable):
57 | if isinstance(iterable, ListWithLazyItems):
58 | iterable = iterable._list
59 | self._list.extend(iterable)
60 |
61 | def __radd__(self, iterable):
62 | lazy_list = type(self)(iterable) # copy
63 | lazy_list.extend(self)
64 | return lazy_list
65 |
66 | def __add__(self, iterable):
67 | lazy_list = type(self)(self) # copy
68 | lazy_list.extend(iterable)
69 | return lazy_list
70 |
71 | @classmethod
72 | def _resolve_lazy_item(cls, item):
73 | if cls.is_lazy_item(item):
74 | item = item()
75 |
76 | return item
77 |
78 | @classmethod
79 | def is_lazy_item(cls, item):
80 | return isinstance(item, Promise)
81 |
82 |
83 | class ListWithLazyItemsRawIterator(ListWithLazyItems):
84 | """
85 | This lazy list yields raw items (i.e. Promises are not resolved)
86 | when iterated.
87 | """
88 |
89 | def __iter__(self):
90 | return iter(self._list)
91 |
--------------------------------------------------------------------------------
/leaflet/templates/leaflet/_leaflet_draw_i18n.js:
--------------------------------------------------------------------------------
1 | {% load i18n %}
2 | L.drawLocal.draw.toolbar.actions.title = "{% trans "Cancel drawing" %}";
3 | L.drawLocal.draw.toolbar.actions.text = "{% trans "Cancel" %}";
4 | L.drawLocal.draw.toolbar.undo.title = "{% trans "Delete last point drawn" %}";
5 | L.drawLocal.draw.toolbar.undo.text = "{% trans "Delete last point" %}";
6 | L.drawLocal.draw.toolbar.buttons.polyline = "{% trans "Draw a polyline" %}";
7 | L.drawLocal.draw.toolbar.buttons.polygon = "{% trans "Draw a polygon" %}";
8 | L.drawLocal.draw.toolbar.buttons.rectangle = "{% trans "Draw a rectangle" %}";
9 | L.drawLocal.draw.toolbar.buttons.circle = "{% trans "Draw a circle" %}";
10 | L.drawLocal.draw.toolbar.buttons.marker = "{% trans "Draw a marker" %}";
11 | L.drawLocal.draw.handlers.circle.tooltip.start = "{% trans "Click and drag to draw circle." %}";
12 | L.drawLocal.draw.handlers.marker.tooltip.start = "{% trans "Click map to place marker." %}";
13 | L.drawLocal.draw.handlers.polygon.tooltip.start = "{% trans "Click to start drawing shape." %}";
14 | L.drawLocal.draw.handlers.polygon.tooltip.cont = "{% trans "Click to continue drawing shape." %}";
15 | L.drawLocal.draw.handlers.polygon.tooltip.end = "{% trans "Click first point to close this shape." %}";
16 | L.drawLocal.draw.handlers.polyline.error = "{% trans "Error: shape edges cannot cross!" %}";
17 | L.drawLocal.draw.handlers.polyline.tooltip.start = "{% trans "Click to start drawing line." %}";
18 | L.drawLocal.draw.handlers.polyline.tooltip.cont = "{% trans "Click to continue drawing line." %}";
19 | L.drawLocal.draw.handlers.polyline.tooltip.end = "{% trans "Click last point to finish line." %}";
20 | L.drawLocal.draw.handlers.rectangle.tooltip.start = "{% trans "Click and drag to draw rectangle." %}";
21 | L.drawLocal.draw.handlers.simpleshape.tooltip.end = "{% trans "Release mouse to finish drawing." %}";
22 |
23 | L.drawLocal.edit.toolbar.actions.save.title = "{% trans "Save changes." %}";
24 | L.drawLocal.edit.toolbar.actions.save.text = "{% trans "Save" %}";
25 | L.drawLocal.edit.toolbar.actions.cancel.title = "{% trans "Cancel editing, discards all changes." %}";
26 | L.drawLocal.edit.toolbar.actions.cancel.text = "{% trans "Cancel" %}";
27 | L.drawLocal.edit.toolbar.buttons.edit = "{% trans "Edit layers" %}";
28 | L.drawLocal.edit.toolbar.buttons.editDisabled = "{% trans "No layers to edit." %}";
29 | L.drawLocal.edit.toolbar.buttons.remove = "{% trans "Delete layers" %}";
30 | L.drawLocal.edit.toolbar.buttons.removeDisabled = "{% trans "No layers to delete." %}";
31 | L.drawLocal.edit.handlers.edit.tooltip.text = "{% trans "Drag handles, or marker to edit feature." %}";
32 | L.drawLocal.edit.handlers.edit.tooltip.subtext = "{% trans "Click cancel to undo changes." %}";
33 | L.drawLocal.edit.handlers.remove.tooltip.text = "{% trans "Click on a feature to remove" %}";
34 |
--------------------------------------------------------------------------------
/docs/advanced.rst:
--------------------------------------------------------------------------------
1 | Advanced usage
2 | ==============
3 |
4 |
5 | ``{% leaflet_map %}`` tag parameters
6 | ------------------------------------
7 |
8 | * ``callback``: javascript function name for initialization callback.
9 | (Default: None).
10 |
11 | * ``fitextent``: control if map initial view shoud be set to extent setting.
12 | (Default: ``True``). Setting fixextent to ``False`` will prevent view reset
13 | and scale controls to be added.
14 |
15 | * ``creatediv``: control if the leaflet map tags creates a new div or not.
16 | (Default: ``True``).
17 | Useful to put the javascript code in the header or footer instead of the
18 | body of the html document. If used, do not forget to create the div manually.
19 |
20 | * ``loadevent``: One or more space-separated *window* events that trigger map initialization.
21 | (Default: ``load``, i.e. all page resources loaded).
22 | If empty values is provided, then map initialization is immediate.
23 | And with a wrong value, the map is never initialized. :)
24 |
25 | * ``settings_overrides``: Map with overrides to the default LEAFLET_CONFIG settings.
26 | (Default: {}).
27 |
28 | Config overrides
29 | ----------------
30 |
31 | It is possible to dynamically override settings in ``LeafletWidget`` init:
32 |
33 | ::
34 |
35 | from leaflet.forms.widgets import LeafletWidget
36 |
37 |
38 | class WeatherStationForm(forms.ModelForm):
39 |
40 | class Meta:
41 | model = WeatherStation
42 | fields = ('name', 'geom')
43 | widgets = {'geom': LeafletWidget(attrs={
44 | 'settings_overrides': {
45 | 'DEFAULT_CENTER': (6.0, 45.0),
46 | }
47 | })}
48 |
49 | For overriding the settings in ``LeafletGeoAdmin``, use set the appropriate property:
50 |
51 | ::
52 |
53 | class WeatherStationAdminAdmin(LeafletGeoAdmin):
54 | settings_overrides = {
55 | 'DEFAULT_CENTER': (6.0, 45.0),
56 | }
57 |
58 |
59 | Projection
60 | ----------
61 |
62 | It is possible to setup the map spatial reference in ``LEAFLET_CONFIG``::
63 |
64 | 'SRID': 2154 # See http://spatialreference.org
65 |
66 | Additional parameter is required to compute scale levels : the tiles extent in
67 | local projection::
68 |
69 | 'TILES_EXTENT': [924861,6375196,985649,6448688],
70 |
71 | For more information, `have a look at this example `_.
72 |
73 | Example of TileCache configuration compatible with Leaflet:
74 |
75 | ::
76 |
77 | [scan-portrait]
78 | type=WMSLayer
79 | layers=scan100,scan25
80 | url=http://server/wms?
81 | extension=jpg
82 | tms_type=google
83 | srs=EPSG:2154
84 | bbox=924861,6375196,985649,6448688
85 |
86 | [cache]
87 | type=GoogleDisk
88 | expire=2592000
89 | base=/tmp/tiles
90 |
91 |
92 | By default, *django-leaflet* will try to load the spatial reference from your static
93 | files at "proj4js/{{ srid }}.js". If it fails, it will eventually rely on
94 | ``_.
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Created by https://www.gitignore.io/api/django,intellij+iml,python
3 |
4 | ### Django ###
5 | *.log
6 | *.pot
7 | *.pyc
8 | __pycache__/
9 | local_settings.py
10 | db.sqlite3
11 | media
12 |
13 | ### Intellij+iml ###
14 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
15 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
16 |
17 | # User-specific stuff:
18 | .idea/**/workspace.xml
19 | .idea/**/tasks.xml
20 |
21 | # Sensitive or high-churn files:
22 | .idea/**/dataSources/
23 | .idea/**/dataSources.ids
24 | .idea/**/dataSources.xml
25 | .idea/**/dataSources.local.xml
26 | .idea/**/sqlDataSources.xml
27 | .idea/**/dynamic.xml
28 | .idea/**/uiDesigner.xml
29 |
30 | # Gradle:
31 | .idea/**/gradle.xml
32 | .idea/**/libraries
33 |
34 | # Mongo Explorer plugin:
35 | .idea/**/mongoSettings.xml
36 |
37 | ## File-based project format:
38 | *.iws
39 |
40 | ## Plugin-specific files:
41 |
42 | # IntelliJ
43 | /out/
44 |
45 | # mpeltonen/sbt-idea plugin
46 | .idea_modules/
47 |
48 | # JIRA plugin
49 | atlassian-ide-plugin.xml
50 |
51 | # Crashlytics plugin (for Android Studio and IntelliJ)
52 | com_crashlytics_export_strings.xml
53 | crashlytics.properties
54 | crashlytics-build.properties
55 | fabric.properties
56 |
57 | ### Intellij+iml Patch ###
58 | # Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
59 |
60 | *.iml
61 | modules.xml
62 | .idea/misc.xml
63 | *.ipr
64 |
65 | ### Python ###
66 | # Byte-compiled / optimized / DLL files
67 | *.py[cod]
68 | *$py.class
69 |
70 | # C extensions
71 | *.so
72 |
73 | # Distribution / packaging
74 | .Python
75 | env/
76 | build/
77 | develop-eggs/
78 | dist/
79 | downloads/
80 | eggs/
81 | .eggs/
82 | lib/
83 | lib64/
84 | parts/
85 | sdist/
86 | var/
87 | wheels/
88 | *.egg-info/
89 | .installed.cfg
90 | *.egg
91 |
92 | # PyInstaller
93 | # Usually these files are written by a python script from a template
94 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
95 | *.manifest
96 | *.spec
97 |
98 | # Installer logs
99 | pip-log.txt
100 | pip-delete-this-directory.txt
101 |
102 | # Unit test / coverage reports
103 | htmlcov/
104 | .tox/
105 | .coverage
106 | .coverage.*
107 | .cache
108 | nosetests.xml
109 | coverage.xml
110 | *,cover
111 | .hypothesis/
112 |
113 | # Translations
114 | *.mo
115 |
116 | # Django stuff:
117 |
118 | # Flask stuff:
119 | instance/
120 | .webassets-cache
121 |
122 | # Scrapy stuff:
123 | .scrapy
124 |
125 | # Sphinx documentation
126 | docs/_build/
127 |
128 | # PyBuilder
129 | target/
130 |
131 | # Jupyter Notebook
132 | .ipynb_checkpoints
133 |
134 | # pyenv
135 | .python-version
136 |
137 | # celery beat schedule file
138 | celerybeat-schedule
139 |
140 | # dotenv
141 | .env
142 |
143 | # virtualenv
144 | .venv
145 | venv/
146 | ENV/
147 |
148 | # Spyder project settings
149 | .spyderproject
150 |
151 | # Rope project settings
152 | .ropeproject
153 |
154 | # End of https://www.gitignore.io/api/django,intellij+iml,python
155 |
156 | /.idea
--------------------------------------------------------------------------------
/leaflet/locale/de/LC_MESSAGES/django.po:
--------------------------------------------------------------------------------
1 | # SOME DESCRIPTIVE TITLE.
2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3 | # This file is distributed under the same license as the PACKAGE package.
4 | # FIRST AUTHOR , YEAR.
5 | #
6 | msgid ""
7 | msgstr ""
8 | "Project-Id-Version: PACKAGE VERSION\n"
9 | "Report-Msgid-Bugs-To: \n"
10 | "POT-Creation-Date: 2013-07-05 16:25+0200\n"
11 | "PO-Revision-Date: 2013-09-02 13:41+0100\n"
12 | "Last-Translator: Mathieu Leplatre \n"
13 | "Language-Team: LANGUAGE \n"
14 | "Language: \n"
15 | "MIME-Version: 1.0\n"
16 | "Content-Type: text/plain; charset=UTF-8\n"
17 | "Content-Transfer-Encoding: 8bit\n"
18 | "Plural-Forms: nplurals=2; plural=(n > 1)\n"
19 |
20 | msgid "Reset view"
21 | msgstr "Ansicht zurücksetzen"
22 |
23 | msgid "OSM"
24 | msgstr "OSM"
25 |
26 | msgid "Background"
27 | msgstr "Hintergrund"
28 |
29 | msgid "Räumliche Ausdehnung sollte ein Tupel sein (minx, miny, maxx, maxy)"
30 | msgstr ""
31 |
32 | msgid "Draw a polyline"
33 | msgstr "Polylinie zeichnen"
34 |
35 | msgid "Draw a polygon"
36 | msgstr "Polygon zeichnen"
37 |
38 | msgid "Draw a rectangle"
39 | msgstr "Rechteck zeichnen"
40 |
41 | msgid "Draw a circle"
42 | msgstr "Kreis zeichnen"
43 |
44 | msgid "Draw a marker"
45 | msgstr "Marker zeichnen"
46 |
47 | msgid "Click and drag to draw circle."
48 | msgstr "Klicken und ziehen, um einen Kreis zu zeichnen."
49 |
50 | msgid "Click map to place marker."
51 | msgstr "Karte anklicken, um Marker zu platzieren."
52 |
53 | msgid "Click to start drawing shape."
54 | msgstr "Klicken, um Polygon zu beginnen."
55 |
56 | msgid "Click to continue drawing shape."
57 | msgstr "Klicken, um Polygon weiter zu zeichnen."
58 |
59 | msgid "Click first point to close this shape."
60 | msgstr "Ersten Punkt anklicken, um dieses Polygon zu schließen."
61 |
62 | msgid "Fehler: Formkanten nicht überqueren!"
63 | msgstr ""
64 |
65 | msgid "Click to start drawing line."
66 | msgstr "Klicken, um Polylinie zu beginnen."
67 |
68 | msgid "Click to continue drawing line."
69 | msgstr "Klicken, um Zeichnung der Polylinie fortzusetzen."
70 |
71 | msgid "Click last point to finish line."
72 | msgstr "Letzten Punkt anklicken, um Polylinie zu beenden."
73 |
74 | msgid "Click and drag to draw rectangle."
75 | msgstr "Klicken und ziehen, um Rechteck zu zeichnen."
76 |
77 | msgid "Release mouse to finish drawing."
78 | msgstr "Maustaste loslassen, um die Zeichnung abzuschließen."
79 |
80 | msgid "Save changes."
81 | msgstr "Änderungen speichern."
82 |
83 | msgid "Save"
84 | msgstr "Speichern"
85 |
86 | msgid "Cancel editing, discards all changes."
87 | msgstr "Bearbeitung abbrechen, alle Änderungen werden verworfen."
88 |
89 | msgid "Cancel"
90 | msgstr "Abbrechen"
91 |
92 | msgid "Cancel drawing"
93 | msgstr "Zeichnen abbrechen"
94 |
95 | msgid "Edit layers"
96 | msgstr "Ebenen bearbeiten"
97 |
98 | msgid "Delete layers"
99 | msgstr "Ebenen löschen"
100 |
101 | msgid "Drag handles, or marker to edit feature."
102 | msgstr "Verschiebe Ziehpunkte oder Marker um Feature zu bearbeiten."
103 |
104 | msgid "Click cancel to undo changes."
105 | msgstr "Abbrechen klicken, um Änderungen rückgängig zu machen."
106 |
107 | msgid "Click on a feature to remove"
108 | msgstr "Feature anklicken, um es zu entfernen."
109 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | ==============
2 | Django Leaflet
3 | ==============
4 |
5 | See the `documentation `_ for more information.
6 |
7 | *django-leaflet* allows you to use `Leaflet `_
8 | in your `Django `_ projects.
9 |
10 | It embeds Leaflet version *1.0.3*.
11 |
12 | .. image:: https://readthedocs.org/projects/django-leaflet/badge/?version=latest
13 | :target: http://django-leaflet.readthedocs.io/en/latest/?badge=latest
14 | :alt: Documentation Status
15 |
16 | .. image:: https://img.shields.io/pypi/v/django-leaflet.svg
17 | :target: https://pypi.python.org/pypi/django-leaflet
18 |
19 | .. image:: https://img.shields.io/pypi/dm/django-leaflet.svg
20 | :target: https://pypi.python.org/pypi/django-leaflet
21 |
22 | .. image:: https://travis-ci.org/makinacorpus/django-leaflet.png
23 | :target: https://travis-ci.org/makinacorpus/django-leaflet
24 |
25 | .. image:: https://coveralls.io/repos/makinacorpus/django-leaflet/badge.png
26 | :target: https://coveralls.io/r/makinacorpus/django-leaflet
27 |
28 |
29 | Main purposes of having a python package for the Leaflet Javascript library :
30 |
31 | * Install and enjoy ;
32 | * Do not embed Leaflet assets in every Django project ;
33 | * Enjoy geometry edition with Leaflet form widget ;
34 | * Control apparence and settings of maps from Django settings (e.g. at deployment) ;
35 | * Reuse Leaflet map initialization code (e.g. local projections) ;
36 |
37 | :note:
38 |
39 | *django-leaflet* is compatible with `django-geojson `_ fields, which
40 | allow handling geographic data without spatial database.
41 |
42 | =========
43 | TUTORIALS
44 | =========
45 |
46 | * `GeoDjango maps with Leaflet `_
47 |
48 |
49 | =======
50 | AUTHORS
51 | =======
52 |
53 | * `Mathieu Leplatre `_
54 | * `Ariel Núñez `_
55 | * `Boris Chervenkov `_
56 | * `Marco Badan `_
57 | * `Bruno Renié `_
58 | * `Simon Thépot `_
59 | * `Thibault Jouannic `_
60 | * `jnm `_
61 | * `Manel Clos `_
62 | * `Gaël Utard `_
63 | * `Alex Marandon `_
64 | * `ollb `_
65 | * `smcoll `_
66 | * `jnm `_
67 | * `OKso `_
68 | * `Florent Lebreton `_
69 | * `rgreenemun `_
70 | * `Marco Badan `_
71 | * David Martinez Morata
72 | * `NotSqrt `_
73 | * `Dylan Verheul `_
74 | * `Mactory `_
75 | * `Petr Dlouhy `_
76 | * `Kostya Esmukov `_
77 | * Yann Fouillat (alias Gagaro)
78 |
79 | |makinacom|_
80 |
81 | .. |makinacom| image:: http://depot.makina-corpus.org/public/logo.gif
82 | .. _makinacom: http://www.makina-corpus.com
83 |
84 | =======
85 | LICENSE
86 | =======
87 |
88 | * Lesser GNU Public License
89 | * Leaflet Copyright - 2010-2011 CloudMade, Vladimir Agafonkin
90 |
--------------------------------------------------------------------------------
/leaflet/locale/hu/LC_MESSAGES/django.po:
--------------------------------------------------------------------------------
1 | msgid ""
2 | msgstr ""
3 | "Project-Id-Version: \n"
4 | "Report-Msgid-Bugs-To: \n"
5 | "POT-Creation-Date: 2013-07-05 16:25+0200\n"
6 | "PO-Revision-Date: 2017-10-03 15:43+0200\n"
7 | "Plural-Forms: nplurals=2; plural=(n != 1);\n"
8 | "MIME-Version: 1.0\n"
9 | "Content-Type: text/plain; charset=UTF-8\n"
10 | "Content-Transfer-Encoding: 8bit\n"
11 | "X-Poedit-SourceCharset: UTF-8\n"
12 | "X-Generator: Poedit 1.8.12\n"
13 | "Language-Team: \n"
14 | "Last-Translator: Miklos Horvath \n"
15 | "Language: hu_HU\n"
16 |
17 | msgid "Reset view"
18 | msgstr "Nézet visszavonása"
19 |
20 | msgid "OSM"
21 | msgstr "OSM"
22 |
23 | msgid "Background"
24 | msgstr "Háttér"
25 |
26 | msgid "Spatial extent should be a tuple (minx, miny, maxx, maxy)"
27 | msgstr "A térbeli kiterjedésnek tuple-nek kell lennie (minx, miny, maxx, maxy)"
28 |
29 | msgid "Draw a polyline"
30 | msgstr "Vonallánc rajzolása"
31 |
32 | msgid "Draw a polygon"
33 | msgstr "Sokszög rajzolása"
34 |
35 | msgid "Draw a rectangle"
36 | msgstr "Négyzet rajzolása"
37 |
38 | msgid "Draw a circle"
39 | msgstr "Kör rajzolása"
40 |
41 | msgid "Draw a marker"
42 | msgstr "Jelző rajzolása"
43 |
44 | msgid "Click and drag to draw circle."
45 | msgstr "Kattints és húzz a kör rajzolásához"
46 |
47 | msgid "Click map to place marker."
48 | msgstr "Kattints a térképre egy jelző elhelyezéséhez"
49 |
50 | msgid "Click to start drawing shape."
51 | msgstr "Kattints a formarajzolás elkezdéséhez"
52 |
53 | msgid "Click to continue drawing shape."
54 | msgstr "Kattints a formarajzolás folytatásához"
55 |
56 | msgid "Click first point to close this shape."
57 | msgstr "Kattints az első pontra ennek a formának a lezárásához"
58 |
59 | msgid "Error: shape edges cannot cross!"
60 | msgstr "Hiba: forma élek nem kereszteződnek!"
61 |
62 | msgid "Click to start drawing line."
63 | msgstr "Kattints a vonal rajzolásának megkezdéséhez"
64 |
65 | msgid "Click to continue drawing line."
66 | msgstr "Kattints a vonal rajzolásának folytatásához"
67 |
68 | msgid "Click last point to finish line."
69 | msgstr "Kattints az utolsó pontra a vonal befejezéséhez"
70 |
71 | msgid "Click and drag to draw rectangle."
72 | msgstr "Kattints és húzz a négyszög rajzolásához"
73 |
74 | msgid "Release mouse to finish drawing."
75 | msgstr "Engedd el az egeret a rajzolás befejezéséhez"
76 |
77 | msgid "Save changes."
78 | msgstr "Változások mentése."
79 |
80 | msgid "Save"
81 | msgstr "Mentés"
82 |
83 | msgid "Cancel editing, discards all changes."
84 | msgstr "Szerkesztés megszakítása, minden változás eldobása."
85 |
86 | msgid "Cancel"
87 | msgstr "Megszakítás"
88 |
89 | msgid "Cancel drawing"
90 | msgstr "Rajzolás megszakítása"
91 |
92 | msgid "Delete last point drawn"
93 | msgstr "Utolsó rajzolt pont törlése"
94 |
95 | msgid "Delete last point"
96 | msgstr "Utolsó pont törlése"
97 |
98 | msgid "Edit layers"
99 | msgstr "Rétegek szerkesztése"
100 |
101 | msgid "No layers to edit."
102 | msgstr "Nincsenek szerkeszthető rétegek"
103 |
104 | msgid "No layers to delete."
105 | msgstr "Nincsenek törölhető rétegek"
106 |
107 | msgid "Delete layers"
108 | msgstr "Rétegek törlése"
109 |
110 | msgid "Drag handles, or marker to edit feature."
111 | msgstr ""
112 |
113 | msgid "Click cancel to undo changes."
114 | msgstr "Kattints a megszakításra a változások visszavonásához"
115 |
116 | msgid "Click on a feature to remove"
117 | msgstr ""
118 |
--------------------------------------------------------------------------------
/leaflet/locale/cs/LC_MESSAGES/django.po:
--------------------------------------------------------------------------------
1 | # SOME DESCRIPTIVE TITLE.
2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3 | # This file is distributed under the same license as the PACKAGE package.
4 | # FIRST AUTHOR , YEAR.
5 | #
6 | msgid ""
7 | msgstr ""
8 | "Project-Id-Version: PACKAGE VERSION\n"
9 | "Report-Msgid-Bugs-To: \n"
10 | "POT-Creation-Date: 2013-07-05 16:25+0200\n"
11 | "PO-Revision-Date: 2013-09-02 13:41+0100\n"
12 | "Last-Translator: Petr Dlouhý \n"
13 | "Language-Team: LANGUAGE \n"
14 | "Language: \n"
15 | "MIME-Version: 1.0\n"
16 | "Content-Type: text/plain; charset=UTF-8\n"
17 | "Content-Transfer-Encoding: 8bit\n"
18 | "Plural-Forms: nplurals=2; plural=(n > 1)\n"
19 |
20 | msgid "Reset view"
21 | msgstr "Resetovat pohled"
22 |
23 | msgid "OSM"
24 | msgstr "OSM"
25 |
26 | msgid "Background"
27 | msgstr "Pozadí"
28 |
29 | msgid "Spatial extent should be a tuple (minx, miny, maxx, maxy)"
30 | msgstr "Rozsah mapy by měl být udaný jako výraz (minx, miny, maxx, maxy)"
31 |
32 | msgid "Draw a polyline"
33 | msgstr "Nakreslit trasu"
34 |
35 | msgid "Draw a polygon"
36 | msgstr "Nakreslit polygon"
37 |
38 | msgid "Draw a rectangle"
39 | msgstr "Nakreslit obdélník"
40 |
41 | msgid "Draw a circle"
42 | msgstr "Nakreslit kruh"
43 |
44 | msgid "Draw a marker"
45 | msgstr "Nakreslit značku"
46 |
47 | msgid "Click and drag to draw circle."
48 | msgstr "Pro nakreslení kruhu klikněte a táhněte."
49 |
50 | msgid "Click map to place marker."
51 | msgstr "Pro nakreslení značky klikněte a táhněte."
52 |
53 | msgid "Click to start drawing shape."
54 | msgstr "Nakreslení útvaru začnete kliknutím."
55 |
56 | msgid "Click to continue drawing shape."
57 | msgstr "Pro pokračování v malování útvaru klikněte."
58 |
59 | msgid "Click first point to close this shape."
60 | msgstr "Klikněte na první bod pro ukončení kterslení tohoto útvaru"
61 |
62 | msgid "Error: shape edges cannot cross!"
63 | msgstr "Chyba: hrany útvaru se nesmějí křižit!"
64 |
65 | msgid "Click to start drawing line."
66 | msgstr "Kliknutím do mapy začnete kreslit trasu."
67 |
68 | msgid "Click to continue drawing line."
69 | msgstr "Kliknutím pokračujete v kreslení trasy. Tažením posunujete mapu."
70 |
71 | msgid "Click last point to finish line."
72 | msgstr "Druhým kliknutím na poslední bod zkompletujete trasu."
73 |
74 | msgid "Click and drag to draw rectangle."
75 | msgstr "Kliknutím a tažením nakreslíte obdélník."
76 |
77 | msgid "Release mouse to finish drawing."
78 | msgstr "Pro ukončení kresby pusťte tlačítko myši."
79 |
80 | msgid "Save changes."
81 | msgstr "Uložit změny."
82 |
83 | msgid "Save"
84 | msgstr "Uložit"
85 |
86 | msgid "Cancel editing, discards all changes."
87 | msgstr "Zrušit úpravy, zahodit veškeré změny."
88 |
89 | msgid "Cancel"
90 | msgstr "Zrušit"
91 |
92 | msgid "Cancel drawing"
93 | msgstr "Zrušit kresbu"
94 |
95 | msgid "Edit layers"
96 | msgstr "Upravit objekty"
97 |
98 | msgid "Delete layers"
99 | msgstr "Smazat objekty"
100 |
101 | msgid "Delete last point drawn"
102 | msgstr "Smazat poslední nakreslený bod"
103 |
104 | msgid "Delete last point"
105 | msgstr "Smazat poslední bod"
106 |
107 | msgid "Drag handles, or marker to edit feature."
108 | msgstr "Tažením za bod upravíte objekt; tažením za fiktivní bod v půlce hrany přidáte další bod."
109 |
110 | msgid "Click cancel to undo changes."
111 | msgstr "Kliknutím na zrušit vrátíte změny."
112 |
113 | msgid "Click on a feature to remove"
114 | msgstr "Kliknutím na objekt ho zrušíte"
115 |
116 |
--------------------------------------------------------------------------------
/leaflet/locale/he/LC_MESSAGES/django.po:
--------------------------------------------------------------------------------
1 | msgid ""
2 | msgstr ""
3 | "Project-Id-Version: django-leaflet\n"
4 | "Report-Msgid-Bugs-To: \n"
5 | "Last-Translator: Udi Oron \n"
6 | "Language-Team: \n"
7 | "Language: he\n"
8 | "MIME-Version: 1.0\n"
9 | "Content-Type: text/plain; charset=UTF-8\n"
10 | "Content-Transfer-Encoding: 8bit\n"
11 | "Plural-Forms: nplurals=2; plural=(n != 1);\n"
12 |
13 | msgid "OSM"
14 | msgstr ""
15 |
16 | msgid "Remove TILES_URL and keep TILES value."
17 | msgstr ""
18 |
19 | msgid "Background"
20 | msgstr "רקע"
21 |
22 | msgid "Spatial extent should be a tuple (minx, miny, maxx, maxy)"
23 | msgstr ""
24 |
25 | msgid "No geometry value provided."
26 | msgstr "לא סופק ערך גיאומטרי."
27 |
28 | msgid "Invalid geometry value."
29 | msgstr "ערך גאומטרי שגוי."
30 |
31 | msgid "Invalid geometry type."
32 | msgstr "סוג גיאומטרי שגוי."
33 |
34 | msgid ""
35 | "An error occurred when transforming the geometry to the SRID of the geometry "
36 | "form field."
37 | msgstr "הייתה בעיה עם השינוי של הצורה לסוג של השדה."
38 |
39 | msgid "Cancel drawing"
40 | msgstr "ביטול ציור"
41 |
42 | msgid "Cancel"
43 | msgstr "ביטול"
44 |
45 | msgid "Delete last point drawn"
46 | msgstr "מחיקת הנקודה האחרונה שצוירה"
47 |
48 | msgid "Delete last point"
49 | msgstr "מחיקת הנקודה האחרונה"
50 |
51 | msgid "Draw a polyline"
52 | msgstr "ציור קו"
53 |
54 | msgid "Draw a polygon"
55 | msgstr "ציור פוליגון"
56 |
57 | msgid "Draw a rectangle"
58 | msgstr "ציור מרובע"
59 |
60 | msgid "Draw a circle"
61 | msgstr "ציור עיגול"
62 |
63 | msgid "Draw a marker"
64 | msgstr "ציור נקודת ציון"
65 |
66 | msgid "Click and drag to draw circle."
67 | msgstr "לחץ וגרור בכדי לצייר עיגול."
68 |
69 | msgid "Click map to place marker."
70 | msgstr "לחץ על המפה לציור נקודת ציון."
71 |
72 | msgid "Click to start drawing shape."
73 | msgstr "לחץ על מנת להתחיל לצייר צורה חדשה."
74 |
75 | msgid "Click to continue drawing shape."
76 | msgstr "לחץ בכדי להמשיך לצייר"
77 |
78 | msgid "Click first point to close this shape."
79 | msgstr "לחץ על הנקודה הראשונה בכדי לסגור את הצורה."
80 |
81 | msgid "Error: shape edges cannot cross!"
82 | msgstr "שגיאה: דפנות הצורה אינם יכולים לחצות זה את זה!"
83 |
84 | msgid "Click to start drawing line."
85 | msgstr "לחץ בכדי להתחיל לצייר קו."
86 |
87 | msgid "Click to continue drawing line."
88 | msgstr "לחץ על מנת להמשיך לצייר את הקו."
89 |
90 | msgid "Click last point to finish line."
91 | msgstr "לחץ על הנקודה האחרונה שנית על מנת לסיים את הקו."
92 |
93 | msgid "Click and drag to draw rectangle."
94 | msgstr "לחץ וגרור על מנת לצייר מרובע."
95 |
96 | msgid "Release mouse to finish drawing."
97 | msgstr "שחרר את הלחצן על מנת לסיים את הציור."
98 |
99 | msgid "Save changes."
100 | msgstr "שמירת שינויים."
101 |
102 | msgid "Save"
103 | msgstr "שמור"
104 |
105 | msgid "Cancel editing, discards all changes."
106 | msgstr "בטל עריכה, כל השינויים יבוטלו."
107 |
108 | msgid "Edit layers"
109 | msgstr "עריכת שכבות"
110 |
111 | msgid "No layers to edit."
112 | msgstr "אין שכבות לעריכה"
113 |
114 | msgid "Delete layers"
115 | msgstr "מחיקת שכבות"
116 |
117 | msgid "No layers to delete."
118 | msgstr "אין שכבות למחיקה"
119 |
120 | msgid "Drag handles, or marker to edit feature."
121 | msgstr "גרור נקודות שליטה או את נקודת הציון על מנת לערוך פריט."
122 |
123 | msgid "Click cancel to undo changes."
124 | msgstr "לחץ על ביטול בכדי לבטל את השינויים."
125 |
126 | msgid "Click on a feature to remove"
127 | msgstr "לחץ על פריט בכדי להסירו."
128 |
129 | msgid "Reset view"
130 | msgstr "איפוס תצוגה"
131 |
--------------------------------------------------------------------------------
/leaflet/locale/fr/LC_MESSAGES/django.po:
--------------------------------------------------------------------------------
1 | # SOME DESCRIPTIVE TITLE.
2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3 | # This file is distributed under the same license as the PACKAGE package.
4 | # FIRST AUTHOR , YEAR.
5 | #
6 | msgid ""
7 | msgstr ""
8 | "Project-Id-Version: PACKAGE VERSION\n"
9 | "Report-Msgid-Bugs-To: \n"
10 | "POT-Creation-Date: 2013-07-05 16:25+0200\n"
11 | "PO-Revision-Date: 2013-09-02 13:41+0100\n"
12 | "Last-Translator: Mathieu Leplatre \n"
13 | "Language-Team: LANGUAGE \n"
14 | "Language: \n"
15 | "MIME-Version: 1.0\n"
16 | "Content-Type: text/plain; charset=UTF-8\n"
17 | "Content-Transfer-Encoding: 8bit\n"
18 | "Plural-Forms: nplurals=2; plural=(n > 1)\n"
19 |
20 | msgid "Reset view"
21 | msgstr "Réinitialiser la vue"
22 |
23 | msgid "OSM"
24 | msgstr "OSM"
25 |
26 | msgid "Background"
27 | msgstr "Fond"
28 |
29 | msgid "Spatial extent should be a tuple (minx, miny, maxx, maxy)"
30 | msgstr "L'étendue (spatial extent) doit être un tuple (minx, miny, maxx, maxy)"
31 |
32 | msgid "Draw a polyline"
33 | msgstr "Dessiner une ligne"
34 |
35 | msgid "Draw a polygon"
36 | msgstr "Dessiner un polygone"
37 |
38 | msgid "Draw a rectangle"
39 | msgstr "Dessiner un rectangle"
40 |
41 | msgid "Draw a circle"
42 | msgstr "Dessiner un cercle"
43 |
44 | msgid "Draw a marker"
45 | msgstr "Dessiner un point"
46 |
47 | msgid "Click and drag to draw circle."
48 | msgstr "Cliquer et glisser pour dessiner le cercle."
49 |
50 | msgid "Click map to place marker."
51 | msgstr "Cliquer sur la carte pour placer le point."
52 |
53 | msgid "Click to start drawing shape."
54 | msgstr "Cliquer pour commencer le dessin de la forme."
55 |
56 | msgid "Click to continue drawing shape."
57 | msgstr "Cliquer pour continuer le dessin de la forme."
58 |
59 | msgid "Click first point to close this shape."
60 | msgstr "Cliquer sur le premier point pour fermer la forme"
61 |
62 | msgid "Error: shape edges cannot cross!"
63 | msgstr "Erreur: les segments ne peuvent se croiser!"
64 |
65 | msgid "Click to start drawing line."
66 | msgstr "Cliquer pour commencer le dessin de la ligne."
67 |
68 | msgid "Click to continue drawing line."
69 | msgstr "Cliquer pour continuer le dessin de la ligne."
70 |
71 | msgid "Click last point to finish line."
72 | msgstr "Cliquer sur le dernier point pour terminer la ligne."
73 |
74 | msgid "Click and drag to draw rectangle."
75 | msgstr "Cliquer et glisser pour dessiner le rectangle."
76 |
77 | msgid "Release mouse to finish drawing."
78 | msgstr "Relâcher la souris pour terminer le dessin."
79 |
80 | msgid "Save changes."
81 | msgstr "Sauvegarder les changements."
82 |
83 | msgid "Save"
84 | msgstr "Sauvegarder"
85 |
86 | msgid "Cancel editing, discards all changes."
87 | msgstr "Annuler l'édition, annule tous les changements."
88 |
89 | msgid "Cancel"
90 | msgstr "Annuler"
91 |
92 | msgid "Cancel drawing"
93 | msgstr "Annuler le dessin"
94 |
95 | msgid "Delete last point drawn"
96 | msgstr "Supprimer le dernier point dessiné"
97 |
98 | msgid "Delete last point"
99 | msgstr "Supprimer le dernier point"
100 |
101 | msgid "Edit layers"
102 | msgstr "Éditer les objets"
103 |
104 | msgid "No layers to edit."
105 | msgstr "Aucun objet à éditer"
106 |
107 | msgid "No layers to delete."
108 | msgstr "Aucun objet à supprimer."
109 |
110 | msgid "Delete layers"
111 | msgstr "Supprimer des objets"
112 |
113 | msgid "Drag handles, or marker to edit feature."
114 | msgstr "Déplacer les poignées ou marqueurs pour éditer la forme."
115 |
116 | msgid "Click cancel to undo changes."
117 | msgstr "Cliquer sur annuler pour annuler les changements."
118 |
119 | msgid "Click on a feature to remove"
120 | msgstr "Cliquer sur une forme pour la supprimer"
121 |
122 |
--------------------------------------------------------------------------------
/example/mushrooms/settings.py:
--------------------------------------------------------------------------------
1 | """
2 | Django settings for mushrooms project.
3 |
4 | Generated by 'django-admin startproject' using Django 1.10.2.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/1.10/topics/settings/
8 |
9 | For the full list of settings and their values, see
10 | https://docs.djangoproject.com/en/1.10/ref/settings/
11 | """
12 |
13 | import os
14 |
15 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
17 |
18 |
19 | # Quick-start development settings - unsuitable for production
20 | # See https://docs.djangoproject.com/en/1.10/howto/deployment/checklist/
21 |
22 | # SECURITY WARNING: keep the secret key used in production secret!
23 | SECRET_KEY = '_omc6hxq40u11no0uvi&g__lzj2n^4-dk#l#i+7+vgng!-bb^)'
24 |
25 | # SECURITY WARNING: don't run with debug turned on in production!
26 | DEBUG = True
27 |
28 | ALLOWED_HOSTS = []
29 |
30 |
31 | # Application definition
32 |
33 | INSTALLED_APPS = [
34 | 'django.contrib.admin',
35 | 'django.contrib.auth',
36 | 'django.contrib.contenttypes',
37 | 'django.contrib.sessions',
38 | 'django.contrib.messages',
39 | 'django.contrib.staticfiles',
40 |
41 | 'leaflet',
42 | 'djgeojson',
43 | 'mushrooms'
44 | ]
45 |
46 | MIDDLEWARE = [
47 | 'django.middleware.security.SecurityMiddleware',
48 | 'django.contrib.sessions.middleware.SessionMiddleware',
49 | 'django.middleware.common.CommonMiddleware',
50 | 'django.middleware.csrf.CsrfViewMiddleware',
51 | 'django.contrib.auth.middleware.AuthenticationMiddleware',
52 | 'django.contrib.messages.middleware.MessageMiddleware',
53 | 'django.middleware.clickjacking.XFrameOptionsMiddleware',
54 | ]
55 |
56 | ROOT_URLCONF = 'mushrooms.urls'
57 |
58 | TEMPLATES = [
59 | {
60 | 'BACKEND': 'django.template.backends.django.DjangoTemplates',
61 | 'DIRS': [],
62 | 'APP_DIRS': True,
63 | 'OPTIONS': {
64 | 'context_processors': [
65 | 'django.template.context_processors.debug',
66 | 'django.template.context_processors.request',
67 | 'django.contrib.auth.context_processors.auth',
68 | 'django.contrib.messages.context_processors.messages',
69 | ],
70 | },
71 | },
72 | ]
73 |
74 | WSGI_APPLICATION = 'mushrooms.wsgi.application'
75 |
76 |
77 | # Database
78 | # https://docs.djangoproject.com/en/1.10/ref/settings/#databases
79 |
80 | DATABASES = {
81 | 'default': {
82 | 'ENGINE': 'django.db.backends.sqlite3',
83 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
84 | }
85 | }
86 |
87 |
88 | # Password validation
89 | # https://docs.djangoproject.com/en/1.10/ref/settings/#auth-password-validators
90 |
91 | AUTH_PASSWORD_VALIDATORS = [
92 | {
93 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
94 | },
95 | {
96 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
97 | },
98 | {
99 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
100 | },
101 | {
102 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
103 | },
104 | ]
105 |
106 |
107 | # Internationalization
108 | # https://docs.djangoproject.com/en/1.10/topics/i18n/
109 |
110 | LANGUAGE_CODE = 'en-us'
111 |
112 | TIME_ZONE = 'UTC'
113 |
114 | USE_I18N = True
115 |
116 | USE_L10N = True
117 |
118 | USE_TZ = True
119 |
120 |
121 | # Static files (CSS, JavaScript, Images)
122 | # https://docs.djangoproject.com/en/1.10/howto/static-files/
123 |
124 | STATIC_URL = '/static/'
125 | MEDIA_URL = '/media/'
126 | MEDIA_ROOT = BASE_DIR
127 |
128 | LEAFLET_CONFIG = {
129 | 'SPATIAL_EXTENT': (5.0, 44.0, 7.5, 46)
130 | }
131 |
--------------------------------------------------------------------------------
/leaflet/forms/widgets.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from __future__ import unicode_literals
3 | from distutils.version import LooseVersion
4 |
5 | from django import get_version
6 | from django import forms
7 | from django.core import validators
8 | from django.core.exceptions import ImproperlyConfigured
9 | from django.template.defaultfilters import slugify
10 | try:
11 | from django.contrib.gis.forms.widgets import BaseGeometryWidget
12 | except (ImportError, ImproperlyConfigured):
13 | from .backport import BaseGeometryWidget
14 |
15 | from leaflet import app_settings, PLUGINS, PLUGIN_FORMS
16 |
17 |
18 | class LeafletWidget(BaseGeometryWidget):
19 | template_name = 'leaflet/widget.html'
20 | map_srid = 4326
21 | map_width = None
22 | map_height = None
23 | modifiable = True
24 | supports_3d = False
25 | include_media = False
26 | settings_overrides = {}
27 |
28 | @property
29 | def media(self):
30 | if not self.include_media:
31 | return forms.Media()
32 |
33 | # We assume that including media for widget means there is
34 | # no Leaflet at all in the page.
35 | js = ['leaflet/leaflet.js'] + PLUGINS[PLUGIN_FORMS]['js']
36 | css = ['leaflet/leaflet.css'] + PLUGINS[PLUGIN_FORMS]['css']
37 | return forms.Media(js=js, css={'screen': css})
38 |
39 | def serialize(self, value):
40 | return value.geojson if value else ''
41 |
42 | def _get_attrs(self, name, attrs=None):
43 | assert self.map_srid == 4326, 'Leaflet vectors should be decimal degrees.'
44 |
45 | # Retrieve params from Field init (if any)
46 | self.geom_type = self.attrs.get('geom_type', self.geom_type)
47 | self.settings_overrides = self.attrs.get('settings_overrides', self.settings_overrides)
48 |
49 | # Setting 'loadevent' added in the widget constructor
50 | loadevent = self.attrs.get('loadevent', app_settings.get('LOADEVENT'))
51 |
52 | attrs = attrs or {}
53 |
54 | # In BaseGeometryWidget, geom_type is set using gdal, and fails with generic.
55 | # See https://code.djangoproject.com/ticket/21021
56 | if self.geom_type == 'GEOMETRY':
57 | attrs['geom_type'] = 'Geometry'
58 |
59 | map_id_css = slugify(attrs.get('id', name)) # id need to have - for the inline formset to replace the prefix
60 | map_id = map_id_css.replace('-', '_') # JS-safe
61 | attrs.update(id=map_id,
62 | id_css=map_id_css,
63 | module='geodjango_%s' % map_id,
64 | id_map=map_id_css + '-map',
65 | id_map_callback=map_id + '_map_callback',
66 | loadevent=loadevent,
67 | modifiable=self.modifiable,
68 | target_map=attrs.get('target_map', getattr(self, 'target_map', None)),
69 | settings_overrides=attrs.get('settings_overrides', getattr(self, 'settings_overrides', None)),
70 | geometry_field_class=attrs.get('geometry_field_class', getattr(self, 'geometry_field_class', 'L.GeometryField')),
71 | field_store_class=attrs.get('field_store_class', getattr(self, 'field_store_class', 'L.FieldStore')))
72 | return attrs
73 |
74 | # Django 1.11 changed how the widgets are rendered
75 | if LooseVersion(get_version()) >= LooseVersion('1.11'):
76 | def get_context(self, name, value, attrs):
77 | value = None if value in validators.EMPTY_VALUES else value
78 | context = super(LeafletWidget, self).get_context(name, value, attrs)
79 | context.update(self._get_attrs(name, attrs))
80 | return context
81 | else:
82 | def render(self, name, value, attrs=None):
83 | attrs = self._get_attrs(name, attrs)
84 | value = None if value in validators.EMPTY_VALUES else value
85 | return super(LeafletWidget, self).render(name, value, attrs)
86 |
--------------------------------------------------------------------------------
/leaflet/locale/es/LC_MESSAGES/django.po:
--------------------------------------------------------------------------------
1 | msgid ""
2 | msgstr ""
3 | "Project-Id-Version: PACKAGE VERSION\n"
4 | "Report-Msgid-Bugs-To: \n"
5 | "POT-Creation-Date: 2013-07-05 16:25+0200\n"
6 | "PO-Revision-Date: Mon Oct 13 2014 16:45:22 GMT+0200 (CEST)\n"
7 | "Last-Translator: Mathieu Leplatre \n"
8 | "Language-Team: LANGUAGE \n"
9 | "Language: Unknown locale\n"
10 | "Plural-Forms: nplurals=2; plural=(n > 1)\n"
11 | "MIME-Version: 1.0\n"
12 | "Content-Type: text/plain; charset=UTF-8\n"
13 | "Content-Transfer-Encoding: 8bit\n"
14 | "X-Poedit-SourceCharset: UTF-8\n"
15 | "X-Loco-Source-Locale: es_ES\n"
16 | "X-Loco-Parser: loco_parse_po\n"
17 | "X-Loco-Target-Locale: zz_ZZ\n"
18 | "X-Generator: Loco - https://localise.biz/"
19 |
20 | msgid "Reset view"
21 | msgstr "Reinicializar la vista"
22 |
23 | msgid "OSM"
24 | msgstr "OSM"
25 |
26 | msgid "Background"
27 | msgstr "Fondo"
28 |
29 | msgid "Spatial extent should be a tuple (minx, miny, maxx, maxy)"
30 | msgstr ""
31 | "La extension (spatial extent) deve ser una tupla de tipo (minx, miny, maxx, "
32 | "maxy)"
33 |
34 | msgid "Draw a polyline"
35 | msgstr "Dibujar una linea"
36 |
37 | msgid "Draw a polygon"
38 | msgstr "Dibujar un poligono"
39 |
40 | msgid "Draw a rectangle"
41 | msgstr "Dibujar un rectángulo"
42 |
43 | msgid "Draw a circle"
44 | msgstr "Dibujar un círculo"
45 |
46 | msgid "Draw a marker"
47 | msgstr "Dibujar un marcador"
48 |
49 | msgid "Click and drag to draw circle."
50 | msgstr "Haz click y arrastra para dibujar un círculo."
51 |
52 | msgid "Click map to place marker."
53 | msgstr "Haz click sobre el mapa para dibujar un marcador."
54 |
55 | msgid "Click to start drawing shape."
56 | msgstr "Haz click para comenzar a dibujar la geometría."
57 |
58 | msgid "Click to continue drawing shape."
59 | msgstr "Haz click para continuar dibujando la geometría."
60 |
61 | msgid "Click first point to close this shape."
62 | msgstr "Haz click en el primer punto para cerrar la geometría."
63 |
64 | msgid "Error: shape edges cannot cross!"
65 | msgstr ""
66 | "Error: ¡los segmentos no se pueden cruzarles!"
67 |
68 | msgid "Click to start drawing line."
69 | msgstr "Haz click para comenzar a dibujar una linea."
70 |
71 | msgid "Click to continue drawing line."
72 | msgstr "Haz click para continuar dibujando la linea."
73 |
74 | msgid "Click last point to finish line."
75 | msgstr "Haz click sobre el ultimo punto para terminar la linea."
76 |
77 | msgid "Click and drag to draw rectangle."
78 | msgstr "Haz click y arrastra para dibujar un rectangulo."
79 |
80 | msgid "Release mouse to finish drawing."
81 | msgstr "libera el raton para terminar de dibujar."
82 |
83 | msgid "Save changes."
84 | msgstr "Guardar los cambios."
85 |
86 | msgid "Save"
87 | msgstr "Guardar"
88 |
89 | msgid "Cancel editing, discards all changes."
90 | msgstr "Cancelar la edición, anulará todos los cambios."
91 |
92 | msgid "Cancel"
93 | msgstr "Cancelar"
94 |
95 | msgid "Cancel drawing"
96 | msgstr "Cancelar el dibujo."
97 |
98 | msgid "Delete last point drawn"
99 | msgstr "Eliminar el último punto dibujado"
100 |
101 | msgid "Delete last point"
102 | msgstr "Suprimir el último punto"
103 |
104 | msgid "Edit layers"
105 | msgstr "Editar capas"
106 |
107 | msgid "No layers to edit."
108 | msgstr "Ninguna capa a editar."
109 |
110 | msgid "No layers to delete."
111 | msgstr "Ninguna capa a suprimir."
112 |
113 | msgid "Delete layers"
114 | msgstr "Suprimir la capa."
115 |
116 | msgid "Drag handles, or marker to edit feature."
117 | msgstr "Desplazar las marcas o puntos para editar la geometría."
118 |
119 | msgid "Click cancel to undo changes."
120 | msgstr "Haz click en cancelar para deshacer los cambios."
121 |
122 | msgid "Click on a feature to remove"
123 | msgstr "Haz click en una geometría para suprimirla"
124 |
--------------------------------------------------------------------------------
/docs/widget.rst:
--------------------------------------------------------------------------------
1 | Leaflet map forms widgets
2 | =========================
3 |
4 | A Leaflet widget is provided to edit geometry fields.
5 | It embeds *Leaflet.draw* in version *0.4.0*.
6 |
7 |
8 | .. image :: https://f.cloud.github.com/assets/546692/1048836/78b6ad94-1094-11e3-86d8-c3e88626a31d.png
9 |
10 |
11 | In Adminsite
12 | ------------
13 |
14 | ::
15 |
16 | from django.contrib import admin
17 | from leaflet.admin import LeafletGeoAdmin
18 |
19 | from .models import WeatherStation
20 |
21 |
22 | admin.site.register(WeatherStation, LeafletGeoAdmin)
23 |
24 |
25 | A mixin is also available for inline forms:
26 |
27 | ::
28 |
29 | from django.contrib import admin
30 | from leaflet.admin import LeafletGeoAdminMixin
31 |
32 | class PoiLocationInline(LeafletGeoAdminMixin, admin.StackedInline):
33 | model = PoiLocation
34 |
35 |
36 | In forms
37 | --------
38 |
39 | With *Django* >= 1.6:
40 |
41 | ::
42 |
43 | from django import forms
44 |
45 | from leaflet.forms.widgets import LeafletWidget
46 |
47 |
48 | class WeatherStationForm(forms.ModelForm):
49 |
50 | class Meta:
51 | model = WeatherStation
52 | fields = ('name', 'geom')
53 | widgets = {'geom': LeafletWidget()}
54 |
55 | With all *Django* versions:
56 |
57 | ::
58 |
59 | from django import forms
60 |
61 | from leaflet.forms.fields import PointField
62 |
63 |
64 | class WeatherStationForm(forms.ModelForm):
65 | geom = PointField()
66 |
67 | class Meta:
68 | model = WeatherStation
69 | fields = ('name', 'geom')
70 |
71 | The related template would look like this:
72 |
73 | ::
74 |
75 | {% load leaflet_tags %}
76 |
77 |
78 | {% leaflet_js plugins="forms" %}
79 | {% leaflet_css plugins="forms" %}
80 |
81 |
82 | Edit {{ object }}
83 |
87 |
88 |
89 |
90 |
91 | Every map field will trigger an event you can use to add your custom machinery :
92 |
93 | ::
94 |
95 | map.on('map:loadfield', function (e) {
96 | ...
97 | // Customize map for field
98 | console.log(e.field, e.fieldid);
99 | ...
100 | });
101 |
102 |
103 | If you need a reusable customization of widgets maps, first override the JavaScript field behaviour by extending ``L.GeometryField``, then in Django subclass the ``LeafletWidget`` to specify the custom ``geometry_field_class``.
104 |
105 | ::
106 |
107 | YourGeometryField = L.GeometryField.extend({
108 | addTo: function (map) {
109 | L.GeometryField.prototype.addTo.call(this, map);
110 | // Customize map for field
111 | console.log(this);
112 | },
113 | // See GeometryField source (static/leaflet/leaflet.forms.js) to override more stuff...
114 | });
115 |
116 | ::
117 |
118 | class YourMapWidget(LeafletWidget):
119 | geometry_field_class = 'YourGeometryField'
120 |
121 | class YourForm(forms.ModelForm):
122 | class Meta:
123 | model = YourModel
124 | fields = ('name', 'geom')
125 | widgets = {'geom': YourMapWidget()}
126 |
127 | Plugins
128 | -------
129 |
130 | It's possible to add extras JS/CSS or auto-include *forms* plugins
131 | everywhere: ::
132 |
133 | LEAFLET_CONFIG = {
134 | 'PLUGINS': {
135 | 'forms': {
136 | 'auto-include': True
137 | }
138 | }
139 | }
140 |
141 | ( *It will be merged over default minimal set required for edition* )
142 |
143 |
144 | Details
145 | -------
146 |
147 | * It relies on global settings for map initialization.
148 | * It works with local map projections. But SRID is specified globally
149 | through ``LEAFLET_CONFIG['SRID']`` as described below.
150 | * Javascript component for de/serializing fields value is pluggable.
151 | * Javascript component for Leaflet.draw behaviour initialization is pluggable.
152 |
--------------------------------------------------------------------------------
/leaflet/static/leaflet/eventlistener.ie6-7.js:
--------------------------------------------------------------------------------
1 | // EventListener | MIT/GPL2 | github.com/jonathantneal/EventListener
2 |
3 | !this.addEventListener && this.attachEvent && (function (window, document) {
4 | var registry = [];
5 |
6 | // add
7 | function addEventListener(type, listener) {
8 | var target = this;
9 |
10 | registry.unshift({
11 | __listener: function (event) {
12 | event.currentTarget = target;
13 | event.pageX = event.clientX + document.documentElement.scrollLeft;
14 | event.pageY = event.clientY + document.documentElement.scrollTop;
15 | event.preventDefault = function () { event.returnValue = false };
16 | event.relatedTarget = event.fromElement || null;
17 | event.stopPropagation = function () { event.cancelBubble = true };
18 | event.relatedTarget = event.fromElement || null;
19 | event.target = event.srcElement || target;
20 | event.timeStamp = +new Date;
21 |
22 | listener.call(target, event);
23 | },
24 | listener: listener,
25 | target: target,
26 | type: type
27 | });
28 |
29 | target.attachEvent("on" + type, registry[0].__listener);
30 | }
31 |
32 | // remove
33 | function removeEventListener(type, listener) {
34 | for (var index = 0, length = registry.length; index < length; ++index) {
35 | if (registry[index].target == this && registry[index].type == type && registry[index].listener == listener) {
36 | return this.detachEvent("on" + type, registry.splice(index, 1)[0].__listener);
37 | }
38 | }
39 | }
40 |
41 | // dispatch
42 | function dispatchEvent(eventObject) {
43 | try {
44 | return this.fireEvent("on" + eventObject.type, eventObject);
45 | } catch (error) {
46 | for (var index = 0, length = registry.length; index < length; ++index) {
47 | if (registry[index].target == this && registry[index].type == eventObject.type) {
48 | registry[index].__listener.call(this, eventObject);
49 | }
50 | }
51 | }
52 | }
53 |
54 | // custom
55 | function CustomEvent(type, canBubble, cancelable, detail) {
56 | var event = document.createEventObject(), key;
57 |
58 | event.type = type;
59 | event.returnValue = !cancelable;
60 | event.cancelBubble = !canBubble;
61 |
62 | for (key in detail) {
63 | event[key] = detail[key];
64 | }
65 |
66 | return event;
67 | }
68 |
69 | function _patchNode(node) {
70 | if (node.dispatchEvent) {
71 | return;
72 | }
73 |
74 | node.addEventListener = addEventListener;
75 | node.removeEventListener = removeEventListener;
76 | node.dispatchEvent = dispatchEvent;
77 |
78 | var appendChild = node.appendChild, createElement = node.createElement, insertBefore = node.insertBefore;
79 |
80 | if (appendChild) {
81 | node.appendChild = function (node) {
82 | var returnValue = appendChild(node);
83 |
84 | _patchNodeList(node.all);
85 |
86 | return returnValue;
87 | };
88 | }
89 |
90 | if (createElement) {
91 | node.createElement = function (nodeName) {
92 | var returnValue = createElement(nodeName);
93 |
94 | _patchNodeList(node.all);
95 |
96 | return returnValue;
97 | };
98 | }
99 |
100 | if (insertBefore) {
101 | node.insertBefore = function (node, referenceElement) {
102 | var returnValue = insertBefore(node, referenceElement);
103 |
104 | _patchNodeList(node.all);
105 |
106 | return returnValue;
107 | };
108 | }
109 |
110 | if ("innerHTML" in node) {
111 | node.attachEvent("onpropertychange", function (event) {
112 | if (event.propertyName != "innerHTML") return;
113 |
114 | _patchNodeList(node.all);
115 | });
116 | }
117 | }
118 |
119 | function _patchNodeList(nodeList) {
120 | for (var i = 0, node; node = nodeList[i]; ++i) {
121 | _patchNode(node);
122 | }
123 | }
124 |
125 | document.attachEvent("onreadystatechange", function (event) {
126 | if (document.readyState == "complete") {
127 | _patchNodeList(document.all);
128 |
129 | // ready
130 | document.dispatchEvent(new CustomEvent("DOMContentLoaded", false, false));
131 | }
132 | });
133 |
134 | _patchNode(window);
135 | _patchNode(document);
136 |
137 | _patchNodeList(document.all);
138 |
139 | window.CustomEvent = CustomEvent;
140 | })(this, document);
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: required
2 | language: python
3 |
4 | python:
5 | - 2.7
6 | - 3.5
7 | - 3.6
8 |
9 | addons:
10 | postgresql: 9.6
11 | apt:
12 | packages:
13 | - postgresql-9.6-postgis-2.3
14 |
15 | env:
16 | matrix:
17 | - DJANGO_VERSION=1.6.11 DATABASE=postgres
18 | - DJANGO_VERSION=1.6.11 DATABASE=sqlite
19 | - DJANGO_VERSION=1.7.11 DATABASE=postgres
20 | - DJANGO_VERSION=1.7.11 DATABASE=sqlite
21 | - DJANGO_VERSION=1.8.18 DATABASE=postgres
22 | - DJANGO_VERSION=1.8.18 DATABASE=sqlite
23 | - DJANGO_VERSION=1.9.13 DATABASE=postgres
24 | - DJANGO_VERSION=1.9.13 DATABASE=sqlite
25 | - DJANGO_VERSION=1.10.8 DATABASE=postgres
26 | - DJANGO_VERSION=1.10.8 DATABASE=sqlite
27 | - DJANGO_VERSION=1.11.7 DATABASE=postgres
28 | - DJANGO_VERSION=1.11.7 DATABASE=sqlite
29 | - DJANGO_VERSION=dev DATABASE=postgres
30 | - DJANGO_VERSION=dev DATABASE=sqlite
31 |
32 | matrix:
33 | exclude:
34 | - python: 3.5
35 | env: DJANGO_VERSION=1.6.11 DATABASE=sqlite
36 | - python: 3.5
37 | env: DJANGO_VERSION=1.6.11 DATABASE=postgres
38 | - python: 3.5
39 | env: DJANGO_VERSION=1.7.11 DATABASE=sqlite
40 | - python: 3.5
41 | env: DJANGO_VERSION=1.7.11 DATABASE=postgres
42 | - python: 3.5
43 | env: DJANGO_VERSION=1.8.18 DATABASE=sqlite
44 | - python: 3.5
45 | env: DJANGO_VERSION=1.9.13 DATABASE=sqlite
46 | - python: 3.5
47 | env: DJANGO_VERSION=1.10.8 DATABASE=sqlite
48 | - python: 3.5
49 | env: DJANGO_VERSION=1.11.7 DATABASE=sqlite
50 | - python: 3.5
51 | env: DJANGO_VERSION=dev DATABASE=sqlite
52 | - python: 3.6
53 | env: DJANGO_VERSION=1.6.11 DATABASE=sqlite
54 | - python: 3.6
55 | env: DJANGO_VERSION=1.6.11 DATABASE=postgres
56 | - python: 3.6
57 | env: DJANGO_VERSION=1.7.11 DATABASE=sqlite
58 | - python: 3.6
59 | env: DJANGO_VERSION=1.7.11 DATABASE=postgres
60 | - python: 3.6
61 | env: DJANGO_VERSION=1.8.18 DATABASE=sqlite
62 | - python: 3.6
63 | env: DJANGO_VERSION=1.8.18 DATABASE=postgres
64 | - python: 3.6
65 | env: DJANGO_VERSION=1.9.13 DATABASE=sqlite
66 | - python: 3.6
67 | env: DJANGO_VERSION=1.9.13 DATABASE=postgres
68 | - python: 3.6
69 | env: DJANGO_VERSION=1.10.8 DATABASE=sqlite
70 | - python: 3.6
71 | env: DJANGO_VERSION=1.10.8 DATABASE=postgres
72 | - python: 3.6
73 | env: DJANGO_VERSION=1.11.7 DATABASE=sqlite
74 | - python: 2.7
75 | env: DJANGO_VERSION=dev DATABASE=sqlite
76 | - python: 2.7
77 | env: DJANGO_VERSION=dev DATABASE=postgres
78 |
79 | allow_failures:
80 | - python: 3.5
81 | env: DJANGO_VERSION=dev DATABASE=sqlite
82 | - python: 3.5
83 | env: DJANGO_VERSION=dev DATABASE=postgres
84 | - python: 3.6
85 | env: DJANGO_VERSION=dev DATABASE=sqlite
86 | - python: 3.6
87 | env: DJANGO_VERSION=dev DATABASE=postgres
88 |
89 | before_install:
90 | - sudo apt-get update
91 |
92 | install:
93 | # GeoDjango dependencies
94 | - sudo apt-get update -qq
95 | - sudo apt-get install -y libproj-dev libgeos-dev
96 |
97 | - if [[ $DATABASE == sqlite ]]; then sudo apt-get install -y libspatialite-dev; fi
98 | - if [[ $DATABASE == sqlite ]]; then pip install pysqlite==2.8.2; fi
99 | - if [[ $DATABASE == postgres ]]; then pip install psycopg2; fi
100 |
101 | # This is a dependency of our Django test script
102 | - pip install argparse
103 | - if [[ $DJANGO_VERSION == dev ]]; then pip
104 | install https://github.com/django/django/tarball/master/django.tar.gz#egg=django; else pip
105 | install -q Django==$DJANGO_VERSION; fi
106 |
107 | - pip install coverage
108 | - pip install flake8
109 |
110 | - python setup.py develop
111 | - npm install leaflet/tests/
112 |
113 | - pip freeze
114 | - dpkg -l
115 |
116 | before_script:
117 | - flake8 --ignore=E501 leaflet
118 | - if [[ $DATABASE == postgres ]]; then psql -c 'create database test_db;' -U postgres; fi
119 | - if [[ $DATABASE == postgres ]]; then psql -c 'CREATE EXTENSION postgis;' -U postgres -d test_db; fi
120 |
121 | script:
122 | - python quicktest.py leaflet --db=$DATABASE
123 | - node --version
124 | - node node_modules/mocha-phantomjs/bin/mocha-phantomjs leaflet/tests/index.html
125 |
126 | after_success:
127 | - coverage run quicktest.py leaflet --db=$DATABASE
128 | - pip install coveralls
129 | - coveralls
130 |
--------------------------------------------------------------------------------
/quicktest.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf8 -*-
2 | from __future__ import unicode_literals
3 |
4 | import os
5 | import sys
6 | import argparse
7 | from django.conf import settings
8 | import django
9 |
10 |
11 | class QuickDjangoTest(object):
12 | """
13 | A quick way to run the Django test suite without a fully-configured project.
14 |
15 | Example usage:
16 |
17 | >>> QuickDjangoTest(apps=['app1', 'app2'], db='sqlite')
18 |
19 | Based on a script published by Lukasz Dziedzia at:
20 | http://stackoverflow.com/questions/3841725/how-to-launch-tests-for-django-reusable-app
21 | """
22 | DIRNAME = os.path.dirname(__file__)
23 | INSTALLED_APPS = [
24 | 'django.contrib.staticfiles',
25 | 'django.contrib.auth',
26 | 'django.contrib.contenttypes',
27 | 'django.contrib.sessions',
28 | 'django.contrib.admin',
29 | ]
30 |
31 | if django.VERSION >= (1, 8, 0):
32 | TEMPLATES = {
33 | 'BACKEND': 'django.template.backends.django.DjangoTemplates',
34 | 'OPTIONS': {
35 | 'context_processors': [
36 | 'django.contrib.auth.context_processors.auth',
37 | ]
38 | },
39 | 'APP_DIRS': True,
40 | }
41 |
42 | def __init__(self, *args, **kwargs):
43 | self.apps = kwargs.get('apps', [])
44 | self.database= kwargs.get('db', 'sqlite')
45 | self.run_tests()
46 |
47 | def run_tests(self):
48 | """
49 | Fire up the Django test suite developed for version 1.2
50 | """
51 | if self.database == 'postgres':
52 | databases = {
53 | 'default': {
54 | 'ENGINE': 'django.contrib.gis.db.backends.postgis',
55 | 'NAME': 'test_db',
56 | 'HOST': '127.0.0.1',
57 | 'USER': 'postgres',
58 | 'PASSWORD': '',
59 | }
60 | }
61 |
62 | else:
63 | databases = {
64 | 'default': {
65 | 'ENGINE': 'django.contrib.gis.db.backends.spatialite',
66 | 'NAME': os.path.join(self.DIRNAME, 'database.db'),
67 | }
68 | }
69 | conf = {
70 | 'DATABASES': databases,
71 | 'INSTALLED_APPS': self.INSTALLED_APPS + self.apps,
72 | 'STATIC_URL': '/static/',
73 | }
74 | if 'SPATIALITE_LIBRARY_PATH' in os.environ:
75 | # If you get SpatiaLite-related errors, refer to this document
76 | # to find out the proper SPATIALITE_LIBRARY_PATH value
77 | # for your platform.
78 | # https://docs.djangoproject.com/en/dev/ref/contrib/gis/install/spatialite/
79 | #
80 | # Example for macOS (with spatialite-tools installed using brew):
81 | # $ export SPATIALITE_LIBRARY_PATH='/usr/local/lib/mod_spatialite.dylib'
82 | conf['SPATIALITE_LIBRARY_PATH'] = os.getenv('SPATIALITE_LIBRARY_PATH')
83 | if django.VERSION >= (1, 8, 0):
84 | conf['TEMPLATES'] = self.TEMPLATES,
85 | settings.configure(**conf)
86 | if django.VERSION >= (1, 7, 0):
87 | # see: https://docs.djangoproject.com/en/dev/releases/1.7/#standalone-scripts
88 | django.setup()
89 | if django.VERSION >= (1, 6, 0):
90 | # see: https://docs.djangoproject.com/en/dev/releases/1.6/#discovery-of-tests-in-any-test-module
91 | from django.test.runner import DiscoverRunner as Runner
92 | else:
93 | from django.test.simple import DjangoTestSuiteRunner as Runner
94 |
95 | failures = Runner().run_tests(self.apps, verbosity=1)
96 | if failures: # pragma: no cover
97 | sys.exit(failures)
98 |
99 | if __name__ == '__main__':
100 | """
101 | What do when the user hits this file from the shell.
102 |
103 | Example usage:
104 |
105 | $ python quicktest.py app1 app2 --db=sqlite
106 |
107 | """
108 | parser = argparse.ArgumentParser(
109 | usage="[args] [--db=sqlite]",
110 | description="Run Django tests on the provided applications."
111 | )
112 | parser.add_argument('apps', nargs='+', type=str)
113 | parser.add_argument('--db', nargs='?', type=str, default='sqlite')
114 | args = parser.parse_args()
115 | QuickDjangoTest(apps=args.apps, db=args.db)
116 |
--------------------------------------------------------------------------------
/leaflet/static/leaflet/eventlistener.ie8.js:
--------------------------------------------------------------------------------
1 | // EventListener | MIT/GPL2 | github.com/jonathantneal/EventListener
2 |
3 | this.Element && Element.prototype.attachEvent && !Element.prototype.addEventListener && (function () {
4 | function addToPrototype(name, method) {
5 | Window.prototype[name] = HTMLDocument.prototype[name] = Element.prototype[name] = method;
6 | }
7 |
8 | // add
9 | addToPrototype("addEventListener", function (type, listener) {
10 | var
11 | target = this,
12 | listeners = target.addEventListener.listeners = target.addEventListener.listeners || {},
13 | typeListeners = listeners[type] = listeners[type] || [];
14 |
15 | // if no events exist, attach the listener
16 | if (!typeListeners.length) {
17 | target.attachEvent("on" + type, typeListeners.event = function (event) {
18 | var documentElement = target.document && target.document.documentElement || target.documentElement || { scrollLeft: 0, scrollTop: 0 };
19 |
20 | // polyfill w3c properties and methods
21 | event.currentTarget = target;
22 | event.pageX = event.clientX + documentElement.scrollLeft;
23 | event.pageY = event.clientY + documentElement.scrollTop;
24 | event.preventDefault = function () { event.returnValue = false };
25 | event.relatedTarget = event.fromElement || null;
26 | event.stopImmediatePropagation = function () { immediatePropagation = false; event.cancelBubble = true };
27 | event.stopPropagation = function () { event.cancelBubble = true };
28 | event.target = event.srcElement || target;
29 | event.timeStamp = +new Date;
30 |
31 | // create an cached list of the master events list (to protect this loop from breaking when an event is removed)
32 | for (var i = 0, typeListenersCache = [].concat(typeListeners), typeListenerCache, immediatePropagation = true; immediatePropagation && (typeListenerCache = typeListenersCache[i]); ++i) {
33 | // check to see if the cached event still exists in the master events list
34 | for (var ii = 0, typeListener; typeListener = typeListeners[ii]; ++ii) {
35 | if (typeListener == typeListenerCache) {
36 | typeListener.call(target, event);
37 |
38 | break;
39 | }
40 | }
41 | }
42 | });
43 | }
44 |
45 | // add the event to the master event list
46 | typeListeners.push(listener);
47 | });
48 |
49 | // remove
50 | addToPrototype("removeEventListener", function (type, listener) {
51 | var
52 | target = this,
53 | listeners = target.addEventListener.listeners = target.addEventListener.listeners || {},
54 | typeListeners = listeners[type] = listeners[type] || [];
55 |
56 | // remove the newest matching event from the master event list
57 | for (var i = typeListeners.length - 1, typeListener; typeListener = typeListeners[i]; --i) {
58 | if (typeListener == listener) {
59 | typeListeners.splice(i, 1);
60 |
61 | break;
62 | }
63 | }
64 |
65 | // if no events exist, detach the listener
66 | if (!typeListeners.length && typeListeners.event) {
67 | target.detachEvent("on" + type, typeListeners.event);
68 | }
69 | });
70 |
71 | // dispatch
72 | addToPrototype("dispatchEvent", function (eventObject) {
73 | var
74 | target = this,
75 | type = eventObject.type,
76 | listeners = target.addEventListener.listeners = target.addEventListener.listeners || {},
77 | typeListeners = listeners[type] = listeners[type] || [];
78 |
79 | try {
80 | return target.fireEvent("on" + type, eventObject);
81 | } catch (error) {
82 | if (typeListeners.event) {
83 | typeListeners.event(eventObject);
84 | }
85 |
86 | return;
87 | }
88 | });
89 |
90 | // CustomEvent
91 | Object.defineProperty(Window.prototype, "CustomEvent", {
92 | get: function () {
93 | var self = this;
94 |
95 | return function CustomEvent(type, eventInitDict) {
96 | var event = self.document.createEventObject(), key;
97 |
98 | event.type = type;
99 | for (key in eventInitDict) {
100 | if (key == 'cancelable'){
101 | event.returnValue = !eventInitDict.cancelable;
102 | } else if (key == 'bubbles'){
103 | event.cancelBubble = !eventInitDict.bubbles;
104 | } else if (key == 'detail'){
105 | event.detail = eventInitDict.detail;
106 | }
107 | }
108 | return event;
109 | };
110 | }
111 | });
112 |
113 | // ready
114 | function ready(event) {
115 | if (ready.interval && document.body) {
116 | ready.interval = clearInterval(ready.interval);
117 |
118 | document.dispatchEvent(new CustomEvent("DOMContentLoaded"));
119 | }
120 | }
121 |
122 | ready.interval = setInterval(ready, 1);
123 |
124 | window.addEventListener("load", ready);
125 | })();
126 |
--------------------------------------------------------------------------------
/leaflet/static/leaflet/draw/leaflet.draw.css:
--------------------------------------------------------------------------------
1 | .leaflet-draw-section{position:relative}.leaflet-draw-toolbar{margin-top:12px}.leaflet-draw-toolbar-top{margin-top:0}.leaflet-draw-toolbar-notop a:first-child{border-top-right-radius:0}.leaflet-draw-toolbar-nobottom a:last-child{border-bottom-right-radius:0}.leaflet-draw-toolbar a{background-image:url('images/spritesheet.png');background-image:linear-gradient(transparent,transparent),url('images/spritesheet.svg');background-repeat:no-repeat;background-size:270px 30px;background-clip:padding-box}.leaflet-retina .leaflet-draw-toolbar a{background-image:url('images/spritesheet-2x.png');background-image:linear-gradient(transparent,transparent),url('images/spritesheet.svg')}
2 | .leaflet-draw a{display:block;text-align:center;text-decoration:none}.leaflet-draw a .sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.leaflet-draw-actions{display:none;list-style:none;margin:0;padding:0;position:absolute;left:26px;top:0;white-space:nowrap}.leaflet-touch .leaflet-draw-actions{left:32px}.leaflet-right .leaflet-draw-actions{right:26px;left:auto}.leaflet-touch .leaflet-right .leaflet-draw-actions{right:32px;left:auto}.leaflet-draw-actions li{display:inline-block}
3 | .leaflet-draw-actions li:first-child a{border-left:0}.leaflet-draw-actions li:last-child a{-webkit-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.leaflet-right .leaflet-draw-actions li:last-child a{-webkit-border-radius:0;border-radius:0}.leaflet-right .leaflet-draw-actions li:first-child a{-webkit-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.leaflet-draw-actions a{background-color:#919187;border-left:1px solid #AAA;color:#FFF;font:11px/19px "Helvetica Neue",Arial,Helvetica,sans-serif;line-height:28px;text-decoration:none;padding-left:10px;padding-right:10px;height:28px}
4 | .leaflet-touch .leaflet-draw-actions a{font-size:12px;line-height:30px;height:30px}.leaflet-draw-actions-bottom{margin-top:0}.leaflet-draw-actions-top{margin-top:1px}.leaflet-draw-actions-top a,.leaflet-draw-actions-bottom a{height:27px;line-height:27px}.leaflet-draw-actions a:hover{background-color:#a0a098}.leaflet-draw-actions-top.leaflet-draw-actions-bottom a{height:26px;line-height:26px}.leaflet-draw-toolbar .leaflet-draw-draw-polyline{background-position:-2px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-polyline{background-position:0 -1px}
5 | .leaflet-draw-toolbar .leaflet-draw-draw-polygon{background-position:-31px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-polygon{background-position:-29px -1px}.leaflet-draw-toolbar .leaflet-draw-draw-rectangle{background-position:-62px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-rectangle{background-position:-60px -1px}.leaflet-draw-toolbar .leaflet-draw-draw-circle{background-position:-92px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-circle{background-position:-90px -1px}
6 | .leaflet-draw-toolbar .leaflet-draw-draw-marker{background-position:-122px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-marker{background-position:-120px -1px}.leaflet-draw-toolbar .leaflet-draw-edit-edit{background-position:-152px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-edit-edit{background-position:-150px -1px}.leaflet-draw-toolbar .leaflet-draw-edit-remove{background-position:-182px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-edit-remove{background-position:-180px -1px}
7 | .leaflet-draw-toolbar .leaflet-draw-edit-edit.leaflet-disabled{background-position:-212px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-edit-edit.leaflet-disabled{background-position:-210px -1px}.leaflet-draw-toolbar .leaflet-draw-edit-remove.leaflet-disabled{background-position:-242px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-edit-remove.leaflet-disabled{background-position:-240px -2px}.leaflet-mouse-marker{background-color:#fff;cursor:crosshair}.leaflet-draw-tooltip{background:#363636;background:rgba(0,0,0,0.5);border:1px solid transparent;-webkit-border-radius:4px;border-radius:4px;color:#fff;font:12px/18px "Helvetica Neue",Arial,Helvetica,sans-serif;margin-left:20px;margin-top:-21px;padding:4px 8px;position:absolute;visibility:hidden;white-space:nowrap;z-index:6}
8 | .leaflet-draw-tooltip:before{border-right:6px solid black;border-right-color:rgba(0,0,0,0.5);border-top:6px solid transparent;border-bottom:6px solid transparent;content:"";position:absolute;top:7px;left:-7px}.leaflet-error-draw-tooltip{background-color:#f2dede;border:1px solid #e6b6bd;color:#b94a48}.leaflet-error-draw-tooltip:before{border-right-color:#e6b6bd}.leaflet-draw-tooltip-single{margin-top:-12px}.leaflet-draw-tooltip-subtext{color:#f8d5e4}.leaflet-draw-guide-dash{font-size:1%;opacity:.6;position:absolute;width:5px;height:5px}
9 | .leaflet-edit-marker-selected{background-color:rgba(254,87,161,0.1);border:4px dashed rgba(254,87,161,0.6);-webkit-border-radius:4px;border-radius:4px;box-sizing:content-box}.leaflet-edit-move{cursor:move}.leaflet-edit-resize{cursor:pointer}.leaflet-oldie .leaflet-draw-toolbar{border:1px solid #999}
--------------------------------------------------------------------------------
/leaflet/locale/ru/LC_MESSAGES/django.po:
--------------------------------------------------------------------------------
1 | # This file is distributed under the same license as the django-leaflet package.
2 | #
3 | # Translators:
4 | # Nikolay Korotkiy , 2017.
5 | msgid ""
6 | msgstr ""
7 | "Project-Id-Version: django-leaflet 0.20.0\n"
8 | "Report-Msgid-Bugs-To: \n"
9 | "POT-Creation-Date: 2017-08-19 23:18+0300\n"
10 | "PO-Revision-Date: 2013-09-02 13:41+0100\n"
11 | "Last-Translator: Nikolay Korotkiy \n"
12 | "Language-Team: Russian \n"
13 | "Language: ru\n"
14 | "MIME-Version: 1.0\n"
15 | "Content-Type: text/plain; charset=UTF-8\n"
16 | "Content-Transfer-Encoding: 8bit\n"
17 | "Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
18 | "%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n"
19 | "%100>=11 && n%100<=14)? 2 : 3);\n"
20 |
21 | #: templates/leaflet/_leaflet_draw_i18n.js:2
22 | msgid "Cancel drawing"
23 | msgstr "Прекратить рисование"
24 |
25 | #: templates/leaflet/_leaflet_draw_i18n.js:3
26 | #: templates/leaflet/_leaflet_draw_i18n.js:26
27 | msgid "Cancel"
28 | msgstr "Отмена"
29 |
30 | #: templates/leaflet/_leaflet_draw_i18n.js:4
31 | msgid "Delete last point drawn"
32 | msgstr "Удалить последнюю нарисованную точку"
33 |
34 | #: templates/leaflet/_leaflet_draw_i18n.js:5
35 | msgid "Delete last point"
36 | msgstr "Удалить последнюю точку"
37 |
38 | #: templates/leaflet/_leaflet_draw_i18n.js:6
39 | msgid "Draw a polyline"
40 | msgstr "Нарисовать линию"
41 |
42 | #: templates/leaflet/_leaflet_draw_i18n.js:7
43 | msgid "Draw a polygon"
44 | msgstr "Нарисовать полигон"
45 |
46 | #: templates/leaflet/_leaflet_draw_i18n.js:8
47 | msgid "Draw a rectangle"
48 | msgstr "Нарисовать прямоугольник"
49 |
50 | #: templates/leaflet/_leaflet_draw_i18n.js:9
51 | msgid "Draw a circle"
52 | msgstr "Нарисовать окружность"
53 |
54 | #: templates/leaflet/_leaflet_draw_i18n.js:10
55 | msgid "Draw a marker"
56 | msgstr "Нарисовать маркер"
57 |
58 | #: templates/leaflet/_leaflet_draw_i18n.js:11
59 | msgid "Click and drag to draw circle."
60 | msgstr "Нажмите и потяните для рисования окружности."
61 |
62 | #: templates/leaflet/_leaflet_draw_i18n.js:12
63 | msgid "Click map to place marker."
64 | msgstr "Кликните на карту для расположения маркера."
65 |
66 | #: templates/leaflet/_leaflet_draw_i18n.js:13
67 | msgid "Click to start drawing shape."
68 | msgstr "Кликните для начала рисования фигуры."
69 |
70 | #: templates/leaflet/_leaflet_draw_i18n.js:14
71 | msgid "Click to continue drawing shape."
72 | msgstr "Кликните для продолжения рисования фигуры."
73 |
74 | #: templates/leaflet/_leaflet_draw_i18n.js:15
75 | msgid "Click first point to close this shape."
76 | msgstr "Кликните на первую вершину для завершения этой фигуры."
77 |
78 | #: templates/leaflet/_leaflet_draw_i18n.js:16
79 | msgid "Error: shape edges cannot cross!"
80 | msgstr "Ошибка: части фигуры не должны пересекаться!"
81 |
82 | #: templates/leaflet/_leaflet_draw_i18n.js:17
83 | msgid "Click to start drawing line."
84 | msgstr "Кликните для начала рисования линии."
85 |
86 | #: templates/leaflet/_leaflet_draw_i18n.js:18
87 | msgid "Click to continue drawing line."
88 | msgstr "Кликните для продолжения рисования линии."
89 |
90 | #: templates/leaflet/_leaflet_draw_i18n.js:19
91 | msgid "Click last point to finish line."
92 | msgstr "Кликните на последнюю вершину для завершения линии."
93 |
94 | #: templates/leaflet/_leaflet_draw_i18n.js:20
95 | msgid "Click and drag to draw rectangle."
96 | msgstr "Кликните и потяните для создания прямоугольника."
97 |
98 | #: templates/leaflet/_leaflet_draw_i18n.js:21
99 | msgid "Release mouse to finish drawing."
100 | msgstr "Отпустите кнопку мыши для окончания рисования."
101 |
102 | #: templates/leaflet/_leaflet_draw_i18n.js:23
103 | msgid "Save changes."
104 | msgstr "Сохранить изменения."
105 |
106 | #: templates/leaflet/_leaflet_draw_i18n.js:24
107 | msgid "Save"
108 | msgstr "Сохранить"
109 |
110 | #: templates/leaflet/_leaflet_draw_i18n.js:25
111 | msgid "Cancel editing, discards all changes."
112 | msgstr "Прекратить редактирование, отмена всех изменений."
113 |
114 | #: templates/leaflet/_leaflet_draw_i18n.js:27
115 | msgid "Edit layers"
116 | msgstr "Редактировать слои"
117 |
118 | #: templates/leaflet/_leaflet_draw_i18n.js:28
119 | msgid "No layers to edit."
120 | msgstr "Отсутствуют слои для редактирования."
121 |
122 | #: templates/leaflet/_leaflet_draw_i18n.js:29
123 | msgid "Delete layers"
124 | msgstr "Удалить слои"
125 |
126 | #: templates/leaflet/_leaflet_draw_i18n.js:30
127 | msgid "No layers to delete."
128 | msgstr "Отсутствуют слои для удаления."
129 |
130 | #: templates/leaflet/_leaflet_draw_i18n.js:31
131 | msgid "Drag handles, or marker to edit feature."
132 | msgstr "Потяните вершину или маркер для изменения объекта."
133 |
134 | #: templates/leaflet/_leaflet_draw_i18n.js:32
135 | msgid "Click cancel to undo changes."
136 | msgstr "Нажмите отмена для возвращения в исходное состояние."
137 |
138 | #: templates/leaflet/_leaflet_draw_i18n.js:33
139 | msgid "Click on a feature to remove"
140 | msgstr "Кликните на объект для его удаления"
141 |
--------------------------------------------------------------------------------
/leaflet/templatetags/leaflet_tags.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from __future__ import unicode_literals
3 |
4 | import json
5 |
6 | from django import template
7 | from django.conf import settings
8 | from django.utils import six
9 | from django.utils.encoding import force_text
10 |
11 | from leaflet import (app_settings, SPATIAL_EXTENT, SRID, PLUGINS, PLUGINS_DEFAULT,
12 | PLUGIN_ALL, PLUGIN_FORMS, JSONLazyTranslationEncoder)
13 |
14 |
15 | register = template.Library()
16 |
17 |
18 | @register.inclusion_tag('leaflet/css.html')
19 | def leaflet_css(plugins=None):
20 | """
21 |
22 | :param only_plugins:
23 | :param exclude_plugins:
24 | :return:
25 | """
26 | plugin_names = _get_plugin_names(plugins)
27 | return {
28 | "PLUGINS_CSS": _get_all_resources_for_plugins(plugin_names, 'css'),
29 | }
30 |
31 |
32 | @register.inclusion_tag('leaflet/js.html')
33 | def leaflet_js(plugins=None):
34 | """
35 |
36 | :param only_plugins:
37 | :param exclude_plugins:
38 | :return:
39 | """
40 | plugin_names = _get_plugin_names(plugins)
41 | with_forms = PLUGIN_FORMS in plugin_names or PLUGIN_ALL in plugin_names
42 | FORCE_IMAGE_PATH = app_settings.get('FORCE_IMAGE_PATH')
43 | template_options = hasattr(settings, 'TEMPLATES') \
44 | and len(settings.TEMPLATES) \
45 | and settings.TEMPLATES[0].get('OPTIONS', None)
46 |
47 | if template_options and 'debug' in template_options:
48 | debug = template_options['debug']
49 | elif hasattr(settings, 'TEMPLATE_DEBUG'):
50 | debug = settings.TEMPLATE_DEBUG
51 | else:
52 | debug = False
53 |
54 | return {
55 | "DEBUG": debug,
56 | "SRID": str(SRID) if SRID else None,
57 | "PLUGINS_JS": _get_all_resources_for_plugins(plugin_names, 'js'),
58 | "with_forms": with_forms,
59 | "FORCE_IMAGE_PATH": FORCE_IMAGE_PATH
60 | }
61 |
62 |
63 | @register.inclusion_tag('leaflet/_leaflet_map.html')
64 | def leaflet_map(name, callback=None, fitextent=True, creatediv=True,
65 | loadevent=app_settings.get('LOADEVENT'),
66 | settings_overrides={}):
67 | """
68 |
69 | :param name:
70 | :param callback:
71 | :param fitextent:
72 | :param creatediv:
73 | :param loadevent:
74 | :param settings_overrides:
75 | :return:
76 | """
77 |
78 | if settings_overrides == '':
79 | settings_overrides = {}
80 |
81 | instance_app_settings = app_settings.copy() # Allow not overidding global app_settings
82 | instance_app_settings.update(**settings_overrides)
83 |
84 | extent = None
85 | if instance_app_settings['SPATIAL_EXTENT'] is not None:
86 | # Leaflet uses [lat, lng]
87 | xmin, ymin, xmax, ymax = instance_app_settings['SPATIAL_EXTENT']
88 | bbox = (ymin, xmin, ymax, xmax)
89 | extent = [bbox[:2], bbox[2:4]]
90 |
91 | djoptions = dict(
92 | srid=SRID,
93 | extent=extent,
94 | fitextent=fitextent,
95 | center=instance_app_settings['DEFAULT_CENTER'],
96 | zoom=instance_app_settings['DEFAULT_ZOOM'],
97 | minzoom=instance_app_settings['MIN_ZOOM'],
98 | maxzoom=instance_app_settings['MAX_ZOOM'],
99 | layers=[(force_text(label), url, attrs) for (label, url, attrs) in instance_app_settings.get('TILES')],
100 | overlays=[(force_text(label), url, attrs) for (label, url, attrs) in instance_app_settings.get('OVERLAYS')],
101 | attributionprefix=force_text(instance_app_settings.get('ATTRIBUTION_PREFIX'), strings_only=True),
102 | scale=instance_app_settings.get('SCALE'),
103 | minimap=instance_app_settings.get('MINIMAP'),
104 | resetview=instance_app_settings.get('RESET_VIEW'),
105 | tilesextent=list(instance_app_settings.get('TILES_EXTENT', []))
106 | )
107 |
108 | return {
109 | # templatetag options
110 | 'name': name,
111 | 'loadevents': json.dumps(loadevent.split(), cls=JSONLazyTranslationEncoder),
112 | 'creatediv': creatediv,
113 | 'callback': callback,
114 | # initialization options
115 | 'djoptions': json.dumps(djoptions, cls=JSONLazyTranslationEncoder),
116 | # settings
117 | 'NO_GLOBALS': instance_app_settings.get('NO_GLOBALS'),
118 | }
119 |
120 |
121 | @register.simple_tag
122 | def leaflet_json_config():
123 | settings_as_json = app_settings.copy()
124 |
125 | if SPATIAL_EXTENT is not None:
126 | xmin, ymin, xmax, ymax = settings_as_json.pop('SPATIAL_EXTENT')
127 | settings_as_json['SPATIAL_EXTENT'] = {'xmin': xmin, 'ymin': ymin,
128 | 'xmax': xmax, 'ymax': ymax}
129 |
130 | return json.dumps(settings_as_json, cls=JSONLazyTranslationEncoder)
131 |
132 |
133 | def _get_plugin_names(plugin_names_from_tag_parameter):
134 | """
135 | Returns a list of plugin names, specified in the parameter.
136 | Used by tags to determine which plugins to include
137 | :param pluging_names_parameter:
138 | :return:
139 | """
140 | if isinstance(plugin_names_from_tag_parameter, (six.binary_type, six.text_type)):
141 | names = plugin_names_from_tag_parameter.split(',')
142 | return [n.strip() for n in names]
143 | else:
144 | return [PLUGINS_DEFAULT]
145 |
146 |
147 | def _get_all_resources_for_plugins(plugin_names, resource_type):
148 | """
149 | Returns a list of URLs for the plugins with the specified resource type (js, css, ...)
150 | :param plugin_names:
151 | :param resource_type:
152 | :return:
153 | """
154 | result = []
155 | for plugin_name in plugin_names:
156 | if plugin_name in PLUGINS:
157 | result.extend(PLUGINS[plugin_name].get(resource_type, []))
158 |
159 | return result
160 |
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | # Makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line.
5 | SPHINXOPTS =
6 | SPHINXBUILD = sphinx-build
7 | PAPER =
8 | BUILDDIR = _build
9 |
10 | # User-friendly check for sphinx-build
11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
13 | endif
14 |
15 | # Internal variables.
16 | PAPEROPT_a4 = -D latex_paper_size=a4
17 | PAPEROPT_letter = -D latex_paper_size=letter
18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
19 | # the i18n builder cannot share the environment and doctrees with the others
20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
21 |
22 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
23 |
24 | help:
25 | @echo "Please use \`make ' where is one of"
26 | @echo " html to make standalone HTML files"
27 | @echo " dirhtml to make HTML files named index.html in directories"
28 | @echo " singlehtml to make a single large HTML file"
29 | @echo " pickle to make pickle files"
30 | @echo " json to make JSON files"
31 | @echo " htmlhelp to make HTML files and a HTML help project"
32 | @echo " qthelp to make HTML files and a qthelp project"
33 | @echo " devhelp to make HTML files and a Devhelp project"
34 | @echo " epub to make an epub"
35 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
36 | @echo " latexpdf to make LaTeX files and run them through pdflatex"
37 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
38 | @echo " text to make text files"
39 | @echo " man to make manual pages"
40 | @echo " texinfo to make Texinfo files"
41 | @echo " info to make Texinfo files and run them through makeinfo"
42 | @echo " gettext to make PO message catalogs"
43 | @echo " changes to make an overview of all changed/added/deprecated items"
44 | @echo " xml to make Docutils-native XML files"
45 | @echo " pseudoxml to make pseudoxml-XML files for display purposes"
46 | @echo " linkcheck to check all external links for integrity"
47 | @echo " doctest to run all doctests embedded in the documentation (if enabled)"
48 |
49 | clean:
50 | rm -rf $(BUILDDIR)/*
51 |
52 | html:
53 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
54 | @echo
55 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
56 |
57 | dirhtml:
58 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
59 | @echo
60 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
61 |
62 | singlehtml:
63 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
64 | @echo
65 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
66 |
67 | pickle:
68 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
69 | @echo
70 | @echo "Build finished; now you can process the pickle files."
71 |
72 | json:
73 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
74 | @echo
75 | @echo "Build finished; now you can process the JSON files."
76 |
77 | htmlhelp:
78 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
79 | @echo
80 | @echo "Build finished; now you can run HTML Help Workshop with the" \
81 | ".hhp project file in $(BUILDDIR)/htmlhelp."
82 |
83 | qthelp:
84 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
85 | @echo
86 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \
87 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
88 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/DjangoLeaflet.qhcp"
89 | @echo "To view the help file:"
90 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/DjangoLeaflet.qhc"
91 |
92 | devhelp:
93 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
94 | @echo
95 | @echo "Build finished."
96 | @echo "To view the help file:"
97 | @echo "# mkdir -p $$HOME/.local/share/devhelp/DjangoLeaflet"
98 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/DjangoLeaflet"
99 | @echo "# devhelp"
100 |
101 | epub:
102 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
103 | @echo
104 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
105 |
106 | latex:
107 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
108 | @echo
109 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
110 | @echo "Run \`make' in that directory to run these through (pdf)latex" \
111 | "(use \`make latexpdf' here to do that automatically)."
112 |
113 | latexpdf:
114 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
115 | @echo "Running LaTeX files through pdflatex..."
116 | $(MAKE) -C $(BUILDDIR)/latex all-pdf
117 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
118 |
119 | latexpdfja:
120 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
121 | @echo "Running LaTeX files through platex and dvipdfmx..."
122 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
123 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
124 |
125 | text:
126 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
127 | @echo
128 | @echo "Build finished. The text files are in $(BUILDDIR)/text."
129 |
130 | man:
131 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
132 | @echo
133 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
134 |
135 | texinfo:
136 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
137 | @echo
138 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
139 | @echo "Run \`make' in that directory to run these through makeinfo" \
140 | "(use \`make info' here to do that automatically)."
141 |
142 | info:
143 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
144 | @echo "Running Texinfo files through makeinfo..."
145 | make -C $(BUILDDIR)/texinfo info
146 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
147 |
148 | gettext:
149 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
150 | @echo
151 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
152 |
153 | changes:
154 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
155 | @echo
156 | @echo "The overview file is in $(BUILDDIR)/changes."
157 |
158 | linkcheck:
159 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
160 | @echo
161 | @echo "Link check complete; look for any errors in the above output " \
162 | "or in $(BUILDDIR)/linkcheck/output.txt."
163 |
164 | doctest:
165 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
166 | @echo "Testing of doctests in the sources finished, look at the " \
167 | "results in $(BUILDDIR)/doctest/output.txt."
168 |
169 | xml:
170 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
171 | @echo
172 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml."
173 |
174 | pseudoxml:
175 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
176 | @echo
177 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
178 |
--------------------------------------------------------------------------------
/docs/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | REM Command file for Sphinx documentation
4 |
5 | if "%SPHINXBUILD%" == "" (
6 | set SPHINXBUILD=sphinx-build
7 | )
8 | set BUILDDIR=_build
9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
10 | set I18NSPHINXOPTS=%SPHINXOPTS% .
11 | if NOT "%PAPER%" == "" (
12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
14 | )
15 |
16 | if "%1" == "" goto help
17 |
18 | if "%1" == "help" (
19 | :help
20 | echo.Please use `make ^` where ^ is one of
21 | echo. html to make standalone HTML files
22 | echo. dirhtml to make HTML files named index.html in directories
23 | echo. singlehtml to make a single large HTML file
24 | echo. pickle to make pickle files
25 | echo. json to make JSON files
26 | echo. htmlhelp to make HTML files and a HTML help project
27 | echo. qthelp to make HTML files and a qthelp project
28 | echo. devhelp to make HTML files and a Devhelp project
29 | echo. epub to make an epub
30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
31 | echo. text to make text files
32 | echo. man to make manual pages
33 | echo. texinfo to make Texinfo files
34 | echo. gettext to make PO message catalogs
35 | echo. changes to make an overview over all changed/added/deprecated items
36 | echo. xml to make Docutils-native XML files
37 | echo. pseudoxml to make pseudoxml-XML files for display purposes
38 | echo. linkcheck to check all external links for integrity
39 | echo. doctest to run all doctests embedded in the documentation if enabled
40 | goto end
41 | )
42 |
43 | if "%1" == "clean" (
44 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
45 | del /q /s %BUILDDIR%\*
46 | goto end
47 | )
48 |
49 |
50 | %SPHINXBUILD% 2> nul
51 | if errorlevel 9009 (
52 | echo.
53 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
54 | echo.installed, then set the SPHINXBUILD environment variable to point
55 | echo.to the full path of the 'sphinx-build' executable. Alternatively you
56 | echo.may add the Sphinx directory to PATH.
57 | echo.
58 | echo.If you don't have Sphinx installed, grab it from
59 | echo.http://sphinx-doc.org/
60 | exit /b 1
61 | )
62 |
63 | if "%1" == "html" (
64 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
65 | if errorlevel 1 exit /b 1
66 | echo.
67 | echo.Build finished. The HTML pages are in %BUILDDIR%/html.
68 | goto end
69 | )
70 |
71 | if "%1" == "dirhtml" (
72 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
73 | if errorlevel 1 exit /b 1
74 | echo.
75 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
76 | goto end
77 | )
78 |
79 | if "%1" == "singlehtml" (
80 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
81 | if errorlevel 1 exit /b 1
82 | echo.
83 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
84 | goto end
85 | )
86 |
87 | if "%1" == "pickle" (
88 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
89 | if errorlevel 1 exit /b 1
90 | echo.
91 | echo.Build finished; now you can process the pickle files.
92 | goto end
93 | )
94 |
95 | if "%1" == "json" (
96 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
97 | if errorlevel 1 exit /b 1
98 | echo.
99 | echo.Build finished; now you can process the JSON files.
100 | goto end
101 | )
102 |
103 | if "%1" == "htmlhelp" (
104 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
105 | if errorlevel 1 exit /b 1
106 | echo.
107 | echo.Build finished; now you can run HTML Help Workshop with the ^
108 | .hhp project file in %BUILDDIR%/htmlhelp.
109 | goto end
110 | )
111 |
112 | if "%1" == "qthelp" (
113 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
114 | if errorlevel 1 exit /b 1
115 | echo.
116 | echo.Build finished; now you can run "qcollectiongenerator" with the ^
117 | .qhcp project file in %BUILDDIR%/qthelp, like this:
118 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\DjangoLeaflet.qhcp
119 | echo.To view the help file:
120 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\DjangoLeaflet.ghc
121 | goto end
122 | )
123 |
124 | if "%1" == "devhelp" (
125 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
126 | if errorlevel 1 exit /b 1
127 | echo.
128 | echo.Build finished.
129 | goto end
130 | )
131 |
132 | if "%1" == "epub" (
133 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
134 | if errorlevel 1 exit /b 1
135 | echo.
136 | echo.Build finished. The epub file is in %BUILDDIR%/epub.
137 | goto end
138 | )
139 |
140 | if "%1" == "latex" (
141 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
142 | if errorlevel 1 exit /b 1
143 | echo.
144 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
145 | goto end
146 | )
147 |
148 | if "%1" == "latexpdf" (
149 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
150 | cd %BUILDDIR%/latex
151 | make all-pdf
152 | cd %BUILDDIR%/..
153 | echo.
154 | echo.Build finished; the PDF files are in %BUILDDIR%/latex.
155 | goto end
156 | )
157 |
158 | if "%1" == "latexpdfja" (
159 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
160 | cd %BUILDDIR%/latex
161 | make all-pdf-ja
162 | cd %BUILDDIR%/..
163 | echo.
164 | echo.Build finished; the PDF files are in %BUILDDIR%/latex.
165 | goto end
166 | )
167 |
168 | if "%1" == "text" (
169 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
170 | if errorlevel 1 exit /b 1
171 | echo.
172 | echo.Build finished. The text files are in %BUILDDIR%/text.
173 | goto end
174 | )
175 |
176 | if "%1" == "man" (
177 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
178 | if errorlevel 1 exit /b 1
179 | echo.
180 | echo.Build finished. The manual pages are in %BUILDDIR%/man.
181 | goto end
182 | )
183 |
184 | if "%1" == "texinfo" (
185 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
186 | if errorlevel 1 exit /b 1
187 | echo.
188 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
189 | goto end
190 | )
191 |
192 | if "%1" == "gettext" (
193 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
194 | if errorlevel 1 exit /b 1
195 | echo.
196 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
197 | goto end
198 | )
199 |
200 | if "%1" == "changes" (
201 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
202 | if errorlevel 1 exit /b 1
203 | echo.
204 | echo.The overview file is in %BUILDDIR%/changes.
205 | goto end
206 | )
207 |
208 | if "%1" == "linkcheck" (
209 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
210 | if errorlevel 1 exit /b 1
211 | echo.
212 | echo.Link check complete; look for any errors in the above output ^
213 | or in %BUILDDIR%/linkcheck/output.txt.
214 | goto end
215 | )
216 |
217 | if "%1" == "doctest" (
218 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
219 | if errorlevel 1 exit /b 1
220 | echo.
221 | echo.Testing of doctests in the sources finished, look at the ^
222 | results in %BUILDDIR%/doctest/output.txt.
223 | goto end
224 | )
225 |
226 | if "%1" == "xml" (
227 | %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
228 | if errorlevel 1 exit /b 1
229 | echo.
230 | echo.Build finished. The XML files are in %BUILDDIR%/xml.
231 | goto end
232 | )
233 |
234 | if "%1" == "pseudoxml" (
235 | %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
236 | if errorlevel 1 exit /b 1
237 | echo.
238 | echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
239 | goto end
240 | )
241 |
242 | :end
243 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
166 |
--------------------------------------------------------------------------------
/docs/templates.rst:
--------------------------------------------------------------------------------
1 | Use in templates
2 | ================
3 |
4 | Use Leaflet API
5 | ---------------
6 |
7 | You can use the *Leaflet* API as usual. There are two ways to
8 | grab a reference on the just initialized map and options.
9 |
10 |
11 | **Using Javascript callback function**
12 |
13 | The easy way :
14 |
15 | ::
16 |
17 |
24 |
25 | {% leaflet_map "yourmap" callback="window.map_init_basic" %}
26 |
27 |
28 | **Using events**
29 |
30 | If you don't want to expose global callbacks :
31 |
32 | ::
33 |
34 |
42 |
43 | Event object has two properties : ``map`` and ``options`` (initialization).
44 |
45 | For Internet Explorer support, we fallback on jQuery if available ::
46 |
47 | $(window).on('map:init', function (e) {
48 | var detail = e.originalEvent ?
49 | e.originalEvent.detail : e.detail;
50 | ...
51 | L.marker([50.5, 30.5]).addTo(detail.map);
52 | ...
53 | });
54 |
55 | If you want to support archaic browsers **and** still avoid jQuery,
56 | *django-leaflet* comes with a minimalist polyfill for events.
57 | Add it in ```` this way ::
58 |
59 |
60 |
61 |
62 |
63 | Customize map size
64 | ------------------
65 |
66 | CSS is your friend:
67 |
68 | ::
69 |
70 |
82 |
83 |
84 |
85 | Configuration
86 | -------------
87 |
88 | In order to configure *django-leaflet*, just add a new section in your
89 | settings::
90 |
91 | LEAFLET_CONFIG = {
92 | # conf here
93 | }
94 |
95 | And add some of the following entries.
96 |
97 |
98 | Spatial extent
99 | ~~~~~~~~~~~~~~
100 |
101 | You can configure a global spatial extent for your maps, that will
102 | automatically center your maps, restrict panning and add reset view and scale
103 | controls. (*See advanced usage to tweak that.*)::
104 |
105 | 'SPATIAL_EXTENT': (5.0, 44.0, 7.5, 46)
106 |
107 |
108 | Initial map center and zoom level
109 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
110 |
111 | In addition to limiting your maps with ``SPATIAL_EXTENT``, you can also specify
112 | initial map center, default, min and max zoom level::
113 |
114 | 'DEFAULT_CENTER': (6.0, 45.0),
115 | 'DEFAULT_ZOOM': 16,
116 | 'MIN_ZOOM': 3,
117 | 'MAX_ZOOM': 18,
118 |
119 | The tuple/list must contain (lat,lng) coords.
120 |
121 |
122 | Default tiles layer
123 | ~~~~~~~~~~~~~~~~~~~
124 |
125 | To globally add a tiles layer to your maps::
126 |
127 | 'TILES': 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'
128 |
129 | This setting can also be a list of tuples ``(name, url, options)``.
130 | The python dict ``options`` accepts all the Leaflet tileLayers options.
131 |
132 | If it contains several layers, a layer switcher will then be added automatically.
133 |
134 | ::
135 |
136 | 'TILES': [('Satellite', 'http://server/a/...', {'attribution': '© Big eye', 'maxZoom': 16}),
137 | ('Streets', 'http://server/b/...', {'attribution': '© Contributors'})]
138 |
139 |
140 | If you omit this setting, a default OpenSTreetMap layer will be created for your convenience. If you do not want
141 | a default layers (perhaps to add them in your own JavaScript code on map initialization), set the value to an empty
142 | list, as shown below.
143 |
144 | ::
145 |
146 | 'TILES': []
147 |
148 | Note that this will also prevent any overlays defined in settings from being displayed.
149 |
150 |
151 | Overlay layers
152 | ~~~~~~~~~~~~~~
153 |
154 | To globally add an overlay layer, use the same syntax as tiles::
155 |
156 | 'OVERLAYS': [('Cadastral', 'http://server/a/{z}/{x}/{y}.png', {'attribution': '© IGN'})]
157 |
158 | Currently, overlay layers from settings are limited to tiles. For vectorial overlays, you
159 | will have to add them via JavaScript (see events).
160 |
161 |
162 | Attribution prefix
163 | ~~~~~~~~~~~~~~~~~~
164 |
165 | To globally add an attribution prefix on maps (most likely an empty string) ::
166 |
167 | 'ATTRIBUTION_PREFIX': 'Powered by django-leaflet'
168 |
169 | Default is ``None``, which leaves the value to `Leaflet's default `_.
170 |
171 |
172 | Scale control
173 | ~~~~~~~~~~~~~
174 |
175 | Scale control may be set to show 'metric' (m/km), or 'imperial' (mi/ft) scale
176 | lines, or 'both'. Default is 'metric'.
177 |
178 | Enable metric and imperial scale control::
179 |
180 | 'SCALE': 'both'
181 |
182 | Disable scale control::
183 |
184 | 'SCALE': None
185 |
186 |
187 | Minimap control
188 | ~~~~~~~~~~~~~~~
189 |
190 | Shows a small map in the corner which shows the same as the main map with a
191 | set zoom offset::
192 |
193 | 'MINIMAP': True
194 |
195 | By default it shows the tiles of the first layer in the list.
196 |
197 | (`More info... `_)
198 |
199 | Reset view button
200 | ~~~~~~~~~~~~~~~~~
201 |
202 | By default, a button appears below the zoom controls and, when clicked, shows the entire map.
203 | To remove this button, set::
204 |
205 | 'RESET_VIEW': False
206 |
207 |
208 | Global initialization functions and ``window.maps``
209 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
210 |
211 | Since 0.7.0, the ``leaflet_map`` template tag no longer registers initialization functions in global scope,
212 | and no longer adds map objects into ``window.maps`` array by default. To restore these features, use::
213 |
214 | 'NO_GLOBALS' = False
215 |
216 | Force Leaflet image path
217 | ~~~~~~~~~~~~~~~~~~~~~~~~
218 |
219 | If you are using staticfiles compression libraries such as django_compressor,
220 | which can do any of compressing, concatenating or renaming javascript files,
221 | this may break Leaflet's own ability to determine its installed path, and in
222 | turn break the method ``L.Icon.Default.imagePath()``.
223 |
224 | To use Django's own knowledge of its static files to force this value
225 | explicitly, use::
226 |
227 | 'FORCE_IMAGE_PATH': True
228 |
229 | Plugins
230 | ~~~~~~~
231 |
232 | To ease the usage of plugins, django-leaflet allows specifying a set of plugins, that can
233 | later be referred to from the template tags by name::
234 |
235 | 'PLUGINS': {
236 | 'name-of-plugin': {
237 | 'css': ['relative/path/to/stylesheet.css', '/root/path/to/stylesheet.css'],
238 | 'js': 'http://absolute-url.example.com/path/to/script.js',
239 | 'auto-include': True,
240 | },
241 | . . .
242 | }
243 |
244 | Both 'css' and 'js' support identical features for specifying resource URLs:
245 |
246 | * can be either a plain string or a list of URLs
247 | * each string can be:
248 |
249 | * absolute URL - will be included as-is; **example**: ``http://absolute-url.example.com/path/to/script.js``
250 | * a URL beginning from the root - will be included as-is; **example**: ``/root/path/to/stylesheet.css``
251 | * a relative URL - settings.STATIC_URL will be prepended; **example**: ``relative/path/to/stylesheet.css`` will be included as **/static/relative/path/to/stylesheet.css** (depending on your setting for STATIC_URL)
252 |
253 | Now, use ``leaflet_js`` and ``leaflet_css`` tags to load CSS and JS resources of
254 | configured Leaflet plugins.
255 |
256 | By default only plugins with ``'auto-include'`` as True will be included.
257 |
258 | To include specific plugins in the page, specify plugin names, comma separated::
259 |
260 | {% load leaflet_tags %}
261 |
262 |
263 | ...
264 | {% leaflet_js plugins="bouncemarker,draw" %}
265 | {% leaflet_css plugins="bouncemarker,draw" %}
266 |
267 |
268 | To include all plugins configured in ``LEAFLET_CONFIG['PLUGINS']``, use::
269 |
270 | {% leaflet_js plugins="ALL" %}
271 | {% leaflet_css plugins="ALL" %}
272 |
--------------------------------------------------------------------------------
/leaflet/static/leaflet/proj4leaflet.js:
--------------------------------------------------------------------------------
1 | (function (factory) {
2 | var L, proj4;
3 | if (typeof define === 'function' && define.amd) {
4 | // AMD
5 | define(['leaflet', 'proj4'], factory);
6 | } else if (typeof module === 'object' && typeof module.exports === "object") {
7 | // Node/CommonJS
8 | L = require('leaflet');
9 | proj4 = require('proj4');
10 | module.exports = factory(L, proj4);
11 | } else {
12 | // Browser globals
13 | if (typeof window.L === 'undefined' || typeof window.proj4 === 'undefined')
14 | throw 'Leaflet and proj4 must be loaded first';
15 | factory(window.L, window.proj4);
16 | }
17 | }(function (L, proj4) {
18 |
19 | L.Proj = {};
20 |
21 | L.Proj._isProj4Obj = function(a) {
22 | return (typeof a.inverse !== 'undefined' &&
23 | typeof a.forward !== 'undefined');
24 | };
25 |
26 | L.Proj.Projection = L.Class.extend({
27 | initialize: function(code, def, bounds) {
28 | var isP4 = L.Proj._isProj4Obj(code);
29 | this._proj = isP4 ? code : this._projFromCodeDef(code, def);
30 | this.bounds = isP4 ? def : bounds;
31 | },
32 |
33 | project: function (latlng) {
34 | var point = this._proj.forward([latlng.lng, latlng.lat]);
35 | return new L.Point(point[0], point[1]);
36 | },
37 |
38 | unproject: function (point, unbounded) {
39 | var point2 = this._proj.inverse([point.x, point.y]);
40 | return new L.LatLng(point2[1], point2[0], unbounded);
41 | },
42 |
43 | _projFromCodeDef: function(code, def) {
44 | if (def) {
45 | proj4.defs(code, def);
46 | } else if (proj4.defs[code] === undefined) {
47 | var urn = code.split(':');
48 | if (urn.length > 3) {
49 | code = urn[urn.length - 3] + ':' + urn[urn.length - 1];
50 | }
51 | if (proj4.defs[code] === undefined) {
52 | throw 'No projection definition for code ' + code;
53 | }
54 | }
55 |
56 | return proj4(code);
57 | }
58 | });
59 |
60 | L.Proj.CRS = L.Class.extend({
61 | includes: L.CRS,
62 |
63 | options: {
64 | transformation: new L.Transformation(1, 0, -1, 0)
65 | },
66 |
67 | initialize: function(a, b, c) {
68 | var code,
69 | proj,
70 | def,
71 | options;
72 |
73 | if (L.Proj._isProj4Obj(a)) {
74 | proj = a;
75 | code = proj.srsCode;
76 | options = b || {};
77 |
78 | this.projection = new L.Proj.Projection(proj, options.bounds);
79 | } else {
80 | code = a;
81 | def = b;
82 | options = c || {};
83 | this.projection = new L.Proj.Projection(code, def, options.bounds);
84 | }
85 |
86 | L.Util.setOptions(this, options);
87 | this.code = code;
88 | this.transformation = this.options.transformation;
89 |
90 | if (this.options.origin) {
91 | this.transformation =
92 | new L.Transformation(1, -this.options.origin[0],
93 | -1, this.options.origin[1]);
94 | }
95 |
96 | if (this.options.scales) {
97 | this._scales = this.options.scales;
98 | } else if (this.options.resolutions) {
99 | this._scales = [];
100 | for (var i = this.options.resolutions.length - 1; i >= 0; i--) {
101 | if (this.options.resolutions[i]) {
102 | this._scales[i] = 1 / this.options.resolutions[i];
103 | }
104 | }
105 | }
106 |
107 | this.infinite = !this.options.bounds;
108 |
109 | },
110 |
111 | scale: function(zoom) {
112 | var iZoom = Math.floor(zoom),
113 | baseScale,
114 | nextScale,
115 | scaleDiff,
116 | zDiff;
117 | if (zoom === iZoom) {
118 | return this._scales[zoom];
119 | } else {
120 | // Non-integer zoom, interpolate
121 | baseScale = this._scales[iZoom];
122 | nextScale = this._scales[iZoom + 1];
123 | scaleDiff = nextScale - baseScale;
124 | zDiff = (zoom - iZoom);
125 | return baseScale + scaleDiff * zDiff;
126 | }
127 | },
128 |
129 | zoom: function(scale) {
130 | // Find closest number in this._scales, down
131 | var downScale = this._closestElement(this._scales, scale),
132 | downZoom = this._scales.indexOf(downScale),
133 | nextScale,
134 | nextZoom,
135 | scaleDiff;
136 | // Check if scale is downScale => return array index
137 | if (scale === downScale) {
138 | return downZoom;
139 | }
140 | // Interpolate
141 | nextZoom = downZoom + 1;
142 | nextScale = this._scales[nextZoom];
143 | if (nextScale === undefined) {
144 | return Infinity;
145 | }
146 | scaleDiff = nextScale - downScale;
147 | return (scale - downScale) / scaleDiff + downZoom;
148 | },
149 |
150 | distance: L.CRS.Earth.distance,
151 |
152 | R: L.CRS.Earth.R,
153 |
154 | /* Get the closest lowest element in an array */
155 | _closestElement: function(array, element) {
156 | var low;
157 | for (var i = array.length; i--;) {
158 | if (array[i] <= element && (low === undefined || low < array[i])) {
159 | low = array[i];
160 | }
161 | }
162 | return low;
163 | }
164 | });
165 |
166 | L.Proj.GeoJSON = L.GeoJSON.extend({
167 | initialize: function(geojson, options) {
168 | this._callLevel = 0;
169 | L.GeoJSON.prototype.initialize.call(this, geojson, options);
170 | },
171 |
172 | addData: function(geojson) {
173 | var crs;
174 |
175 | if (geojson) {
176 | if (geojson.crs && geojson.crs.type === 'name') {
177 | crs = new L.Proj.CRS(geojson.crs.properties.name);
178 | } else if (geojson.crs && geojson.crs.type) {
179 | crs = new L.Proj.CRS(geojson.crs.type + ':' + geojson.crs.properties.code);
180 | }
181 |
182 | if (crs !== undefined) {
183 | this.options.coordsToLatLng = function(coords) {
184 | var point = L.point(coords[0], coords[1]);
185 | return crs.projection.unproject(point);
186 | };
187 | }
188 | }
189 |
190 | // Base class' addData might call us recursively, but
191 | // CRS shouldn't be cleared in that case, since CRS applies
192 | // to the whole GeoJSON, inluding sub-features.
193 | this._callLevel++;
194 | try {
195 | L.GeoJSON.prototype.addData.call(this, geojson);
196 | } finally {
197 | this._callLevel--;
198 | if (this._callLevel === 0) {
199 | delete this.options.coordsToLatLng;
200 | }
201 | }
202 | }
203 | });
204 |
205 | L.Proj.geoJson = function(geojson, options) {
206 | return new L.Proj.GeoJSON(geojson, options);
207 | };
208 |
209 | L.Proj.ImageOverlay = L.ImageOverlay.extend({
210 | initialize: function (url, bounds, options) {
211 | L.ImageOverlay.prototype.initialize.call(this, url, null, options);
212 | this._projectedBounds = bounds;
213 | },
214 |
215 | // Danger ahead: Overriding internal methods in Leaflet.
216 | // Decided to do this rather than making a copy of L.ImageOverlay
217 | // and doing very tiny modifications to it.
218 | // Future will tell if this was wise or not.
219 | _animateZoom: function (event) {
220 | var scale = this._map.getZoomScale(event.zoom);
221 | var northWest = L.point(this._projectedBounds.min.x, this._projectedBounds.max.y);
222 | var offset = this._projectedToNewLayerPoint(northWest, event.zoom, event.center);
223 |
224 | L.DomUtil.setTransform(this._image, offset, scale);
225 | },
226 |
227 | _reset: function () {
228 | var zoom = this._map.getZoom();
229 | var pixelOrigin = this._map.getPixelOrigin();
230 | var bounds = L.bounds(
231 | this._transform(this._projectedBounds.min, zoom)._subtract(pixelOrigin),
232 | this._transform(this._projectedBounds.max, zoom)._subtract(pixelOrigin)
233 | );
234 | var size = bounds.getSize();
235 |
236 | L.DomUtil.setPosition(this._image, bounds.min);
237 | this._image.style.width = size.x + 'px';
238 | this._image.style.height = size.y + 'px';
239 | },
240 |
241 | _projectedToNewLayerPoint: function (point, zoom, center) {
242 | var viewHalf = this._map.getSize()._divideBy(2);
243 | var newTopLeft = this._map.project(center, zoom)._subtract(viewHalf)._round();
244 | var topLeft = newTopLeft.add(this._map._getMapPanePos());
245 |
246 | return this._transform(point, zoom)._subtract(topLeft);
247 | },
248 |
249 | _transform: function (point, zoom) {
250 | var crs = this._map.options.crs;
251 | var transformation = crs.transformation;
252 | var scale = crs.scale(zoom);
253 |
254 | return transformation.transform(point, scale);
255 | }
256 | });
257 |
258 | L.Proj.imageOverlay = function (url, bounds, options) {
259 | return new L.Proj.ImageOverlay(url, bounds, options);
260 | };
261 |
262 | return L.Proj;
263 | }));
264 |
--------------------------------------------------------------------------------
/leaflet/static/leaflet/draw/leaflet.draw-src.css:
--------------------------------------------------------------------------------
1 | /* ================================================================== */
2 | /* Toolbars
3 | /* ================================================================== */
4 |
5 | .leaflet-draw-section {
6 | position: relative;
7 | }
8 |
9 | .leaflet-draw-toolbar {
10 | margin-top: 12px;
11 | }
12 |
13 | .leaflet-draw-toolbar-top {
14 | margin-top: 0;
15 | }
16 |
17 | .leaflet-draw-toolbar-notop a:first-child {
18 | border-top-right-radius: 0;
19 | }
20 |
21 | .leaflet-draw-toolbar-nobottom a:last-child {
22 | border-bottom-right-radius: 0;
23 | }
24 |
25 | .leaflet-draw-toolbar a {
26 | background-image: url('images/spritesheet.png');
27 | background-image: linear-gradient(transparent, transparent), url('images/spritesheet.svg');
28 | background-repeat: no-repeat;
29 | background-size: 270px 30px;
30 | background-clip: padding-box;
31 | }
32 |
33 | .leaflet-retina .leaflet-draw-toolbar a {
34 | background-image: url('images/spritesheet-2x.png');
35 | background-image: linear-gradient(transparent, transparent), url('images/spritesheet.svg');
36 | }
37 |
38 | .leaflet-draw a {
39 | display: block;
40 | text-align: center;
41 | text-decoration: none;
42 | }
43 |
44 | .leaflet-draw a .sr-only {
45 | position: absolute;
46 | width: 1px;
47 | height: 1px;
48 | padding: 0;
49 | margin: -1px;
50 | overflow: hidden;
51 | clip: rect(0,0,0,0);
52 | border: 0;
53 | }
54 |
55 | /* ================================================================== */
56 | /* Toolbar actions menu
57 | /* ================================================================== */
58 |
59 | .leaflet-draw-actions {
60 | display: none;
61 | list-style: none;
62 | margin: 0;
63 | padding: 0;
64 | position: absolute;
65 | left: 26px; /* leaflet-draw-toolbar.left + leaflet-draw-toolbar.width */
66 | top: 0;
67 | white-space: nowrap;
68 | }
69 |
70 | .leaflet-touch .leaflet-draw-actions {
71 | left: 32px;
72 | }
73 |
74 | .leaflet-right .leaflet-draw-actions {
75 | right: 26px;
76 | left: auto;
77 | }
78 |
79 | .leaflet-touch .leaflet-right .leaflet-draw-actions {
80 | right: 32px;
81 | left: auto;
82 | }
83 |
84 | .leaflet-draw-actions li {
85 | display: inline-block;
86 | }
87 |
88 | .leaflet-draw-actions li:first-child a {
89 | border-left: none;
90 | }
91 |
92 | .leaflet-draw-actions li:last-child a {
93 | -webkit-border-radius: 0 4px 4px 0;
94 | border-radius: 0 4px 4px 0;
95 | }
96 |
97 | .leaflet-right .leaflet-draw-actions li:last-child a {
98 | -webkit-border-radius: 0;
99 | border-radius: 0;
100 | }
101 |
102 | .leaflet-right .leaflet-draw-actions li:first-child a {
103 | -webkit-border-radius: 4px 0 0 4px;
104 | border-radius: 4px 0 0 4px;
105 | }
106 |
107 | .leaflet-draw-actions a {
108 | background-color: #919187;
109 | border-left: 1px solid #AAA;
110 | color: #FFF;
111 | font: 11px/19px "Helvetica Neue", Arial, Helvetica, sans-serif;
112 | line-height: 28px;
113 | text-decoration: none;
114 | padding-left: 10px;
115 | padding-right: 10px;
116 | height: 28px;
117 | }
118 |
119 | .leaflet-touch .leaflet-draw-actions a {
120 | font-size: 12px;
121 | line-height: 30px;
122 | height: 30px;
123 | }
124 |
125 | .leaflet-draw-actions-bottom {
126 | margin-top: 0;
127 | }
128 |
129 | .leaflet-draw-actions-top {
130 | margin-top: 1px;
131 | }
132 |
133 | .leaflet-draw-actions-top a,
134 | .leaflet-draw-actions-bottom a {
135 | height: 27px;
136 | line-height: 27px;
137 | }
138 |
139 | .leaflet-draw-actions a:hover {
140 | background-color: #A0A098;
141 | }
142 |
143 | .leaflet-draw-actions-top.leaflet-draw-actions-bottom a {
144 | height: 26px;
145 | line-height: 26px;
146 | }
147 |
148 | /* ================================================================== */
149 | /* Draw toolbar
150 | /* ================================================================== */
151 |
152 | .leaflet-draw-toolbar .leaflet-draw-draw-polyline {
153 | background-position: -2px -2px;
154 | }
155 |
156 | .leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-polyline {
157 | background-position: 0 -1px;
158 | }
159 |
160 | .leaflet-draw-toolbar .leaflet-draw-draw-polygon {
161 | background-position: -31px -2px;
162 | }
163 |
164 | .leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-polygon {
165 | background-position: -29px -1px;
166 | }
167 |
168 | .leaflet-draw-toolbar .leaflet-draw-draw-rectangle {
169 | background-position: -62px -2px;
170 | }
171 |
172 | .leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-rectangle {
173 | background-position: -60px -1px;
174 | }
175 |
176 | .leaflet-draw-toolbar .leaflet-draw-draw-circle {
177 | background-position: -92px -2px;
178 | }
179 |
180 | .leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-circle {
181 | background-position: -90px -1px;
182 | }
183 |
184 | .leaflet-draw-toolbar .leaflet-draw-draw-marker {
185 | background-position: -122px -2px;
186 | }
187 |
188 | .leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-marker {
189 | background-position: -120px -1px;
190 | }
191 |
192 | /* ================================================================== */
193 | /* Edit toolbar
194 | /* ================================================================== */
195 |
196 | .leaflet-draw-toolbar .leaflet-draw-edit-edit {
197 | background-position: -152px -2px;
198 | }
199 |
200 | .leaflet-touch .leaflet-draw-toolbar .leaflet-draw-edit-edit {
201 | background-position: -150px -1px;
202 | }
203 |
204 | .leaflet-draw-toolbar .leaflet-draw-edit-remove {
205 | background-position: -182px -2px;
206 | }
207 |
208 | .leaflet-touch .leaflet-draw-toolbar .leaflet-draw-edit-remove {
209 | background-position: -180px -1px;
210 | }
211 |
212 | .leaflet-draw-toolbar .leaflet-draw-edit-edit.leaflet-disabled {
213 | background-position: -212px -2px;
214 | }
215 |
216 | .leaflet-touch .leaflet-draw-toolbar .leaflet-draw-edit-edit.leaflet-disabled {
217 | background-position: -210px -1px;
218 | }
219 |
220 | .leaflet-draw-toolbar .leaflet-draw-edit-remove.leaflet-disabled {
221 | background-position: -242px -2px;
222 | }
223 |
224 | .leaflet-touch .leaflet-draw-toolbar .leaflet-draw-edit-remove.leaflet-disabled {
225 | background-position: -240px -2px;
226 | }
227 |
228 | /* ================================================================== */
229 | /* Drawing styles
230 | /* ================================================================== */
231 |
232 | .leaflet-mouse-marker {
233 | background-color: #fff;
234 | cursor: crosshair;
235 | }
236 |
237 | .leaflet-draw-tooltip {
238 | background: rgb(54, 54, 54);
239 | background: rgba(0, 0, 0, 0.5);
240 | border: 1px solid transparent;
241 | -webkit-border-radius: 4px;
242 | border-radius: 4px;
243 | color: #fff;
244 | font: 12px/18px "Helvetica Neue", Arial, Helvetica, sans-serif;
245 | margin-left: 20px;
246 | margin-top: -21px;
247 | padding: 4px 8px;
248 | position: absolute;
249 | visibility: hidden;
250 | white-space: nowrap;
251 | z-index: 6;
252 | }
253 |
254 | .leaflet-draw-tooltip:before {
255 | border-right: 6px solid black;
256 | border-right-color: rgba(0, 0, 0, 0.5);
257 | border-top: 6px solid transparent;
258 | border-bottom: 6px solid transparent;
259 | content: "";
260 | position: absolute;
261 | top: 7px;
262 | left: -7px;
263 | }
264 |
265 | .leaflet-error-draw-tooltip {
266 | background-color: #F2DEDE;
267 | border: 1px solid #E6B6BD;
268 | color: #B94A48;
269 | }
270 |
271 | .leaflet-error-draw-tooltip:before {
272 | border-right-color: #E6B6BD;
273 | }
274 |
275 | .leaflet-draw-tooltip-single {
276 | margin-top: -12px
277 | }
278 |
279 | .leaflet-draw-tooltip-subtext {
280 | color: #f8d5e4;
281 | }
282 |
283 | .leaflet-draw-guide-dash {
284 | font-size: 1%;
285 | opacity: 0.6;
286 | position: absolute;
287 | width: 5px;
288 | height: 5px;
289 | }
290 |
291 | /* ================================================================== */
292 | /* Edit styles
293 | /* ================================================================== */
294 |
295 | .leaflet-edit-marker-selected {
296 | background-color: rgba(254, 87, 161, 0.1);
297 | border: 4px dashed rgba(254, 87, 161, 0.6);
298 | -webkit-border-radius: 4px;
299 | border-radius: 4px;
300 | box-sizing: content-box;
301 | }
302 |
303 | .leaflet-edit-move {
304 | cursor: move;
305 | }
306 |
307 | .leaflet-edit-resize {
308 | cursor: pointer;
309 | }
310 |
311 | /* ================================================================== */
312 | /* Old IE styles
313 | /* ================================================================== */
314 |
315 | .leaflet-oldie .leaflet-draw-toolbar {
316 | border: 1px solid #999;
317 | }
--------------------------------------------------------------------------------
/docs/conf.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Django Leaflet documentation build configuration file, created by
4 | # sphinx-quickstart on Tue Feb 28 14:56:43 2017.
5 | #
6 | # This file is execfile()d with the current directory set to its
7 | # containing dir.
8 | #
9 | # Note that not all possible configuration values are present in this
10 | # autogenerated file.
11 | #
12 | # All configuration values have a default; values that are commented out
13 | # serve to show the default.
14 |
15 | import sys
16 | import os
17 |
18 | # If extensions (or modules to document with autodoc) are in another directory,
19 | # add these directories to sys.path here. If the directory is relative to the
20 | # documentation root, use os.path.abspath to make it absolute, like shown here.
21 | #sys.path.insert(0, os.path.abspath('.'))
22 |
23 | # -- General configuration ------------------------------------------------
24 |
25 | # If your documentation needs a minimal Sphinx version, state it here.
26 | #needs_sphinx = '1.0'
27 |
28 | # Add any Sphinx extension module names here, as strings. They can be
29 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
30 | # ones.
31 | extensions = []
32 |
33 | # Add any paths that contain templates here, relative to this directory.
34 | templates_path = ['_templates']
35 |
36 | # The suffix of source filenames.
37 | source_suffix = '.rst'
38 |
39 | # The encoding of source files.
40 | #source_encoding = 'utf-8-sig'
41 |
42 | # The master toctree document.
43 | master_doc = 'index'
44 |
45 | # General information about the project.
46 | project = u'Django Leaflet'
47 | copyright = u'2017, Makina Corpus'
48 |
49 | # The version info for the project you're documenting, acts as replacement for
50 | # |version| and |release|, also used in various other places throughout the
51 | # built documents.
52 | #
53 | # The short X.Y version.
54 | version = '0.20'
55 | # The full version, including alpha/beta/rc tags.
56 | release = '0.20'
57 |
58 | # The language for content autogenerated by Sphinx. Refer to documentation
59 | # for a list of supported languages.
60 | #language = None
61 |
62 | # There are two options for replacing |today|: either, you set today to some
63 | # non-false value, then it is used:
64 | #today = ''
65 | # Else, today_fmt is used as the format for a strftime call.
66 | #today_fmt = '%B %d, %Y'
67 |
68 | # List of patterns, relative to source directory, that match files and
69 | # directories to ignore when looking for source files.
70 | exclude_patterns = ['_build']
71 |
72 | # The reST default role (used for this markup: `text`) to use for all
73 | # documents.
74 | #default_role = None
75 |
76 | # If true, '()' will be appended to :func: etc. cross-reference text.
77 | #add_function_parentheses = True
78 |
79 | # If true, the current module name will be prepended to all description
80 | # unit titles (such as .. function::).
81 | #add_module_names = True
82 |
83 | # If true, sectionauthor and moduleauthor directives will be shown in the
84 | # output. They are ignored by default.
85 | #show_authors = False
86 |
87 | # The name of the Pygments (syntax highlighting) style to use.
88 | pygments_style = 'sphinx'
89 |
90 | # A list of ignored prefixes for module index sorting.
91 | #modindex_common_prefix = []
92 |
93 | # If true, keep warnings as "system message" paragraphs in the built documents.
94 | #keep_warnings = False
95 |
96 |
97 | # -- Options for HTML output ----------------------------------------------
98 |
99 | # The theme to use for HTML and HTML Help pages. See the documentation for
100 | # a list of builtin themes.
101 | html_theme = 'default'
102 |
103 | # Theme options are theme-specific and customize the look and feel of a theme
104 | # further. For a list of options available for each theme, see the
105 | # documentation.
106 | #html_theme_options = {}
107 |
108 | # Add any paths that contain custom themes here, relative to this directory.
109 | #html_theme_path = []
110 |
111 | # The name for this set of Sphinx documents. If None, it defaults to
112 | # " v documentation".
113 | #html_title = None
114 |
115 | # A shorter title for the navigation bar. Default is the same as html_title.
116 | #html_short_title = None
117 |
118 | # The name of an image file (relative to this directory) to place at the top
119 | # of the sidebar.
120 | #html_logo = None
121 |
122 | # The name of an image file (within the static path) to use as favicon of the
123 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
124 | # pixels large.
125 | #html_favicon = None
126 |
127 | # Add any paths that contain custom static files (such as style sheets) here,
128 | # relative to this directory. They are copied after the builtin static files,
129 | # so a file named "default.css" will overwrite the builtin "default.css".
130 | html_static_path = ['_static']
131 |
132 | # Add any extra paths that contain custom files (such as robots.txt or
133 | # .htaccess) here, relative to this directory. These files are copied
134 | # directly to the root of the documentation.
135 | #html_extra_path = []
136 |
137 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
138 | # using the given strftime format.
139 | #html_last_updated_fmt = '%b %d, %Y'
140 |
141 | # If true, SmartyPants will be used to convert quotes and dashes to
142 | # typographically correct entities.
143 | #html_use_smartypants = True
144 |
145 | # Custom sidebar templates, maps document names to template names.
146 | #html_sidebars = {}
147 |
148 | # Additional templates that should be rendered to pages, maps page names to
149 | # template names.
150 | #html_additional_pages = {}
151 |
152 | # If false, no module index is generated.
153 | #html_domain_indices = True
154 |
155 | # If false, no index is generated.
156 | #html_use_index = True
157 |
158 | # If true, the index is split into individual pages for each letter.
159 | #html_split_index = False
160 |
161 | # If true, links to the reST sources are added to the pages.
162 | #html_show_sourcelink = True
163 |
164 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
165 | #html_show_sphinx = True
166 |
167 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
168 | #html_show_copyright = True
169 |
170 | # If true, an OpenSearch description file will be output, and all pages will
171 | # contain a tag referring to it. The value of this option must be the
172 | # base URL from which the finished HTML is served.
173 | #html_use_opensearch = ''
174 |
175 | # This is the file name suffix for HTML files (e.g. ".xhtml").
176 | #html_file_suffix = None
177 |
178 | # Output file base name for HTML help builder.
179 | htmlhelp_basename = 'DjangoLeafletdoc'
180 |
181 |
182 | # -- Options for LaTeX output ---------------------------------------------
183 |
184 | latex_elements = {
185 | # The paper size ('letterpaper' or 'a4paper').
186 | #'papersize': 'letterpaper',
187 |
188 | # The font size ('10pt', '11pt' or '12pt').
189 | #'pointsize': '10pt',
190 |
191 | # Additional stuff for the LaTeX preamble.
192 | #'preamble': '',
193 | }
194 |
195 | # Grouping the document tree into LaTeX files. List of tuples
196 | # (source start file, target name, title,
197 | # author, documentclass [howto, manual, or own class]).
198 | latex_documents = [
199 | ('index', 'DjangoLeaflet.tex', u'Django Leaflet Documentation',
200 | u'Makina Corpus', 'manual'),
201 | ]
202 |
203 | # The name of an image file (relative to this directory) to place at the top of
204 | # the title page.
205 | #latex_logo = None
206 |
207 | # For "manual" documents, if this is true, then toplevel headings are parts,
208 | # not chapters.
209 | #latex_use_parts = False
210 |
211 | # If true, show page references after internal links.
212 | #latex_show_pagerefs = False
213 |
214 | # If true, show URL addresses after external links.
215 | #latex_show_urls = False
216 |
217 | # Documents to append as an appendix to all manuals.
218 | #latex_appendices = []
219 |
220 | # If false, no module index is generated.
221 | #latex_domain_indices = True
222 |
223 |
224 | # -- Options for manual page output ---------------------------------------
225 |
226 | # One entry per manual page. List of tuples
227 | # (source start file, name, description, authors, manual section).
228 | man_pages = [
229 | ('index', 'djangoleaflet', u'Django Leaflet Documentation',
230 | [u'Makina Corpus'], 1)
231 | ]
232 |
233 | # If true, show URL addresses after external links.
234 | #man_show_urls = False
235 |
236 |
237 | # -- Options for Texinfo output -------------------------------------------
238 |
239 | # Grouping the document tree into Texinfo files. List of tuples
240 | # (source start file, target name, title, author,
241 | # dir menu entry, description, category)
242 | texinfo_documents = [
243 | ('index', 'DjangoLeaflet', u'Django Leaflet Documentation',
244 | u'Makina Corpus', 'DjangoLeaflet', 'One line description of project.',
245 | 'Miscellaneous'),
246 | ]
247 |
248 | # Documents to append as an appendix to all manuals.
249 | #texinfo_appendices = []
250 |
251 | # If false, no module index is generated.
252 | #texinfo_domain_indices = True
253 |
254 | # How to display URL addresses: 'footnote', 'no', or 'inline'.
255 | #texinfo_show_urls = 'footnote'
256 |
257 | # If true, do not generate a @detailmenu in the "Top" node's menu.
258 | #texinfo_no_detailmenu = False
259 |
--------------------------------------------------------------------------------
/leaflet/static/leaflet/leaflet.extras.js:
--------------------------------------------------------------------------------
1 | L.Control.ResetView = L.Control.extend({
2 | statics: {
3 | ICON: 'url(images/reset-view.png)',
4 | TITLE: "Reset view"
5 | },
6 |
7 | options: {
8 | position: 'topleft'
9 | },
10 |
11 | initialize: function (bounds, options) {
12 | // Accept function as argument to bounds
13 | this.getBounds = typeof(bounds) == 'function' ? bounds :
14 | function () {
15 | return bounds;
16 | };
17 |
18 | L.Util.setOptions(this, options);
19 | },
20 |
21 | onAdd: function (map) {
22 | if (map.resetviewControl) {
23 | map.removeControl(map.resetviewControl);
24 | }
25 | map.resetviewControl = this;
26 |
27 | var container = L.DomUtil.create('div', 'leaflet-control-zoom leaflet-bar');
28 | var link = L.DomUtil.create('a', 'leaflet-control-zoom-out leaflet-bar-part', container);
29 | link.href = '#';
30 | link.title = L.Control.ResetView.TITLE;
31 | link.style.backgroundImage = L.Control.ResetView.ICON;
32 |
33 | L.DomEvent.addListener(link, 'click', L.DomEvent.stopPropagation)
34 | .addListener(link, 'click', L.DomEvent.preventDefault)
35 | .addListener(link, 'click', L.Util.bind(function () {
36 | map.fitBounds(this.getBounds());
37 | }, this));
38 | return container;
39 | }
40 | });
41 |
42 |
43 | L.Map.DjangoMap = L.Map.extend({
44 |
45 | initialize: function (id, options) {
46 | // Merge compatible options
47 | // (can be undefined)
48 | var djoptions = options.djoptions;
49 | options.zoom = djoptions.zoom;
50 | options.center = djoptions.center;
51 |
52 | if (!isNaN(parseInt(djoptions.minzoom, 10)))
53 | options.minZoom = djoptions.minzoom;
54 |
55 | if (!isNaN(parseInt(djoptions.maxzoom, 10)))
56 | options.maxZoom = djoptions.maxzoom;
57 |
58 | // Translate to native options
59 | options = L.Util.extend(options,
60 | this._projectionOptions(djoptions));
61 | if (djoptions.extent) {
62 | options.maxBounds = djoptions.extent;
63 | }
64 |
65 | L.Map.prototype.initialize.call(this, id, options);
66 |
67 | this._djAddLayers();
68 | this._djSetupControls();
69 |
70 | if (djoptions.fitextent && djoptions.extent && !(djoptions.center || djoptions.zoom)) {
71 | this.fitBounds(options.maxBounds);
72 | }
73 | },
74 |
75 | _projectionOptions: function (djoptions) {
76 | if (!djoptions.srid)
77 | return {};
78 |
79 | var projopts = {};
80 |
81 | var bbox = djoptions.tilesextent,
82 | maxResolution = computeMaxResolution(bbox);
83 | // See https://github.com/ajashton/TileCache/blob/master/tilecache/TileCache/Layer.py#L197
84 | var resolutions = [];
85 | for (var z = 0; z < 20; z++) {
86 | resolutions.push(maxResolution / Math.pow(2, z));
87 | }
88 | var crs = new L.Proj.CRS('EPSG:' + djoptions.srid,
89 | Proj4js.defs['EPSG:' + djoptions.srid],
90 | {
91 | origin: [bbox[0], bbox[3]],
92 | resolutions: resolutions
93 | });
94 | return {
95 | crs: crs,
96 | scale: crs.scale,
97 | continuousWorld: true
98 | };
99 |
100 | function computeMaxResolution(bbox) {
101 | // See https://github.com/ajashton/TileCache/blob/master/tilecache/TileCache/Layer.py#L185-L196
102 | var size = 256,
103 | width = bbox[2] - bbox[0],
104 | height = bbox[3] - bbox[1];
105 | var aspect = Math.floor(Math.max(width, height) / Math.min(width, height) + 0.5);
106 | return Math.max(width, height) / (size * aspect);
107 | }
108 | },
109 |
110 | _djAddLayers: function () {
111 | var layers = this.options.djoptions.layers;
112 | var overlays = this.options.djoptions.overlays || [];
113 | var continuousWorld = this.options.continuousWorld;
114 |
115 | if (!layers || !layers.length) {
116 | // No layers, we're done (ignoring overlays)
117 | return;
118 | }
119 |
120 | if (layers.length == 1 && overlays.length == 0) {
121 | var layer = l2d(layers[0]);
122 | // Make the only layer match the map max/min_zoom
123 | layer.options = L.Util.extend(layer.options, {
124 | minZoom: this.options.minZoom,
125 | maxZoom: this.options.maxZoom
126 | });
127 | L.tileLayer(layer.url, layer.options).addTo(this);
128 | return;
129 | }
130 |
131 | this.layerscontrol = L.control.layers().addTo(this);
132 | for (var i = 0, n = layers.length; i < n; i++) {
133 | var layer = l2d(layers[i]),
134 | l = L.tileLayer(layer.url, layer.options);
135 | this.layerscontrol.addBaseLayer(l, layer.name);
136 | // Show first one as default
137 | if (i === 0) l.addTo(this);
138 | }
139 | for (var i = 0, n = overlays.length; i < n; i++) {
140 | var layer = l2d(overlays[i]),
141 | l = L.tileLayer(layer.url, layer.options);
142 | this.layerscontrol.addOverlay(l, layer.name);
143 | }
144 |
145 | function l2d(l) {
146 | var options = {'continuousWorld': continuousWorld};
147 | if (typeof l[2] === 'string') {
148 | // remain compatible with django-leaflet <= 0.15.0
149 | options = L.Util.extend(options, {'attribution': l[2]});
150 | } else {
151 | options = L.Util.extend(options, l[2]);
152 | }
153 | return {name: l[0], url: l[1], options: options};
154 | }
155 | },
156 |
157 | _djSetupControls: function () {
158 | // Attribution prefix ?
159 | if (this.attributionControl &&
160 | this.options.djoptions.attributionprefix !== null) {
161 | this.attributionControl.setPrefix(this.options.djoptions.attributionprefix);
162 | }
163 |
164 | // Scale control ?
165 | if (this.options.djoptions.scale) {
166 | this.whenReady(function () {
167 | var scale_opt = this.options.djoptions.scale;
168 | var show_imperial = /both|imperial/.test(scale_opt);
169 | var show_metric = /both|metric/.test(scale_opt);
170 | new L.Control.Scale({imperial: show_imperial, metric: show_metric}).addTo(this);
171 | }, this);
172 | }
173 |
174 | // Minimap control ?
175 | if (this.options.djoptions.minimap) {
176 | for (var firstLayer in this._layers) break;
177 | var url = this._layers[firstLayer]._url;
178 | var layer = L.tileLayer(url);
179 | this.minimapcontrol = null;
180 | this.whenReady(function () {
181 | this.minimapcontrol = new L.Control.MiniMap(layer,
182 | {toggleDisplay: true}).addTo(this);
183 | }, this);
184 | }
185 |
186 | // ResetView control ?
187 | if (this.options.djoptions.resetview) {
188 | var bounds = this.options.djoptions.extent;
189 | if (bounds) {
190 | // Add reset view control
191 | this.whenReady(function () {
192 | new L.Control.ResetView(bounds).addTo(this);
193 | }, this);
194 | }
195 | }
196 | }
197 |
198 | });
199 |
200 |
201 | L.Map.djangoMap = function (id, options) {
202 | var container = L.DomUtil.get(id);
203 | if (container._leaflet) // Already initialized
204 | return;
205 |
206 | var map = new L.Map.DjangoMap(id, options);
207 |
208 | if (options.globals) {
209 | // Register document maps, like window.forms :)
210 | window.maps = window.maps || [];
211 | window.maps.push(map);
212 | }
213 |
214 | if (options.callback === null) {
215 | /*
216 | * Deprecate django-leaflet < 0.7 default callback
217 | */
218 | var defaultcb = window[id + 'Init'];
219 | if (typeof(defaultcb) == 'function') {
220 | options.callback = defaultcb;
221 | if (console) console.warn('DEPRECATED: Use of default callback ' + defaultcb.name + '() is deprecated (see documentation).');
222 | }
223 | }
224 |
225 | /*
226 | * Trigger custom map:init Event
227 | */
228 | triggerEvent(window, 'map:init', {id: id, map: map, options: options});
229 |
230 | /*
231 | * Run callback if specified
232 | */
233 | if (typeof(options.callback) == 'function') {
234 | options.callback(map, options);
235 | }
236 |
237 | return map;
238 |
239 |
240 | function triggerEvent(target, type, data) {
241 | if (typeof window.CustomEvent == 'function') {
242 | var evt = new CustomEvent(type, {detail: data});
243 | target.dispatchEvent(evt);
244 | }
245 | else if (window.jQuery) {
246 | var evt = jQuery.Event(type);
247 | evt.detail = data;
248 | jQuery(target).trigger(evt);
249 | }
250 | }
251 | };
252 |
--------------------------------------------------------------------------------
/leaflet/static/leaflet/Control.MiniMap.js:
--------------------------------------------------------------------------------
1 | L.Control.MiniMap = L.Control.extend({
2 | options: {
3 | position: 'bottomright',
4 | toggleDisplay: false,
5 | zoomLevelOffset: -5,
6 | zoomLevelFixed: false,
7 | zoomAnimation: false,
8 | autoToggleDisplay: false,
9 | width: 150,
10 | height: 150,
11 | aimingRectOptions: {color: "#ff7800", weight: 1, clickable: false},
12 | shadowRectOptions: {color: "#000000", weight: 1, clickable: false, opacity:0, fillOpacity:0}
13 | },
14 |
15 | hideText: 'Hide MiniMap',
16 |
17 | showText: 'Show MiniMap',
18 |
19 | //layer is the map layer to be shown in the minimap
20 | initialize: function (layer, options) {
21 | L.Util.setOptions(this, options);
22 | //Make sure the aiming rects are non-clickable even if the user tries to set them clickable (most likely by forgetting to specify them false)
23 | this.options.aimingRectOptions.clickable = false;
24 | this.options.shadowRectOptions.clickable = false;
25 | this._layer = layer;
26 | },
27 |
28 | onAdd: function (map) {
29 |
30 | this._mainMap = map;
31 |
32 | //Creating the container and stopping events from spilling through to the main map.
33 | this._container = L.DomUtil.create('div', 'leaflet-control-minimap');
34 | this._container.style.width = this.options.width + 'px';
35 | this._container.style.height = this.options.height + 'px';
36 | L.DomEvent.disableClickPropagation(this._container);
37 | L.DomEvent.on(this._container, 'mousewheel', L.DomEvent.stopPropagation);
38 |
39 |
40 | this._miniMap = new L.Map(this._container,
41 | {
42 | attributionControl: false,
43 | zoomControl: false,
44 | zoomAnimation: this.options.zoomAnimation,
45 | autoToggleDisplay: this.options.autoToggleDisplay,
46 | touchZoom: !this.options.zoomLevelFixed,
47 | scrollWheelZoom: !this.options.zoomLevelFixed,
48 | doubleClickZoom: !this.options.zoomLevelFixed,
49 | boxZoom: !this.options.zoomLevelFixed,
50 | crs: map.options.crs
51 | });
52 |
53 | this._miniMap.addLayer(this._layer);
54 |
55 | //These bools are used to prevent infinite loops of the two maps notifying each other that they've moved.
56 | this._mainMapMoving = false;
57 | this._miniMapMoving = false;
58 |
59 | //Keep a record of this to prevent auto toggling when the user explicitly doesn't want it.
60 | this._userToggledDisplay = false;
61 | this._minimized = false;
62 |
63 | if (this.options.toggleDisplay) {
64 | this._addToggleButton();
65 | }
66 |
67 | this._miniMap.whenReady(L.Util.bind(function () {
68 | this._aimingRect = L.rectangle(this._mainMap.getBounds(), this.options.aimingRectOptions).addTo(this._miniMap);
69 | this._shadowRect = L.rectangle(this._mainMap.getBounds(), this.options.shadowRectOptions).addTo(this._miniMap);
70 | this._mainMap.on('moveend', this._onMainMapMoved, this);
71 | this._mainMap.on('move', this._onMainMapMoving, this);
72 | this._miniMap.on('movestart', this._onMiniMapMoveStarted, this);
73 | this._miniMap.on('move', this._onMiniMapMoving, this);
74 | this._miniMap.on('moveend', this._onMiniMapMoved, this);
75 | }, this));
76 |
77 | return this._container;
78 | },
79 |
80 | addTo: function (map) {
81 | L.Control.prototype.addTo.call(this, map);
82 | this._miniMap.setView(this._mainMap.getCenter(), this._decideZoom(true));
83 | this._setDisplay(this._decideMinimized());
84 | return this;
85 | },
86 |
87 | onRemove: function (map) {
88 | this._mainMap.off('moveend', this._onMainMapMoved, this);
89 | this._mainMap.off('move', this._onMainMapMoving, this);
90 | this._miniMap.off('moveend', this._onMiniMapMoved, this);
91 |
92 | this._miniMap.removeLayer(this._layer);
93 | },
94 |
95 | _addToggleButton: function () {
96 | this._toggleDisplayButton = this.options.toggleDisplay ? this._createButton(
97 | '', this.hideText, 'leaflet-control-minimap-toggle-display', this._container, this._toggleDisplayButtonClicked, this) : undefined;
98 | },
99 |
100 | _createButton: function (html, title, className, container, fn, context) {
101 | var link = L.DomUtil.create('a', className, container);
102 | link.innerHTML = html;
103 | link.href = '#';
104 | link.title = title;
105 |
106 | var stop = L.DomEvent.stopPropagation;
107 |
108 | L.DomEvent
109 | .on(link, 'click', stop)
110 | .on(link, 'mousedown', stop)
111 | .on(link, 'dblclick', stop)
112 | .on(link, 'click', L.DomEvent.preventDefault)
113 | .on(link, 'click', fn, context);
114 |
115 | return link;
116 | },
117 |
118 | _toggleDisplayButtonClicked: function () {
119 | this._userToggledDisplay = true;
120 | if (!this._minimized) {
121 | this._minimize();
122 | this._toggleDisplayButton.title = this.showText;
123 | }
124 | else {
125 | this._restore();
126 | this._toggleDisplayButton.title = this.hideText;
127 | }
128 | },
129 |
130 | _setDisplay: function (minimize) {
131 | if (minimize != this._minimized) {
132 | if (!this._minimized) {
133 | this._minimize();
134 | }
135 | else {
136 | this._restore();
137 | }
138 | }
139 | },
140 |
141 | _minimize: function () {
142 | // hide the minimap
143 | if (this.options.toggleDisplay) {
144 | this._container.style.width = '19px';
145 | this._container.style.height = '19px';
146 | this._toggleDisplayButton.className += ' minimized';
147 | }
148 | else {
149 | this._container.style.display = 'none';
150 | }
151 | this._minimized = true;
152 | },
153 |
154 | _restore: function () {
155 | if (this.options.toggleDisplay) {
156 | this._container.style.width = this.options.width + 'px';
157 | this._container.style.height = this.options.height + 'px';
158 | this._toggleDisplayButton.className = this._toggleDisplayButton.className
159 | .replace(/(?:^|\s)minimized(?!\S)/g, '');
160 | }
161 | else {
162 | this._container.style.display = 'block';
163 | }
164 | this._minimized = false;
165 | },
166 |
167 | _onMainMapMoved: function (e) {
168 | if (!this._miniMapMoving) {
169 | this._mainMapMoving = true;
170 | this._miniMap.setView(this._mainMap.getCenter(), this._decideZoom(true));
171 | this._setDisplay(this._decideMinimized());
172 | } else {
173 | this._miniMapMoving = false;
174 | }
175 | this._aimingRect.setBounds(this._mainMap.getBounds());
176 | },
177 |
178 | _onMainMapMoving: function (e) {
179 | this._aimingRect.setBounds(this._mainMap.getBounds());
180 | },
181 |
182 | _onMiniMapMoveStarted:function (e) {
183 | var lastAimingRect = this._aimingRect.getBounds();
184 | var sw = this._miniMap.latLngToContainerPoint(lastAimingRect.getSouthWest());
185 | var ne = this._miniMap.latLngToContainerPoint(lastAimingRect.getNorthEast());
186 | this._lastAimingRectPosition = {sw:sw,ne:ne};
187 | },
188 |
189 | _onMiniMapMoving: function (e) {
190 | if (!this._mainMapMoving && this._lastAimingRectPosition) {
191 | this._shadowRect.setBounds(new L.LatLngBounds(this._miniMap.containerPointToLatLng(this._lastAimingRectPosition.sw),this._miniMap.containerPointToLatLng(this._lastAimingRectPosition.ne)));
192 | this._shadowRect.setStyle({opacity:1,fillOpacity:0.3});
193 | }
194 | },
195 |
196 | _onMiniMapMoved: function (e) {
197 | if (!this._mainMapMoving) {
198 | this._miniMapMoving = true;
199 | this._mainMap.setView(this._miniMap.getCenter(), this._decideZoom(false));
200 | this._shadowRect.setStyle({opacity:0,fillOpacity:0});
201 | } else {
202 | this._mainMapMoving = false;
203 | }
204 | },
205 |
206 | _decideZoom: function (fromMaintoMini) {
207 | if (!this.options.zoomLevelFixed) {
208 | if (fromMaintoMini)
209 | return this._mainMap.getZoom() + this.options.zoomLevelOffset;
210 | else {
211 | var currentDiff = this._miniMap.getZoom() - this._mainMap.getZoom();
212 | var proposedZoom = this._miniMap.getZoom() - this.options.zoomLevelOffset;
213 | var toRet;
214 |
215 | if (currentDiff > this.options.zoomLevelOffset && this._mainMap.getZoom() < this._miniMap.getMinZoom() - this.options.zoomLevelOffset) {
216 | //This means the miniMap is zoomed out to the minimum zoom level and can't zoom any more.
217 | if (this._miniMap.getZoom() > this._lastMiniMapZoom) {
218 | //This means the user is trying to zoom in by using the minimap, zoom the main map.
219 | toRet = this._mainMap.getZoom() + 1;
220 | //Also we cheat and zoom the minimap out again to keep it visually consistent.
221 | this._miniMap.setZoom(this._miniMap.getZoom() -1);
222 | } else {
223 | //Either the user is trying to zoom out past the mini map's min zoom or has just panned using it, we can't tell the difference.
224 | //Therefore, we ignore it!
225 | toRet = this._mainMap.getZoom();
226 | }
227 | } else {
228 | //This is what happens in the majority of cases, and always if you configure the min levels + offset in a sane fashion.
229 | toRet = proposedZoom;
230 | }
231 | this._lastMiniMapZoom = this._miniMap.getZoom();
232 | return toRet;
233 | }
234 | } else {
235 | if (fromMaintoMini)
236 | return this.options.zoomLevelFixed;
237 | else
238 | return this._mainMap.getZoom();
239 | }
240 | },
241 |
242 | _decideMinimized: function () {
243 | if (this._userToggledDisplay) {
244 | return this._minimized;
245 | }
246 |
247 | if (this.options.autoToggleDisplay) {
248 | if (this._mainMap.getBounds().contains(this._miniMap.getBounds())) {
249 | return true;
250 | }
251 | return false;
252 | }
253 |
254 | return this._minimized;
255 | }
256 | });
257 |
258 | L.Map.mergeOptions({
259 | miniMapControl: false
260 | });
261 |
262 | L.Map.addInitHook(function () {
263 | if (this.options.miniMapControl) {
264 | this.miniMapControl = (new L.Control.MiniMap()).addTo(this);
265 | }
266 | });
267 |
268 | L.control.minimap = function (options) {
269 | return new L.Control.MiniMap(options);
270 | };
271 |
--------------------------------------------------------------------------------
/leaflet/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from __future__ import unicode_literals
3 | from django.core.serializers.json import DjangoJSONEncoder
4 | from django.utils.encoding import force_text
5 | from django.utils.functional import Promise
6 |
7 | try:
8 | from urllib.parse import urlparse
9 | except ImportError:
10 | from urlparse import urlparse
11 | import warnings
12 |
13 | try:
14 | from collections import OrderedDict
15 | except ImportError:
16 | # python 2.6 compatibility (need to install ordereddict package).
17 | from ordereddict import OrderedDict
18 |
19 | from django.conf import settings
20 | from django.core.exceptions import ImproperlyConfigured
21 | from django.contrib.staticfiles.templatetags.staticfiles import static
22 | from django.utils.translation import ugettext_lazy as _
23 | from django.utils import six
24 | import django
25 |
26 | from .utils import memoized_lazy_function, ListWithLazyItems, ListWithLazyItemsRawIterator
27 |
28 |
29 | DEFAULT_TILES = [(_('OSM'), '//{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
30 | '© OpenStreetMap contributors')]
31 |
32 | LEAFLET_CONFIG = getattr(settings, 'LEAFLET_CONFIG', {})
33 |
34 | app_settings = dict({
35 | 'TILES': DEFAULT_TILES,
36 | 'OVERLAYS': [],
37 | 'ATTRIBUTION_PREFIX': None,
38 | 'LOADEVENT': 'load',
39 | 'DEFAULT_ZOOM': None,
40 | 'MIN_ZOOM': None,
41 | 'MAX_ZOOM': None,
42 | 'DEFAULT_CENTER': None,
43 | 'FORCE_IMAGE_PATH': False,
44 | 'SRID': None,
45 | 'TILES_EXTENT': [],
46 | 'SCALE': 'metric',
47 | 'MINIMAP': False,
48 | 'RESET_VIEW': True,
49 | 'NO_GLOBALS': True,
50 | 'PLUGINS': OrderedDict(),
51 | 'SPATIAL_EXTENT': (-180, -90, 180, 90),
52 | }, **LEAFLET_CONFIG)
53 |
54 |
55 | # Backward-compatibility : defaults TILES with value of TILES_URL
56 | if 'TILES_URL' in LEAFLET_CONFIG:
57 | warnings.warn("TILES_URL is deprecated.", DeprecationWarning)
58 | if 'TILES' in LEAFLET_CONFIG:
59 | raise ImproperlyConfigured(_("Remove TILES_URL and keep TILES value."))
60 | app_settings['TILES'] = [(app_settings['TILES_URL'])]
61 |
62 |
63 | # If TILES is a string, convert to tuple
64 | if isinstance(app_settings.get('TILES'), six.string_types):
65 | app_settings['TILES'] = [(_('Background'), app_settings.get('TILES'), '')]
66 |
67 |
68 | # Verify that scale setting is valid. For backwards-compatibility, interpret 'True' as 'metric'.
69 | SCALE = app_settings.get("SCALE", None)
70 | if SCALE is True:
71 | app_settings["SCALE"] = 'metric'
72 | elif SCALE not in ('metric', 'imperial', 'both', None, False):
73 | raise ImproperlyConfigured("LEAFLET_CONFIG['SCALE'] must be True, False, None, 'metric', 'imperial' or 'both'.")
74 |
75 |
76 | SPATIAL_EXTENT = app_settings.get("SPATIAL_EXTENT")
77 | if SPATIAL_EXTENT is None:
78 | # Deprecate lookup in global Django settings
79 | if hasattr(settings, 'SPATIAL_EXTENT'):
80 | warnings.warn("SPATIAL_EXTENT is deprecated. Use LEAFLET_CONFIG['SPATIAL_EXTENT'] instead.", DeprecationWarning)
81 | SPATIAL_EXTENT = getattr(settings, 'SPATIAL_EXTENT', (-180, -90, 180, 90))
82 | if SPATIAL_EXTENT is not None:
83 | if not isinstance(SPATIAL_EXTENT, (tuple, list)) or len(SPATIAL_EXTENT) != 4:
84 | raise ImproperlyConfigured(_("Spatial extent should be a tuple (minx, miny, maxx, maxy)"))
85 |
86 |
87 | SRID = app_settings.get("SRID")
88 | if SRID is None:
89 | # Deprecate lookup in global Django settings
90 | if hasattr(settings, 'MAP_SRID'):
91 | warnings.warn("MAP_SRID is deprecated. Use LEAFLET_CONFIG['SRID'] instead.", DeprecationWarning)
92 | if hasattr(settings, 'SRID'):
93 | warnings.warn("SRID is deprecated. Use LEAFLET_CONFIG['SRID'] instead.", DeprecationWarning)
94 | SRID = getattr(settings, 'MAP_SRID', getattr(settings, 'SRID', 3857))
95 | if SRID == 3857: # Leaflet's default, do not setup custom projection machinery
96 | SRID = None
97 |
98 |
99 | TILES_EXTENT = app_settings.get("TILES_EXTENT")
100 | # Due to bug in Leaflet/Proj4Leaflet ()
101 | # landscape extents are not supported.
102 | if SRID and TILES_EXTENT and (TILES_EXTENT[2] - TILES_EXTENT[0] > TILES_EXTENT[3] - TILES_EXTENT[1]):
103 | raise ImproperlyConfigured('Landscape tiles extent not supported (%s).' % (TILES_EXTENT,))
104 |
105 |
106 | DEFAULT_CENTER = app_settings['DEFAULT_CENTER']
107 | if DEFAULT_CENTER is not None and not (isinstance(DEFAULT_CENTER, (list, tuple)) and len(DEFAULT_CENTER) == 2):
108 | raise ImproperlyConfigured("LEAFLET_CONFIG['DEFAULT_CENTER'] must be an list/tuple with two elements - (lon, lat)")
109 |
110 |
111 | DEFAULT_ZOOM = app_settings['DEFAULT_ZOOM']
112 | if DEFAULT_ZOOM is not None and not (isinstance(DEFAULT_ZOOM, six.integer_types) and (1 <= DEFAULT_ZOOM <= 24)):
113 | raise ImproperlyConfigured("LEAFLET_CONFIG['DEFAULT_ZOOM'] must be an int between 1 and 24.")
114 |
115 |
116 | PLUGINS = app_settings['PLUGINS']
117 | if not (isinstance(PLUGINS, dict) and all([isinstance(el, dict) for el in PLUGINS.values()])):
118 | error_msg = """LEAFLET_CONFIG['PLUGINS'] must be dict of dicts in the format:
119 | { '[plugin_name]': { 'js': '[path-to-js]', 'css': '[path-to-css]' } } .)"""
120 | raise ImproperlyConfigured(error_msg)
121 |
122 | PLUGIN_ALL = 'ALL'
123 | PLUGINS_DEFAULT = '__default__'
124 | PLUGIN_FORMS = 'forms'
125 |
126 | # Add plugins required for forms (not auto-included)
127 | # Assets will be preprended to any existing entry in PLUGINS['forms']
128 | _forms_js = ['leaflet/draw/leaflet.draw.js',
129 | 'leaflet/leaflet.extras.js',
130 | 'leaflet/leaflet.forms.js']
131 | if SRID:
132 | _forms_js += ['leaflet/proj4js.js',
133 | 'leaflet/proj4leaflet.js',
134 | 'proj4js/%s.js' % SRID]
135 |
136 | _forms_css = ['leaflet/draw/leaflet.draw.css']
137 | _forms_plugins = PLUGINS.setdefault(PLUGIN_FORMS, {})
138 | _forms_plugins['js'] = _forms_js + _forms_plugins.get('js', [])
139 | _forms_plugins['css'] = _forms_css + _forms_plugins.get('css', [])
140 | _forms_plugins.setdefault('auto-include', False)
141 | PLUGINS[PLUGIN_FORMS] = _forms_plugins
142 |
143 | # Take advantage of plugin system for Leaflet.MiniMap
144 | if app_settings.get('MINIMAP'):
145 | PLUGINS['minimap'] = {
146 | 'css': 'leaflet/Control.MiniMap.css',
147 | 'js': 'leaflet/Control.MiniMap.js',
148 | 'auto-include': True
149 | }
150 |
151 |
152 | def _normalize_plugins_config():
153 | """
154 | Normalizes the PLUGINS setting:
155 | * ensures the 'css' and 'js' are arrays of URLs
156 | * ensures all URLs are transformed as follows:
157 | ** if the URL is absolute - leave it as-is
158 | ** if the URL is a root URL - starts with a / - leave it as-is
159 | ** the the URL is not a root URL - does not start with / - prepend settings.STATIC_URL
160 | Also, adds a special key - ALL - that includes 'css' and 'js' for all plugins listed
161 | """
162 | if '__is_normalized__' in PLUGINS: # already normalized
163 | return
164 |
165 | listed_plugins = list(PLUGINS.keys())
166 | PLUGINS[PLUGINS_DEFAULT] = OrderedDict()
167 | PLUGINS[PLUGIN_ALL] = OrderedDict()
168 |
169 | RESOURCE_TYPE_KEYS = ['css', 'js']
170 |
171 | for key in listed_plugins:
172 | plugin_dict = PLUGINS[key]
173 |
174 | for resource_type in RESOURCE_TYPE_KEYS:
175 | # normalize the resource URLs
176 | urls = plugin_dict.get(resource_type, None)
177 | if isinstance(urls, (six.binary_type, six.string_types)):
178 | urls = [urls]
179 | elif isinstance(urls, tuple): # force to list
180 | urls = list(urls)
181 | elif isinstance(urls, list): # already a list
182 | pass
183 | elif isinstance(urls, ListWithLazyItems):
184 | # prevent evaluating Promises too early
185 | urls = ListWithLazyItemsRawIterator(urls)
186 | else: # css/js has not been specified or the wrong type
187 | urls = []
188 |
189 | # normalize the URLs - see the docstring for details
190 | for i, url in enumerate(urls):
191 | if ListWithLazyItems.is_lazy_item(url):
192 | # If it is a Promise, then we have already
193 | # seen this url and have lazily applied the `static` call
194 | # to it, so we can safely skip the check below.
195 | continue
196 | url_parts = urlparse(url)
197 | if url_parts.scheme or url_parts.path.startswith('/'):
198 | # absolute URL or a URL starting at root
199 | pass
200 | else:
201 | # pass relative URL through django.contrib.staticfiles
202 | urls[i] = memoized_lazy_function(static, url) # lazy variant of `static(url)`
203 |
204 | urls = ListWithLazyItems(urls)
205 | plugin_dict[resource_type] = urls
206 |
207 | # Append it to the DEFAULT pseudo-plugin if auto-include
208 | if plugin_dict.get('auto-include', False):
209 | PLUGINS[PLUGINS_DEFAULT].setdefault(resource_type, ListWithLazyItems()).extend(urls)
210 |
211 | # also append it to the ALL pseudo-plugin;
212 | PLUGINS[PLUGIN_ALL].setdefault(resource_type, ListWithLazyItems()).extend(urls)
213 |
214 | PLUGINS['__is_normalized__'] = True
215 |
216 |
217 | default_app_config = 'leaflet.apps.LeafletConfig'
218 |
219 | if django.VERSION >= (1, 8, 0): # otherwise is called in apps.py
220 | if django.apps.apps.ready:
221 | _normalize_plugins_config()
222 | else:
223 | _normalize_plugins_config()
224 |
225 |
226 | class JSONLazyTranslationEncoder(DjangoJSONEncoder):
227 | def default(self, obj):
228 | if isinstance(obj, Promise):
229 | return force_text(obj)
230 | return super(JSONLazyTranslationEncoder, self).default(obj)
231 |
--------------------------------------------------------------------------------
/leaflet/static/leaflet/leaflet.forms.js:
--------------------------------------------------------------------------------
1 | L.FieldStore = L.Class.extend({
2 | initialize: function (fieldid, options) {
3 | this.formfield = document.getElementById(fieldid);
4 | L.setOptions(this, options);
5 | },
6 |
7 | load: function () {
8 | var value = (this.formfield.value || '');
9 | return this._deserialize(value);
10 | },
11 |
12 | save: function (layer) {
13 | this.formfield.value = this._serialize(layer);
14 | },
15 |
16 | _serialize: function (layer) {
17 | var items = typeof(layer.getLayers) == 'function' ? layer.getLayers() : [layer],
18 | is_multi = this.options.is_collection || items.length > 1,
19 | is_generic = this.options.is_generic,
20 | collection_type = this.options.collection_type,
21 | is_empty = items.length === 0;
22 |
23 | if (is_empty)
24 | return '';
25 |
26 | var geom = is_multi ? layer : items[0];
27 | if (typeof geom.toGeoJSON != 'function') {
28 | throw 'Unsupported layer type ' + geom.constructor.name;
29 | }
30 |
31 | // Leaflet requires access to original feature attribute for GeoJSON
32 | // serialization. (see https://github.com/Leaflet/Leaflet/blob/v0.7.3/src/layer/GeoJSON.js#L256-L258)
33 | // When creating new records, it's empty so we force it here.
34 | if (!geom.feature) {
35 | geom.feature = {geometry: {type: this.options.geom_type}};
36 | }
37 |
38 | var geojson = geom.toGeoJSON();
39 | var is_geometrycollection = (geojson.geometry && geojson.geometry.type == 'GeometryCollection');
40 | if (is_multi && is_generic && !is_geometrycollection) {
41 | var flat = {type: 'GeometryCollection', geometries: []};
42 | for (var i=0; i < geojson.features.length; i++) {
43 | flat.geometries.push(geojson.features[i].geometry);
44 | }
45 | geojson = flat;
46 | }
47 | // Special case for MultiPolyline/MultiPolygon because it was removed from leaflet 1.0
48 | else if (is_multi && collection_type != 'featureGroup') {
49 | var latlngs = [];
50 | for (var i = 0; i < geojson.features.length; i++) {
51 | var latlng = [];
52 | var coord = geojson.features[i].geometry.coordinates;
53 | if (collection_type == 'polygon') {
54 | coord = coord[0];
55 | }
56 | for (var j = 0; j < coord.length; j++) {
57 | latlng.push([coord[j][1], coord[j][0]]);
58 | }
59 | if (collection_type == 'polygon') {
60 | latlng = [latlng];
61 | }
62 | latlngs.push(latlng);
63 | }
64 | geom = L[collection_type](latlngs);
65 | geojson = geom.toGeoJSON().geometry;
66 | }
67 | // In order to make multipoint work, it seems we need to treat it similarly to the GeometryCollections
68 | else if (this.options.geom_type == 'MULTIPOINT') {
69 | var flat = {type: 'MultiPoint', coordinates: []};
70 | for (var i=0; i < geojson.features.length; i++) {
71 | flat.coordinates.push(geojson.features[i].geometry.coordinates);
72 | }
73 | geojson = flat;
74 | }
75 | else {
76 | geojson = geojson.geometry;
77 | }
78 | return JSON.stringify(geojson);
79 | },
80 |
81 | _deserialize: function (value) {
82 | if (/^\s*$/.test(value)) {
83 | return null;
84 | }
85 | return L.GeoJSON.geometryToLayer(JSON.parse(value));
86 | },
87 | });
88 |
89 |
90 | L.GeometryField = L.Class.extend({
91 | statics: {
92 | unsavedText: 'Map geometry is unsaved'
93 | },
94 |
95 | options: {
96 | field_store_class: L.FieldStore
97 | },
98 |
99 | initialize: function (options) {
100 |
101 | var geom_type = options.geom_type.toLowerCase();
102 | options.is_generic = /geometry/.test(geom_type);
103 | options.is_collection = /(^multi|collection$)/.test(geom_type);
104 | options.is_linestring = /linestring$/.test(geom_type) || options.is_generic;
105 | options.is_polygon = /polygon$/.test(geom_type) || options.is_generic;
106 | options.is_point = /point$/.test(geom_type) || options.is_generic;
107 | options.collection_type = ({
108 | 'multilinestring': 'polyline',
109 | 'multipolygon': 'polygon',
110 | })[geom_type] || 'featureGroup';
111 |
112 | L.setOptions(this, options);
113 |
114 | this._drawControl = null;
115 | this._unsavedChanges = false;
116 |
117 | // Warn if leaving with unsaved changes
118 | var _beforeunload = window.onbeforeunload;
119 | window.onbeforeunload = L.Util.bind(function(e) {
120 | if (this._unsavedChanges)
121 | return L.GeometryField.unsavedText;
122 | if (typeof(_beforeunload) == 'function')
123 | return _beforeunload();
124 | }, this);
125 | },
126 |
127 | addTo: function (map) {
128 | this._map = map;
129 |
130 | var store_opts = L.Util.extend(this.options, {defaults: map.defaults});
131 | this.store = new this.options.field_store_class(this.options.fieldid, store_opts);
132 |
133 | this.drawnItems = this._editionLayer();
134 | map.addLayer(this.drawnItems);
135 |
136 | // Initialize the draw control and pass it the FeatureGroup of editable layers
137 | var drawControl = this._drawControl = new L.Control.Draw(this._controlDrawOptions());
138 |
139 | if (this.options.modifiable) {
140 | map.addControl(drawControl);
141 | L.DomUtil.addClass(drawControl._container, this.options.fieldid);
142 |
143 | //
144 | // In case there is several draw controls on the same map (target map option)
145 | map['drawControl' + this.options.fieldid] = drawControl;
146 | // We use a flag to ignore events of other draw controls
147 | for (var toolbar in drawControl._toolbars) {
148 | drawControl._toolbars[toolbar].on('enable disable', function (e) {
149 | this._acceptDrawEvents = e.type === 'enable';
150 | }, this);
151 | }
152 |
153 | map.on('draw:created draw:edited draw:deleted', function (e) {
154 | // Ignore if coming from other Draw controls
155 | if (!this._acceptDrawEvents)
156 | return;
157 | // Call onCreated(), onEdited(), onDeleted()
158 | var eventName = e.type.replace('draw:', ''),
159 | method = 'on' + eventName.charAt(0).toUpperCase() + eventName.slice(1);
160 | this[method](e);
161 | }, this);
162 |
163 | // Flag for unsaved changes
164 | map.on('draw:drawstart draw:editstart', function () {
165 | if (this._acceptDrawEvents) this._unsavedChanges = true;
166 | }, this);
167 | map.on('draw:drawstop draw:editstop', function () {
168 | if (this._acceptDrawEvents) this._unsavedChanges = false;
169 | }, this);
170 | }
171 |
172 | this.load();
173 |
174 | map.fire('map:loadfield', {field: this, fieldid: this.options.fieldid});
175 |
176 | return this;
177 | },
178 |
179 | load: function () {
180 | var geometry = this.store.load();
181 | if (geometry) {
182 | // Add initial geometry to the map
183 | if (geometry instanceof L.LayerGroup) {
184 | geometry.eachLayer(function (l) {
185 | this.drawnItems.addLayer(l);
186 | }, this);
187 | }
188 | else if (this.options.collection_type !== 'featureGroup'
189 | && (geometry instanceof L.Polygon || geometry instanceof L.Polyline)) {
190 | var latlngs = geometry.getLatLngs();
191 | for (var i = 0; i < latlngs.length; i++) {
192 | this.drawnItems.addLayer(L[this.options.collection_type](latlngs[i]));
193 | }
194 | }
195 | else {
196 | this.drawnItems.addLayer(geometry);
197 | }
198 | this.drawnItems.addTo(this._map);
199 | }
200 | this._setView();
201 | return geometry;
202 | },
203 |
204 | _setView: function () {
205 | // Change view extent
206 | if (this.drawnItems.getLayers().length > 0) {
207 | var bounds = this.drawnItems.getBounds();
208 | var options = {
209 | maxZoom: this._map.maxZoom || 15
210 | };
211 | this._map.fitBounds(bounds, options);
212 | }
213 | // Else keep view extent set by django-leaflet template tag
214 | },
215 |
216 | onCreated: function (e) {
217 | // Remove previously drawn if field is not collection.
218 | if (!this.options.is_collection) {
219 | this.drawnItems.eachLayer(function (l) {
220 | this._map.removeLayer(l);
221 | }, this);
222 | this.drawnItems.clearLayers();
223 | }
224 | var layer = e.layer;
225 | this._map.addLayer(layer);
226 | this.drawnItems.addLayer(layer);
227 | this.store.save(this.drawnItems);
228 | },
229 |
230 | onEdited: function (e) {
231 | this.store.save(this.drawnItems);
232 | },
233 |
234 | onDeleted: function (e) {
235 | var layer = e.layer;
236 | this.drawnItems.removeLayer(layer);
237 | this.store.save(this.drawnItems);
238 | },
239 |
240 | _editionLayer: function () {
241 | var type = 'featureGroup',
242 | constructor = L[type];
243 | if (typeof(constructor) != 'function') {
244 | throw 'Unsupported geometry type: ' + type;
245 | }
246 | return constructor([], {});
247 | },
248 |
249 | _controlDrawOptions: function () {
250 | return {
251 | edit: {
252 | featureGroup: this.drawnItems
253 | },
254 | draw: {
255 | polyline: this.options.is_linestring,
256 | polygon: this.options.is_polygon,
257 | circle: false, // Turns off this drawing tool
258 | rectangle: this.options.is_polygon,
259 | marker: this.options.is_point,
260 | }
261 | };
262 | }
263 | });
264 |
--------------------------------------------------------------------------------