├── leaflet ├── models.py ├── forms │ ├── __init__.py │ ├── nogeos.py │ ├── fields.py │ └── widgets.py ├── templatetags │ ├── __init__.py │ └── leaflet_tags.py ├── tests │ ├── __init__.py │ ├── package.json │ ├── index.html │ ├── test.extras.js │ └── test.forms.js ├── locale │ ├── cs │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── de │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── es │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── fr │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── he │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── hu │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── it │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ └── ru │ │ └── LC_MESSAGES │ │ ├── django.mo │ │ └── django.po ├── static │ └── leaflet │ │ ├── images │ │ ├── layers.png │ │ ├── toggle.png │ │ ├── zoom-in.png │ │ ├── layers-2x.png │ │ ├── reset-view.png │ │ ├── zoom-out.png │ │ ├── marker-icon.png │ │ ├── marker-icon-2x.png │ │ ├── marker-icon@2x.png │ │ └── marker-shadow.png │ │ ├── draw │ │ ├── images │ │ │ ├── layers.png │ │ │ ├── layers-2x.png │ │ │ ├── marker-icon.png │ │ │ ├── spritesheet.png │ │ │ ├── marker-icon-2x.png │ │ │ ├── marker-shadow.png │ │ │ ├── spritesheet-2x.png │ │ │ └── spritesheet.svg │ │ ├── leaflet.draw.css │ │ └── leaflet.draw-src.css │ │ ├── Control.MiniMap.css │ │ ├── leaflet.ie.css │ │ ├── eventlistener.ie6-7.js │ │ ├── eventlistener.ie8.js │ │ ├── proj4leaflet.js │ │ ├── leaflet.extras.js │ │ ├── Control.MiniMap.js │ │ └── leaflet.forms.js ├── apps.py ├── templates │ └── leaflet │ │ ├── css.html │ │ ├── admin │ │ └── widget.html │ │ ├── js.html │ │ ├── _leaflet_map.html │ │ ├── widget.html │ │ └── _leaflet_draw_i18n.js ├── admin.py ├── utils.py └── __init__.py ├── example ├── mushrooms │ ├── __init__.py │ ├── migrations │ │ ├── __init__.py │ │ └── 0001_initial.py │ ├── admin.py │ ├── models.py │ ├── wsgi.py │ ├── templates │ │ └── index.html │ ├── urls.py │ └── settings.py ├── requirements.txt ├── README.rst └── manage.py ├── .coveragerc ├── MANIFEST.in ├── docs ├── index.rst ├── installation.rst ├── advanced.rst ├── widget.rst ├── Makefile ├── make.bat ├── templates.rst └── conf.py ├── COPYRIGHT ├── setup.py ├── .gitignore ├── README.rst ├── .travis.yml ├── quicktest.py └── LICENSE /leaflet/models.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /leaflet/forms/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/mushrooms/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /leaflet/templatetags/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | source = leaflet 3 | -------------------------------------------------------------------------------- /example/mushrooms/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /leaflet/tests/__init__.py: -------------------------------------------------------------------------------- 1 | from .tests import * # noqa 2 | -------------------------------------------------------------------------------- /leaflet/locale/cs/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fitoprincipe/django-leaflet/master/leaflet/locale/cs/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /leaflet/locale/de/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fitoprincipe/django-leaflet/master/leaflet/locale/de/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /leaflet/locale/es/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fitoprincipe/django-leaflet/master/leaflet/locale/es/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /leaflet/locale/fr/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fitoprincipe/django-leaflet/master/leaflet/locale/fr/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /leaflet/locale/he/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fitoprincipe/django-leaflet/master/leaflet/locale/he/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /leaflet/locale/hu/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fitoprincipe/django-leaflet/master/leaflet/locale/hu/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /leaflet/locale/it/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fitoprincipe/django-leaflet/master/leaflet/locale/it/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /leaflet/locale/ru/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fitoprincipe/django-leaflet/master/leaflet/locale/ru/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /example/requirements.txt: -------------------------------------------------------------------------------- 1 | Django==1.11.3 2 | django-geojson==2.10.0 3 | django-leaflet==0.22.0 4 | jsonfield==2.0.2 5 | Pillow==4.2.1 6 | six==1.10.0 -------------------------------------------------------------------------------- /leaflet/static/leaflet/images/layers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fitoprincipe/django-leaflet/master/leaflet/static/leaflet/images/layers.png -------------------------------------------------------------------------------- /leaflet/static/leaflet/images/toggle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fitoprincipe/django-leaflet/master/leaflet/static/leaflet/images/toggle.png -------------------------------------------------------------------------------- /leaflet/static/leaflet/images/zoom-in.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fitoprincipe/django-leaflet/master/leaflet/static/leaflet/images/zoom-in.png -------------------------------------------------------------------------------- /leaflet/static/leaflet/images/layers-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fitoprincipe/django-leaflet/master/leaflet/static/leaflet/images/layers-2x.png -------------------------------------------------------------------------------- /leaflet/static/leaflet/images/reset-view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fitoprincipe/django-leaflet/master/leaflet/static/leaflet/images/reset-view.png -------------------------------------------------------------------------------- /leaflet/static/leaflet/images/zoom-out.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fitoprincipe/django-leaflet/master/leaflet/static/leaflet/images/zoom-out.png -------------------------------------------------------------------------------- /leaflet/static/leaflet/draw/images/layers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fitoprincipe/django-leaflet/master/leaflet/static/leaflet/draw/images/layers.png -------------------------------------------------------------------------------- /leaflet/static/leaflet/images/marker-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fitoprincipe/django-leaflet/master/leaflet/static/leaflet/images/marker-icon.png -------------------------------------------------------------------------------- /leaflet/static/leaflet/draw/images/layers-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fitoprincipe/django-leaflet/master/leaflet/static/leaflet/draw/images/layers-2x.png -------------------------------------------------------------------------------- /leaflet/static/leaflet/images/marker-icon-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fitoprincipe/django-leaflet/master/leaflet/static/leaflet/images/marker-icon-2x.png -------------------------------------------------------------------------------- /leaflet/static/leaflet/images/marker-icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fitoprincipe/django-leaflet/master/leaflet/static/leaflet/images/marker-icon@2x.png -------------------------------------------------------------------------------- /leaflet/static/leaflet/images/marker-shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fitoprincipe/django-leaflet/master/leaflet/static/leaflet/images/marker-shadow.png -------------------------------------------------------------------------------- /leaflet/static/leaflet/draw/images/marker-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fitoprincipe/django-leaflet/master/leaflet/static/leaflet/draw/images/marker-icon.png -------------------------------------------------------------------------------- /leaflet/static/leaflet/draw/images/spritesheet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fitoprincipe/django-leaflet/master/leaflet/static/leaflet/draw/images/spritesheet.png -------------------------------------------------------------------------------- /leaflet/static/leaflet/draw/images/marker-icon-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fitoprincipe/django-leaflet/master/leaflet/static/leaflet/draw/images/marker-icon-2x.png -------------------------------------------------------------------------------- /leaflet/static/leaflet/draw/images/marker-shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fitoprincipe/django-leaflet/master/leaflet/static/leaflet/draw/images/marker-shadow.png -------------------------------------------------------------------------------- /leaflet/static/leaflet/draw/images/spritesheet-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fitoprincipe/django-leaflet/master/leaflet/static/leaflet/draw/images/spritesheet-2x.png -------------------------------------------------------------------------------- /example/mushrooms/admin.py: -------------------------------------------------------------------------------- 1 | from leaflet.admin import LeafletGeoAdmin 2 | from django.contrib import admin 3 | 4 | from . import models as mushrooms_models 5 | 6 | 7 | admin.site.register(mushrooms_models.MushroomSpot, LeafletGeoAdmin) 8 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.rst CHANGES LICENSE 2 | recursive-include leaflet/locale *.mo 3 | recursive-include leaflet/templates *.html *.js 4 | recursive-include leaflet/static *.js 5 | recursive-include leaflet/static *.css 6 | recursive-include leaflet/static *.png 7 | recursive-include leaflet/static *.svg 8 | -------------------------------------------------------------------------------- /leaflet/apps.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from django.apps import AppConfig 4 | 5 | 6 | class LeafletConfig(AppConfig): 7 | name = 'leaflet' 8 | verbose_name = "Leaflet" 9 | 10 | def ready(self): 11 | from . import _normalize_plugins_config 12 | _normalize_plugins_config() 13 | -------------------------------------------------------------------------------- /leaflet/tests/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "django-leaflet-tests", 3 | "version": "0.8.0", 4 | "description": "Fake package for tests purposes only", 5 | "dependencies": { 6 | "leaflet": "1.0.3", 7 | "happen": "0.3.x", 8 | "mocha": "4.0.1", 9 | "chai": "4.1.x", 10 | "sinon": "1.7.x", 11 | "mocha-phantomjs": "4.1.x", 12 | "phantomjs-prebuilt": "2.1.x" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /example/mushrooms/models.py: -------------------------------------------------------------------------------- 1 | from djgeojson.fields import PolygonField 2 | from django.db import models 3 | 4 | 5 | class MushroomSpot(models.Model): 6 | 7 | title = models.CharField(max_length=256) 8 | description = models.TextField() 9 | picture = models.ImageField() 10 | geom = PolygonField() 11 | 12 | def __unicode__(self): 13 | return self.title 14 | 15 | @property 16 | def picture_url(self): 17 | return self.picture.url 18 | -------------------------------------------------------------------------------- /example/mushrooms/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for mushrooms project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.10/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mushrooms.settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /leaflet/templates/leaflet/css.html: -------------------------------------------------------------------------------- 1 | {% load static from staticfiles %} 2 | 3 | 6 | 7 | {% if PLUGINS_CSS %} 8 | {% for css in PLUGINS_CSS %} 9 | 10 | {% endfor %} 11 | {% endif %} 12 | -------------------------------------------------------------------------------- /leaflet/forms/nogeos.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | class GEOSGeometry(object): 5 | """ 6 | A simple mock of django.contrib.gis.geos.GEOSGeometry to make 7 | django-leaflet work with only django-geojson (without libgeos) 8 | """ 9 | def __init__(self, geo_input, srid=None): 10 | self.srid = srid 11 | self.geojson = geo_input 12 | return 13 | 14 | 15 | class GEOSException(Exception): 16 | pass 17 | 18 | 19 | class OGRException(Exception): 20 | pass 21 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. Django Leaflet documentation master file, created by 2 | sphinx-quickstart on Tue Feb 28 14:56:43 2017. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to Django Leaflet's documentation! 7 | ========================================== 8 | 9 | Contents: 10 | 11 | .. toctree:: 12 | :maxdepth: 2 13 | 14 | installation 15 | templates 16 | widget 17 | advanced 18 | 19 | 20 | 21 | Indices and tables 22 | ================== 23 | 24 | * :ref:`genindex` 25 | * :ref:`modindex` 26 | * :ref:`search` 27 | 28 | -------------------------------------------------------------------------------- /example/README.rst: -------------------------------------------------------------------------------- 1 | 2 | .. note:: 3 | 4 | This project does **not** require any GIS library or specific database. 5 | Map data are stored in simple JSON fields. 6 | 7 | Install 8 | ======= 9 | 10 | Install Django dependencies: 11 | 12 | .. code-block:: bash 13 | 14 | pip install -r requirements.txt 15 | 16 | Initialize database tables: 17 | 18 | .. code-block:: bash 19 | 20 | python manage.py migrate 21 | 22 | Create a super-user for the admin: 23 | 24 | .. code-block:: bash 25 | 26 | python manage.py createsuperuser 27 | 28 | Run 29 | === 30 | 31 | .. code-block:: bash 32 | 33 | python manage.py runserver 34 | 35 | The map visible on http://127.0.0.1:8000/ can be edited from the AdminSite at ``/admin``. 36 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012-2013 Makina Corpus 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 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 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 |
84 | {{ form }} 85 | 86 |
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 | --------------------------------------------------------------------------------