├── .idea ├── .gitignore ├── geoserver_django_leaflet.iml ├── inspectionProfiles │ └── profiles_settings.xml ├── misc.xml └── modules.xml ├── README.md ├── db.sqlite3 ├── geoserver_django_leaflet ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-37.pyc │ ├── geoserver.cpython-37.pyc │ ├── settings.cpython-37.pyc │ ├── urls.cpython-37.pyc │ └── wsgi.cpython-37.pyc ├── asgi.py ├── geoserver.py ├── settings.py ├── urls.py └── wsgi.py ├── manage.py ├── static_files ├── draw_files │ ├── Control.Draw.js │ ├── Draw.Circle.js │ ├── Draw.CircleMarker.js │ ├── Draw.Feature.js │ ├── Draw.Marker.js │ ├── Draw.Polygon.js │ ├── Draw.Polyline.js │ ├── Draw.Rectangle.js │ ├── Draw.SimpleShape.js │ ├── DrawToolbar.js │ ├── Edit.Circle.js │ ├── Edit.CircleMarker.js │ ├── Edit.Marker.js │ ├── Edit.Poly.js │ ├── Edit.Rectangle.js │ ├── Edit.SimpleShape.js │ ├── EditToolbar.Delete.js │ ├── EditToolbar.Edit.js │ ├── EditToolbar.js │ ├── GeometryUtil.js │ ├── LatLngUtil.js │ ├── Leaflet.Draw.Event.js │ ├── Leaflet.draw.js │ ├── LineUtil.Intersect.js │ ├── Polygon.Intersect.js │ ├── Polyline.Intersect.js │ ├── Toolbar.js │ ├── Tooltip.js │ ├── TouchEvents.js │ ├── jquery-1.7.2.js │ ├── jquery.cookie.js │ ├── leaflet-src.js │ ├── leaflet.css │ ├── leaflet.draw.css │ ├── spritesheet-2x.png │ └── spritesheet.svg ├── jquery-1.7.2.js ├── leaflet-src.js └── leaflet.css └── templates ├── attr_feature.html ├── bbox_feature.html ├── draw.html ├── remove.html └── tile_wmts.html /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/geoserver_django_leaflet.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # geoserver_django_leaflet 2 | 地图服务用GeoServer发布,Django做后端转发服务请求,leaflet做前端渲染。 3 | 项目的结构和具体说明,可参见CSDN:https://blog.csdn.net/sinat_41310868/article/details/117999616 4 | -------------------------------------------------------------------------------- /db.sqlite3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yimengyao13/geoserver_django_leaflet/2efa16ab245481391434ffa644658ced04ce07ba/db.sqlite3 -------------------------------------------------------------------------------- /geoserver_django_leaflet/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yimengyao13/geoserver_django_leaflet/2efa16ab245481391434ffa644658ced04ce07ba/geoserver_django_leaflet/__init__.py -------------------------------------------------------------------------------- /geoserver_django_leaflet/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yimengyao13/geoserver_django_leaflet/2efa16ab245481391434ffa644658ced04ce07ba/geoserver_django_leaflet/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /geoserver_django_leaflet/__pycache__/geoserver.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yimengyao13/geoserver_django_leaflet/2efa16ab245481391434ffa644658ced04ce07ba/geoserver_django_leaflet/__pycache__/geoserver.cpython-37.pyc -------------------------------------------------------------------------------- /geoserver_django_leaflet/__pycache__/settings.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yimengyao13/geoserver_django_leaflet/2efa16ab245481391434ffa644658ced04ce07ba/geoserver_django_leaflet/__pycache__/settings.cpython-37.pyc -------------------------------------------------------------------------------- /geoserver_django_leaflet/__pycache__/urls.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yimengyao13/geoserver_django_leaflet/2efa16ab245481391434ffa644658ced04ce07ba/geoserver_django_leaflet/__pycache__/urls.cpython-37.pyc -------------------------------------------------------------------------------- /geoserver_django_leaflet/__pycache__/wsgi.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yimengyao13/geoserver_django_leaflet/2efa16ab245481391434ffa644658ced04ce07ba/geoserver_django_leaflet/__pycache__/wsgi.cpython-37.pyc -------------------------------------------------------------------------------- /geoserver_django_leaflet/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for geoserver_django_leaflet project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.0/howto/deployment/asgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.asgi import get_asgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'geoserver_django_leaflet.settings') 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /geoserver_django_leaflet/geoserver.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.http import HttpResponse 3 | from django.shortcuts import render 4 | import requests 5 | base_url = 'http://localhost:8080/' 6 | # 获取地图瓦片服务 7 | def wmts(request): 8 | print(request) 9 | url = base_url+'geoserver/fangjia/wms?service=WMS&request=GetMap&layers='+request.GET['layers']+\ 10 | '&styles=&format='+request.GET['format']+'&transparent='+request.GET['transparent']+\ 11 | '&version='+request.GET['version']+'&width='+request.GET['width']+'&height='+request.GET['height']+\ 12 | '&srs='+request.GET['srs']+'&bbox='+request.GET['bbox'] 13 | if 'CQL_FILTER' in request.GET: 14 | url = url + '&CQL_FILTER=' + request.GET['CQL_FILTER'] 15 | print(url) 16 | image_data = requests.get(url=url,stream=True) 17 | return HttpResponse(image_data,content_type='image/png') 18 | # 访问tile_wmts页面 19 | def tile_wmts(request): 20 | return render(request,'tile_wmts.html') 21 | # 根据查询条件,获取要素服务 22 | def getfeature(request): 23 | url = base_url + 'geoserver/wfs?request=GetFeature&version=1.1.0&typeName='+request.GET['typeName']+'&outputFormat=application%2Fjson' 24 | if 'BBOX' in request.GET: 25 | url = url + '&BBOX=' + request.GET['BBOX'] 26 | if 'CQL_FILTER' in request.GET: 27 | url = url + '&CQL_FILTER=' + request.GET['CQL_FILTER'] 28 | print(url) 29 | json_data = requests.get(url=url) 30 | return HttpResponse(json_data,content_type='application/json') 31 | # 访问bbox_feature页面 32 | def bbox_feature(request): 33 | return render(request,'bbox_feature.html') 34 | # 访问attr_feature页面 35 | def attr_feature(request): 36 | return render(request,'attr_feature.html') 37 | # 渲染draw页面 38 | def draw(request): 39 | return render(request, 'draw.html') 40 | # 渲染delete页面 41 | def remove(request): 42 | return render(request,'remove.html') 43 | 44 | # 提交 gml到geoserver 45 | def postgml(request): 46 | if request.POST: 47 | head = {"Content-Type": "text/xml; charset=UTF-8", "Connection": "close"} 48 | r = requests.post(base_url+'geoserver/wfs', data=request.POST['gml'].encode('gbk'), headers=head) 49 | return HttpResponse(r.text) 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /geoserver_django_leaflet/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for geoserver_django_leaflet project. 3 | 4 | Generated by 'django-admin startproject' using Django 3.0.6. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.0/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/3.0/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/3.0/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = 'ea(72ymwq6fb0(6%ab&c*ots)2=%e8ur1q)tfl8$0ejo^o()f=' 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 | 42 | MIDDLEWARE = [ 43 | 'django.middleware.security.SecurityMiddleware', 44 | 'django.contrib.sessions.middleware.SessionMiddleware', 45 | 'django.middleware.common.CommonMiddleware', 46 | 'django.middleware.csrf.CsrfViewMiddleware', 47 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 48 | 'django.contrib.messages.middleware.MessageMiddleware', 49 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 50 | ] 51 | 52 | ROOT_URLCONF = 'geoserver_django_leaflet.urls' 53 | 54 | TEMPLATES = [ 55 | { 56 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 57 | 'DIRS': [os.path.join(BASE_DIR, 'templates')], 58 | 'APP_DIRS': True, 59 | 'OPTIONS': { 60 | 'context_processors': [ 61 | 'django.template.context_processors.debug', 62 | 'django.template.context_processors.request', 63 | 'django.contrib.auth.context_processors.auth', 64 | 'django.contrib.messages.context_processors.messages', 65 | ], 66 | }, 67 | }, 68 | ] 69 | 70 | WSGI_APPLICATION = 'geoserver_django_leaflet.wsgi.application' 71 | 72 | 73 | # Database 74 | # https://docs.djangoproject.com/en/3.0/ref/settings/#databases 75 | 76 | DATABASES = { 77 | 'default': { 78 | 'ENGINE': 'django.db.backends.sqlite3', 79 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 80 | } 81 | } 82 | 83 | 84 | # Password validation 85 | # https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators 86 | 87 | AUTH_PASSWORD_VALIDATORS = [ 88 | { 89 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 90 | }, 91 | { 92 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 93 | }, 94 | { 95 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 96 | }, 97 | { 98 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 99 | }, 100 | ] 101 | 102 | 103 | # Internationalization 104 | # https://docs.djangoproject.com/en/3.0/topics/i18n/ 105 | 106 | LANGUAGE_CODE = 'en-us' 107 | 108 | TIME_ZONE = 'UTC' 109 | 110 | USE_I18N = True 111 | 112 | USE_L10N = True 113 | 114 | USE_TZ = True 115 | 116 | 117 | # Static files (CSS, JavaScript, Images) 118 | # https://docs.djangoproject.com/en/3.0/howto/static-files/ 119 | 120 | STATIC_URL = '/static/' 121 | STATICFILES_DIRS = [ 122 | os.path.join(BASE_DIR, "static_files"), 123 | ] 124 | -------------------------------------------------------------------------------- /geoserver_django_leaflet/urls.py: -------------------------------------------------------------------------------- 1 | """geoserver_django_leaflet URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/3.0/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: path('', 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: path('', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.urls import include, path 14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 | """ 16 | from django.contrib import admin 17 | from django.urls import path 18 | from . import geoserver 19 | from django.conf.urls import url 20 | urlpatterns = [ 21 | url(r'^getwmts$', geoserver.wmts), 22 | url(r'^tile_wmts',geoserver.tile_wmts), 23 | url(r'^feature', geoserver.getfeature), 24 | url(r'^bbox_feature',geoserver.bbox_feature), 25 | url(r'^attr_feature', geoserver.attr_feature), 26 | url(r'^postgml',geoserver.postgml), 27 | url(r'^draw',geoserver.draw), 28 | url(r'^remove',geoserver.remove), 29 | ] 30 | -------------------------------------------------------------------------------- /geoserver_django_leaflet/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for geoserver_django_leaflet 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/3.0/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', 'geoserver_django_leaflet.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'geoserver_django_leaflet.settings') 9 | try: 10 | from django.core.management import execute_from_command_line 11 | except ImportError as exc: 12 | raise ImportError( 13 | "Couldn't import Django. Are you sure it's installed and " 14 | "available on your PYTHONPATH environment variable? Did you " 15 | "forget to activate a virtual environment?" 16 | ) from exc 17 | execute_from_command_line(sys.argv) 18 | 19 | 20 | if __name__ == '__main__': 21 | main() 22 | -------------------------------------------------------------------------------- /static_files/draw_files/Control.Draw.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @class L.Control.Draw 3 | * @aka L.Draw 4 | */ 5 | L.Control.Draw = L.Control.extend({ 6 | 7 | // Options 8 | options: { 9 | position: 'topleft', 10 | draw: {}, 11 | edit: false 12 | }, 13 | 14 | // @method initialize(): void 15 | // Initializes draw control, toolbars from the options 16 | initialize: function (options) { 17 | if (L.version < '0.7') { 18 | throw new Error('Leaflet.draw 0.2.3+ requires Leaflet 0.7.0+. Download latest from https://github.com/Leaflet/Leaflet/'); 19 | } 20 | 21 | L.Control.prototype.initialize.call(this, options); 22 | 23 | var toolbar; 24 | 25 | this._toolbars = {}; 26 | 27 | // Initialize toolbars 28 | if (L.DrawToolbar && this.options.draw) { 29 | toolbar = new L.DrawToolbar(this.options.draw); 30 | 31 | this._toolbars[L.DrawToolbar.TYPE] = toolbar; 32 | 33 | // Listen for when toolbar is enabled 34 | this._toolbars[L.DrawToolbar.TYPE].on('enable', this._toolbarEnabled, this); 35 | } 36 | 37 | if (L.EditToolbar && this.options.edit) { 38 | toolbar = new L.EditToolbar(this.options.edit); 39 | 40 | this._toolbars[L.EditToolbar.TYPE] = toolbar; 41 | 42 | // Listen for when toolbar is enabled 43 | this._toolbars[L.EditToolbar.TYPE].on('enable', this._toolbarEnabled, this); 44 | } 45 | L.toolbar = this; //set global var for editing the toolbar 46 | }, 47 | 48 | // @method onAdd(): container 49 | // Adds the toolbar container to the map 50 | onAdd: function (map) { 51 | var container = L.DomUtil.create('div', 'leaflet-draw'), 52 | addedTopClass = false, 53 | topClassName = 'leaflet-draw-toolbar-top', 54 | toolbarContainer; 55 | 56 | for (var toolbarId in this._toolbars) { 57 | if (this._toolbars.hasOwnProperty(toolbarId)) { 58 | toolbarContainer = this._toolbars[toolbarId].addToolbar(map); 59 | 60 | if (toolbarContainer) { 61 | // Add class to the first toolbar to remove the margin 62 | if (!addedTopClass) { 63 | if (!L.DomUtil.hasClass(toolbarContainer, topClassName)) { 64 | L.DomUtil.addClass(toolbarContainer.childNodes[0], topClassName); 65 | } 66 | addedTopClass = true; 67 | } 68 | 69 | container.appendChild(toolbarContainer); 70 | } 71 | } 72 | } 73 | 74 | return container; 75 | }, 76 | 77 | // @method onRemove(): void 78 | // Removes the toolbars from the map toolbar container 79 | onRemove: function () { 80 | for (var toolbarId in this._toolbars) { 81 | if (this._toolbars.hasOwnProperty(toolbarId)) { 82 | this._toolbars[toolbarId].removeToolbar(); 83 | } 84 | } 85 | }, 86 | 87 | // @method setDrawingOptions(options): void 88 | // Sets options to all toolbar instances 89 | setDrawingOptions: function (options) { 90 | for (var toolbarId in this._toolbars) { 91 | if (this._toolbars[toolbarId] instanceof L.DrawToolbar) { 92 | this._toolbars[toolbarId].setOptions(options); 93 | } 94 | } 95 | }, 96 | 97 | _toolbarEnabled: function (e) { 98 | var enabledToolbar = e.target; 99 | 100 | for (var toolbarId in this._toolbars) { 101 | if (this._toolbars[toolbarId] !== enabledToolbar) { 102 | this._toolbars[toolbarId].disable(); 103 | } 104 | } 105 | } 106 | }); 107 | 108 | L.Map.mergeOptions({ 109 | drawControlTooltips: true, 110 | drawControl: false 111 | }); 112 | 113 | L.Map.addInitHook(function () { 114 | if (this.options.drawControl) { 115 | this.drawControl = new L.Control.Draw(); 116 | this.addControl(this.drawControl); 117 | } 118 | }); 119 | -------------------------------------------------------------------------------- /static_files/draw_files/Draw.Circle.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @class L.Draw.Circle 3 | * @aka Draw.Circle 4 | * @inherits L.Draw.SimpleShape 5 | */ 6 | L.Draw.Circle = L.Draw.SimpleShape.extend({ 7 | statics: { 8 | TYPE: 'circle' 9 | }, 10 | 11 | options: { 12 | shapeOptions: { 13 | stroke: true, 14 | color: '#3388ff', 15 | weight: 4, 16 | opacity: 0.5, 17 | fill: true, 18 | fillColor: null, //same as color by default 19 | fillOpacity: 0.2, 20 | clickable: true 21 | }, 22 | showRadius: true, 23 | metric: true, // Whether to use the metric measurement system or imperial 24 | feet: true, // When not metric, use feet instead of yards for display 25 | nautic: false // When not metric, not feet use nautic mile for display 26 | }, 27 | 28 | // @method initialize(): void 29 | initialize: function (map, options) { 30 | // Save the type so super can fire, need to do this as cannot do this.TYPE :( 31 | this.type = L.Draw.Circle.TYPE; 32 | 33 | this._initialLabelText = L.drawLocal.draw.handlers.circle.tooltip.start; 34 | 35 | L.Draw.SimpleShape.prototype.initialize.call(this, map, options); 36 | }, 37 | 38 | _drawShape: function (latlng) { 39 | // Calculate the distance based on the version 40 | if (L.GeometryUtil.isVersion07x()) { 41 | var distance = this._startLatLng.distanceTo(latlng); 42 | } else { 43 | var distance = this._map.distance(this._startLatLng, latlng); 44 | } 45 | 46 | if (!this._shape) { 47 | this._shape = new L.Circle(this._startLatLng, distance, this.options.shapeOptions); 48 | this._map.addLayer(this._shape); 49 | } else { 50 | this._shape.setRadius(distance); 51 | } 52 | }, 53 | 54 | _fireCreatedEvent: function () { 55 | var circle = new L.Circle(this._startLatLng, this._shape.getRadius(), this.options.shapeOptions); 56 | L.Draw.SimpleShape.prototype._fireCreatedEvent.call(this, circle); 57 | }, 58 | 59 | _onMouseMove: function (e) { 60 | var latlng = e.latlng, 61 | showRadius = this.options.showRadius, 62 | useMetric = this.options.metric, 63 | radius; 64 | 65 | this._tooltip.updatePosition(latlng); 66 | if (this._isDrawing) { 67 | this._drawShape(latlng); 68 | 69 | // Get the new radius (rounded to 1 dp) 70 | radius = this._shape.getRadius().toFixed(1); 71 | 72 | var subtext = ''; 73 | if (showRadius) { 74 | subtext = L.drawLocal.draw.handlers.circle.radius + ': ' + 75 | L.GeometryUtil.readableDistance(radius, useMetric, this.options.feet, this.options.nautic); 76 | } 77 | this._tooltip.updateContent({ 78 | text: this._endLabelText, 79 | subtext: subtext 80 | }); 81 | } 82 | } 83 | }); 84 | -------------------------------------------------------------------------------- /static_files/draw_files/Draw.CircleMarker.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @class L.Draw.CircleMarker 3 | * @aka Draw.CircleMarker 4 | * @inherits L.Draw.Marker 5 | */ 6 | L.Draw.CircleMarker = L.Draw.Marker.extend({ 7 | statics: { 8 | TYPE: 'circlemarker' 9 | }, 10 | 11 | options: { 12 | stroke: true, 13 | color: '#3388ff', 14 | weight: 4, 15 | opacity: 0.5, 16 | fill: true, 17 | fillColor: null, //same as color by default 18 | fillOpacity: 0.2, 19 | clickable: true, 20 | zIndexOffset: 2000 // This should be > than the highest z-index any markers 21 | }, 22 | 23 | // @method initialize(): void 24 | initialize: function (map, options) { 25 | // Save the type so super can fire, need to do this as cannot do this.TYPE :( 26 | this.type = L.Draw.CircleMarker.TYPE; 27 | 28 | this._initialLabelText = L.drawLocal.draw.handlers.circlemarker.tooltip.start; 29 | 30 | L.Draw.Feature.prototype.initialize.call(this, map, options); 31 | }, 32 | 33 | 34 | _fireCreatedEvent: function () { 35 | var circleMarker = new L.CircleMarker(this._marker.getLatLng(), this.options); 36 | L.Draw.Feature.prototype._fireCreatedEvent.call(this, circleMarker); 37 | }, 38 | 39 | _createMarker: function (latlng) { 40 | return new L.CircleMarker(latlng, this.options); 41 | } 42 | }); 43 | -------------------------------------------------------------------------------- /static_files/draw_files/Draw.Feature.js: -------------------------------------------------------------------------------- 1 | L.Draw = L.Draw || {}; 2 | 3 | /** 4 | * @class L.Draw.Feature 5 | * @aka Draw.Feature 6 | */ 7 | L.Draw.Feature = L.Handler.extend({ 8 | 9 | // @method initialize(): void 10 | initialize: function (map, options) { 11 | this._map = map; 12 | this._container = map._container; 13 | this._overlayPane = map._panes.overlayPane; 14 | this._popupPane = map._panes.popupPane; 15 | 16 | // Merge default shapeOptions options with custom shapeOptions 17 | if (options && options.shapeOptions) { 18 | options.shapeOptions = L.Util.extend({}, this.options.shapeOptions, options.shapeOptions); 19 | } 20 | L.setOptions(this, options); 21 | 22 | var version = L.version.split('.'); 23 | //If Version is >= 1.2.0 24 | if (parseInt(version[0], 10) === 1 && parseInt(version[1], 10) >= 2) { 25 | L.Draw.Feature.include(L.Evented.prototype); 26 | } else { 27 | L.Draw.Feature.include(L.Mixin.Events); 28 | } 29 | }, 30 | 31 | // @method enable(): void 32 | // Enables this handler 33 | enable: function () { 34 | if (this._enabled) { 35 | return; 36 | } 37 | 38 | L.Handler.prototype.enable.call(this); 39 | 40 | this.fire('enabled', {handler: this.type}); 41 | 42 | this._map.fire(L.Draw.Event.DRAWSTART, {layerType: this.type}); 43 | }, 44 | 45 | // @method disable(): void 46 | disable: function () { 47 | if (!this._enabled) { 48 | return; 49 | } 50 | 51 | L.Handler.prototype.disable.call(this); 52 | 53 | this._map.fire(L.Draw.Event.DRAWSTOP, {layerType: this.type}); 54 | 55 | this.fire('disabled', {handler: this.type}); 56 | }, 57 | 58 | // @method addHooks(): void 59 | // Add's event listeners to this handler 60 | addHooks: function () { 61 | var map = this._map; 62 | 63 | if (map) { 64 | L.DomUtil.disableTextSelection(); 65 | 66 | map.getContainer().focus(); 67 | 68 | this._tooltip = new L.Draw.Tooltip(this._map); 69 | 70 | L.DomEvent.on(this._container, 'keyup', this._cancelDrawing, this); 71 | } 72 | }, 73 | 74 | // @method removeHooks(): void 75 | // Removes event listeners from this handler 76 | removeHooks: function () { 77 | if (this._map) { 78 | L.DomUtil.enableTextSelection(); 79 | 80 | this._tooltip.dispose(); 81 | this._tooltip = null; 82 | 83 | L.DomEvent.off(this._container, 'keyup', this._cancelDrawing, this); 84 | } 85 | }, 86 | 87 | // @method setOptions(object): void 88 | // Sets new options to this handler 89 | setOptions: function (options) { 90 | L.setOptions(this, options); 91 | }, 92 | 93 | _fireCreatedEvent: function (layer) { 94 | this._map.fire(L.Draw.Event.CREATED, {layer: layer, layerType: this.type}); 95 | }, 96 | 97 | // Cancel drawing when the escape key is pressed 98 | _cancelDrawing: function (e) { 99 | if (e.keyCode === 27) { 100 | this._map.fire('draw:canceled', {layerType: this.type}); 101 | this.disable(); 102 | } 103 | } 104 | }); 105 | -------------------------------------------------------------------------------- /static_files/draw_files/Draw.Marker.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @class L.Draw.Marker 3 | * @aka Draw.Marker 4 | * @inherits L.Draw.Feature 5 | */ 6 | L.Draw.Marker = L.Draw.Feature.extend({ 7 | statics: { 8 | TYPE: 'marker' 9 | }, 10 | 11 | options: { 12 | icon: new L.Icon.Default(), 13 | repeatMode: false, 14 | zIndexOffset: 2000 // This should be > than the highest z-index any markers 15 | }, 16 | 17 | // @method initialize(): void 18 | initialize: function (map, options) { 19 | // Save the type so super can fire, need to do this as cannot do this.TYPE :( 20 | this.type = L.Draw.Marker.TYPE; 21 | 22 | this._initialLabelText = L.drawLocal.draw.handlers.marker.tooltip.start; 23 | 24 | L.Draw.Feature.prototype.initialize.call(this, map, options); 25 | }, 26 | 27 | // @method addHooks(): void 28 | // Add listener hooks to this handler. 29 | addHooks: function () { 30 | L.Draw.Feature.prototype.addHooks.call(this); 31 | 32 | if (this._map) { 33 | this._tooltip.updateContent({text: this._initialLabelText}); 34 | 35 | // Same mouseMarker as in Draw.Polyline 36 | if (!this._mouseMarker) { 37 | this._mouseMarker = L.marker(this._map.getCenter(), { 38 | icon: L.divIcon({ 39 | className: 'leaflet-mouse-marker', 40 | iconAnchor: [20, 20], 41 | iconSize: [40, 40] 42 | }), 43 | opacity: 0, 44 | zIndexOffset: this.options.zIndexOffset 45 | }); 46 | } 47 | 48 | this._mouseMarker 49 | .on('click', this._onClick, this) 50 | .addTo(this._map); 51 | 52 | this._map.on('mousemove', this._onMouseMove, this); 53 | this._map.on('click', this._onTouch, this); 54 | } 55 | }, 56 | 57 | // @method removeHooks(): void 58 | // Remove listener hooks from this handler. 59 | removeHooks: function () { 60 | L.Draw.Feature.prototype.removeHooks.call(this); 61 | 62 | if (this._map) { 63 | this._map 64 | .off('click', this._onClick, this) 65 | .off('click', this._onTouch, this); 66 | if (this._marker) { 67 | this._marker.off('click', this._onClick, this); 68 | this._map 69 | .removeLayer(this._marker); 70 | delete this._marker; 71 | } 72 | 73 | this._mouseMarker.off('click', this._onClick, this); 74 | this._map.removeLayer(this._mouseMarker); 75 | delete this._mouseMarker; 76 | 77 | this._map.off('mousemove', this._onMouseMove, this); 78 | } 79 | }, 80 | 81 | _onMouseMove: function (e) { 82 | var latlng = e.latlng; 83 | 84 | this._tooltip.updatePosition(latlng); 85 | this._mouseMarker.setLatLng(latlng); 86 | 87 | if (!this._marker) { 88 | this._marker = this._createMarker(latlng); 89 | // Bind to both marker and map to make sure we get the click event. 90 | this._marker.on('click', this._onClick, this); 91 | this._map 92 | .on('click', this._onClick, this) 93 | .addLayer(this._marker); 94 | } 95 | else { 96 | latlng = this._mouseMarker.getLatLng(); 97 | this._marker.setLatLng(latlng); 98 | } 99 | }, 100 | 101 | _createMarker: function (latlng) { 102 | return new L.Marker(latlng, { 103 | icon: this.options.icon, 104 | zIndexOffset: this.options.zIndexOffset 105 | }); 106 | }, 107 | 108 | _onClick: function () { 109 | this._fireCreatedEvent(); 110 | 111 | this.disable(); 112 | if (this.options.repeatMode) { 113 | this.enable(); 114 | } 115 | }, 116 | 117 | _onTouch: function (e) { 118 | // called on click & tap, only really does any thing on tap 119 | this._onMouseMove(e); // creates & places marker 120 | this._onClick(); // permanently places marker & ends interaction 121 | }, 122 | 123 | _fireCreatedEvent: function () { 124 | var marker = new L.Marker.Touch(this._marker.getLatLng(), {icon: this.options.icon}); 125 | L.Draw.Feature.prototype._fireCreatedEvent.call(this, marker); 126 | } 127 | }); 128 | -------------------------------------------------------------------------------- /static_files/draw_files/Draw.Polygon.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @class L.Draw.Polygon 3 | * @aka Draw.Polygon 4 | * @inherits L.Draw.Polyline 5 | */ 6 | L.Draw.Polygon = L.Draw.Polyline.extend({ 7 | statics: { 8 | TYPE: 'polygon' 9 | }, 10 | 11 | Poly: L.Polygon, 12 | 13 | options: { 14 | showArea: false, 15 | showLength: false, 16 | shapeOptions: { 17 | stroke: true, 18 | color: '#3388ff', 19 | weight: 4, 20 | opacity: 0.5, 21 | fill: true, 22 | fillColor: null, //same as color by default 23 | fillOpacity: 0.2, 24 | clickable: true 25 | }, 26 | // Whether to use the metric measurement system (truthy) or not (falsy). 27 | // Also defines the units to use for the metric system as an array of 28 | // strings (e.g. `['ha', 'm']`). 29 | metric: true, 30 | feet: true, // When not metric, to use feet instead of yards for display. 31 | nautic: false, // When not metric, not feet use nautic mile for display 32 | // Defines the precision for each type of unit (e.g. {km: 2, ft: 0} 33 | precision: {} 34 | }, 35 | 36 | // @method initialize(): void 37 | initialize: function (map, options) { 38 | L.Draw.Polyline.prototype.initialize.call(this, map, options); 39 | 40 | // Save the type so super can fire, need to do this as cannot do this.TYPE :( 41 | this.type = L.Draw.Polygon.TYPE; 42 | }, 43 | 44 | _updateFinishHandler: function () { 45 | var markerCount = this._markers.length; 46 | 47 | // The first marker should have a click handler to close the polygon 48 | if (markerCount === 1) { 49 | this._markers[0].on('click', this._finishShape, this); 50 | } 51 | 52 | // Add and update the double click handler 53 | if (markerCount > 2) { 54 | this._markers[markerCount - 1].on('dblclick', this._finishShape, this); 55 | // Only need to remove handler if has been added before 56 | if (markerCount > 3) { 57 | this._markers[markerCount - 2].off('dblclick', this._finishShape, this); 58 | } 59 | } 60 | }, 61 | 62 | _getTooltipText: function () { 63 | var text, subtext; 64 | 65 | if (this._markers.length === 0) { 66 | text = L.drawLocal.draw.handlers.polygon.tooltip.start; 67 | } else if (this._markers.length < 3) { 68 | text = L.drawLocal.draw.handlers.polygon.tooltip.cont; 69 | subtext = this._getMeasurementString(); 70 | } else { 71 | text = L.drawLocal.draw.handlers.polygon.tooltip.end; 72 | subtext = this._getMeasurementString(); 73 | } 74 | 75 | return { 76 | text: text, 77 | subtext: subtext 78 | }; 79 | }, 80 | 81 | _getMeasurementString: function () { 82 | var area = this._area, 83 | measurementString = ''; 84 | 85 | 86 | if (!area && !this.options.showLength) { 87 | return null; 88 | } 89 | 90 | if (this.options.showLength) { 91 | measurementString = L.Draw.Polyline.prototype._getMeasurementString.call(this); 92 | } 93 | 94 | if (area) { 95 | measurementString += '
' + L.GeometryUtil.readableArea(area, this.options.metric, this.options.precision); 96 | } 97 | 98 | return measurementString; 99 | }, 100 | 101 | _shapeIsValid: function () { 102 | return this._markers.length >= 3; 103 | }, 104 | 105 | _vertexChanged: function (latlng, added) { 106 | var latLngs; 107 | 108 | // Check to see if we should show the area 109 | if (!this.options.allowIntersection && this.options.showArea) { 110 | latLngs = this._poly.getLatLngs(); 111 | 112 | this._area = L.GeometryUtil.geodesicArea(latLngs); 113 | } 114 | 115 | L.Draw.Polyline.prototype._vertexChanged.call(this, latlng, added); 116 | }, 117 | 118 | _cleanUpShape: function () { 119 | var markerCount = this._markers.length; 120 | 121 | if (markerCount > 0) { 122 | this._markers[0].off('click', this._finishShape, this); 123 | 124 | if (markerCount > 2) { 125 | this._markers[markerCount - 1].off('dblclick', this._finishShape, this); 126 | } 127 | } 128 | } 129 | }); 130 | -------------------------------------------------------------------------------- /static_files/draw_files/Draw.Polyline.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @class L.Draw.Polyline 3 | * @aka Draw.Polyline 4 | * @inherits L.Draw.Feature 5 | */ 6 | L.Draw.Polyline = L.Draw.Feature.extend({ 7 | statics: { 8 | TYPE: 'polyline' 9 | }, 10 | 11 | Poly: L.Polyline, 12 | 13 | options: { 14 | allowIntersection: true, 15 | repeatMode: false, 16 | drawError: { 17 | color: '#b00b00', 18 | timeout: 2500 19 | }, 20 | icon: new L.DivIcon({ 21 | iconSize: new L.Point(8, 8), 22 | className: 'leaflet-div-icon leaflet-editing-icon' 23 | }), 24 | touchIcon: new L.DivIcon({ 25 | iconSize: new L.Point(20, 20), 26 | className: 'leaflet-div-icon leaflet-editing-icon leaflet-touch-icon' 27 | }), 28 | guidelineDistance: 20, 29 | maxGuideLineLength: 4000, 30 | shapeOptions: { 31 | stroke: true, 32 | color: '#3388ff', 33 | weight: 4, 34 | opacity: 0.5, 35 | fill: false, 36 | clickable: true 37 | }, 38 | metric: true, // Whether to use the metric measurement system or imperial 39 | feet: true, // When not metric, to use feet instead of yards for display. 40 | nautic: false, // When not metric, not feet use nautic mile for display 41 | showLength: true, // Whether to display distance in the tooltip 42 | zIndexOffset: 2000, // This should be > than the highest z-index any map layers 43 | factor: 1, // To change distance calculation 44 | maxPoints: 0 // Once this number of points are placed, finish shape 45 | }, 46 | 47 | // @method initialize(): void 48 | initialize: function (map, options) { 49 | // if touch, switch to touch icon 50 | if (L.Browser.touch) { 51 | this.options.icon = this.options.touchIcon; 52 | } 53 | 54 | // Need to set this here to ensure the correct message is used. 55 | this.options.drawError.message = L.drawLocal.draw.handlers.polyline.error; 56 | 57 | // Merge default drawError options with custom options 58 | if (options && options.drawError) { 59 | options.drawError = L.Util.extend({}, this.options.drawError, options.drawError); 60 | } 61 | 62 | // Save the type so super can fire, need to do this as cannot do this.TYPE :( 63 | this.type = L.Draw.Polyline.TYPE; 64 | 65 | L.Draw.Feature.prototype.initialize.call(this, map, options); 66 | }, 67 | 68 | // @method addHooks(): void 69 | // Add listener hooks to this handler 70 | addHooks: function () { 71 | L.Draw.Feature.prototype.addHooks.call(this); 72 | if (this._map) { 73 | this._markers = []; 74 | 75 | this._markerGroup = new L.LayerGroup(); 76 | this._map.addLayer(this._markerGroup); 77 | 78 | this._poly = new L.Polyline([], this.options.shapeOptions); 79 | 80 | this._tooltip.updateContent(this._getTooltipText()); 81 | 82 | // Make a transparent marker that will used to catch click events. These click 83 | // events will create the vertices. We need to do this so we can ensure that 84 | // we can create vertices over other map layers (markers, vector layers). We 85 | // also do not want to trigger any click handlers of objects we are clicking on 86 | // while drawing. 87 | if (!this._mouseMarker) { 88 | this._mouseMarker = L.marker(this._map.getCenter(), { 89 | icon: L.divIcon({ 90 | className: 'leaflet-mouse-marker', 91 | iconAnchor: [20, 20], 92 | iconSize: [40, 40] 93 | }), 94 | opacity: 0, 95 | zIndexOffset: this.options.zIndexOffset 96 | }); 97 | } 98 | 99 | this._mouseMarker 100 | .on('mouseout', this._onMouseOut, this) 101 | .on('mousemove', this._onMouseMove, this) // Necessary to prevent 0.8 stutter 102 | .on('mousedown', this._onMouseDown, this) 103 | .on('mouseup', this._onMouseUp, this) // Necessary for 0.8 compatibility 104 | .addTo(this._map); 105 | 106 | this._map 107 | .on('mouseup', this._onMouseUp, this) // Necessary for 0.7 compatibility 108 | .on('mousemove', this._onMouseMove, this) 109 | .on('zoomlevelschange', this._onZoomEnd, this) 110 | .on('touchstart', this._onTouch, this) 111 | .on('zoomend', this._onZoomEnd, this); 112 | 113 | } 114 | }, 115 | 116 | // @method removeHooks(): void 117 | // Remove listener hooks from this handler. 118 | removeHooks: function () { 119 | L.Draw.Feature.prototype.removeHooks.call(this); 120 | 121 | this._clearHideErrorTimeout(); 122 | 123 | this._cleanUpShape(); 124 | 125 | // remove markers from map 126 | this._map.removeLayer(this._markerGroup); 127 | delete this._markerGroup; 128 | delete this._markers; 129 | 130 | this._map.removeLayer(this._poly); 131 | delete this._poly; 132 | 133 | this._mouseMarker 134 | .off('mousedown', this._onMouseDown, this) 135 | .off('mouseout', this._onMouseOut, this) 136 | .off('mouseup', this._onMouseUp, this) 137 | .off('mousemove', this._onMouseMove, this); 138 | this._map.removeLayer(this._mouseMarker); 139 | delete this._mouseMarker; 140 | 141 | // clean up DOM 142 | this._clearGuides(); 143 | 144 | this._map 145 | .off('mouseup', this._onMouseUp, this) 146 | .off('mousemove', this._onMouseMove, this) 147 | .off('zoomlevelschange', this._onZoomEnd, this) 148 | .off('zoomend', this._onZoomEnd, this) 149 | .off('touchstart', this._onTouch, this) 150 | .off('click', this._onTouch, this); 151 | }, 152 | 153 | // @method deleteLastVertex(): void 154 | // Remove the last vertex from the polyline, removes polyline from map if only one point exists. 155 | deleteLastVertex: function () { 156 | if (this._markers.length <= 1) { 157 | return; 158 | } 159 | 160 | var lastMarker = this._markers.pop(), 161 | poly = this._poly, 162 | // Replaces .spliceLatLngs() 163 | latlngs = poly.getLatLngs(), 164 | latlng = latlngs.splice(-1, 1)[0]; 165 | this._poly.setLatLngs(latlngs); 166 | 167 | this._markerGroup.removeLayer(lastMarker); 168 | 169 | if (poly.getLatLngs().length < 2) { 170 | this._map.removeLayer(poly); 171 | } 172 | 173 | this._vertexChanged(latlng, false); 174 | }, 175 | 176 | // @method addVertex(): void 177 | // Add a vertex to the end of the polyline 178 | addVertex: function (latlng) { 179 | var markersLength = this._markers.length; 180 | // markersLength must be greater than or equal to 2 before intersections can occur 181 | if (markersLength >= 2 && !this.options.allowIntersection && this._poly.newLatLngIntersects(latlng)) { 182 | this._showErrorTooltip(); 183 | return; 184 | } 185 | else if (this._errorShown) { 186 | this._hideErrorTooltip(); 187 | } 188 | 189 | this._markers.push(this._createMarker(latlng)); 190 | 191 | this._poly.addLatLng(latlng); 192 | 193 | if (this._poly.getLatLngs().length === 2) { 194 | this._map.addLayer(this._poly); 195 | } 196 | 197 | this._vertexChanged(latlng, true); 198 | }, 199 | 200 | // @method completeShape(): void 201 | // Closes the polyline between the first and last points 202 | completeShape: function () { 203 | if (this._markers.length <= 1 || !this._shapeIsValid()) { 204 | return; 205 | } 206 | 207 | this._fireCreatedEvent(); 208 | this.disable(); 209 | 210 | if (this.options.repeatMode) { 211 | this.enable(); 212 | } 213 | }, 214 | 215 | _finishShape: function () { 216 | var latlngs = this._poly._defaultShape ? this._poly._defaultShape() : this._poly.getLatLngs(); 217 | var intersects = this._poly.newLatLngIntersects(latlngs[latlngs.length - 1]); 218 | 219 | if ((!this.options.allowIntersection && intersects) || !this._shapeIsValid()) { 220 | this._showErrorTooltip(); 221 | return; 222 | } 223 | 224 | this._fireCreatedEvent(); 225 | this.disable(); 226 | if (this.options.repeatMode) { 227 | this.enable(); 228 | } 229 | }, 230 | 231 | // Called to verify the shape is valid when the user tries to finish it 232 | // Return false if the shape is not valid 233 | _shapeIsValid: function () { 234 | return true; 235 | }, 236 | 237 | _onZoomEnd: function () { 238 | if (this._markers !== null) { 239 | this._updateGuide(); 240 | } 241 | }, 242 | 243 | _onMouseMove: function (e) { 244 | var newPos = this._map.mouseEventToLayerPoint(e.originalEvent); 245 | var latlng = this._map.layerPointToLatLng(newPos); 246 | 247 | // Save latlng 248 | // should this be moved to _updateGuide() ? 249 | this._currentLatLng = latlng; 250 | 251 | this._updateTooltip(latlng); 252 | 253 | // Update the guide line 254 | this._updateGuide(newPos); 255 | 256 | // Update the mouse marker position 257 | this._mouseMarker.setLatLng(latlng); 258 | 259 | L.DomEvent.preventDefault(e.originalEvent); 260 | }, 261 | 262 | _vertexChanged: function (latlng, added) { 263 | this._map.fire(L.Draw.Event.DRAWVERTEX, {layers: this._markerGroup}); 264 | this._updateFinishHandler(); 265 | 266 | this._updateRunningMeasure(latlng, added); 267 | 268 | this._clearGuides(); 269 | 270 | this._updateTooltip(); 271 | }, 272 | 273 | _onMouseDown: function (e) { 274 | if (!this._clickHandled && !this._touchHandled && !this._disableMarkers) { 275 | this._onMouseMove(e); 276 | this._clickHandled = true; 277 | this._disableNewMarkers(); 278 | var originalEvent = e.originalEvent; 279 | var clientX = originalEvent.clientX; 280 | var clientY = originalEvent.clientY; 281 | this._startPoint.call(this, clientX, clientY); 282 | } 283 | }, 284 | 285 | _startPoint: function (clientX, clientY) { 286 | this._mouseDownOrigin = L.point(clientX, clientY); 287 | }, 288 | 289 | _onMouseUp: function (e) { 290 | var originalEvent = e.originalEvent; 291 | var clientX = originalEvent.clientX; 292 | var clientY = originalEvent.clientY; 293 | this._endPoint.call(this, clientX, clientY, e); 294 | this._clickHandled = null; 295 | }, 296 | 297 | _endPoint: function (clientX, clientY, e) { 298 | if (this._mouseDownOrigin) { 299 | var dragCheckDistance = L.point(clientX, clientY) 300 | .distanceTo(this._mouseDownOrigin); 301 | var lastPtDistance = this._calculateFinishDistance(e.latlng); 302 | if (this.options.maxPoints > 1 && this.options.maxPoints == this._markers.length + 1) { 303 | this.addVertex(e.latlng); 304 | this._finishShape(); 305 | } else if (lastPtDistance < 10 && L.Browser.touch) { 306 | this._finishShape(); 307 | } else if (Math.abs(dragCheckDistance) < 9 * (window.devicePixelRatio || 1)) { 308 | this.addVertex(e.latlng); 309 | } 310 | this._enableNewMarkers(); // after a short pause, enable new markers 311 | } 312 | this._mouseDownOrigin = null; 313 | }, 314 | 315 | // ontouch prevented by clickHandled flag because some browsers fire both click/touch events, 316 | // causing unwanted behavior 317 | _onTouch: function (e) { 318 | var originalEvent = e.originalEvent; 319 | var clientX; 320 | var clientY; 321 | if (originalEvent.touches && originalEvent.touches[0] && !this._clickHandled && !this._touchHandled && !this._disableMarkers) { 322 | clientX = originalEvent.touches[0].clientX; 323 | clientY = originalEvent.touches[0].clientY; 324 | this._disableNewMarkers(); 325 | this._touchHandled = true; 326 | this._startPoint.call(this, clientX, clientY); 327 | this._endPoint.call(this, clientX, clientY, e); 328 | this._touchHandled = null; 329 | } 330 | this._clickHandled = null; 331 | }, 332 | 333 | _onMouseOut: function () { 334 | if (this._tooltip) { 335 | this._tooltip._onMouseOut.call(this._tooltip); 336 | } 337 | }, 338 | 339 | // calculate if we are currently within close enough distance 340 | // of the closing point (first point for shapes, last point for lines) 341 | // this is semi-ugly code but the only reliable way i found to get the job done 342 | // note: calculating point.distanceTo between mouseDownOrigin and last marker did NOT work 343 | _calculateFinishDistance: function (potentialLatLng) { 344 | var lastPtDistance; 345 | if (this._markers.length > 0) { 346 | var finishMarker; 347 | if (this.type === L.Draw.Polyline.TYPE) { 348 | finishMarker = this._markers[this._markers.length - 1]; 349 | } else if (this.type === L.Draw.Polygon.TYPE) { 350 | finishMarker = this._markers[0]; 351 | } else { 352 | return Infinity; 353 | } 354 | var lastMarkerPoint = this._map.latLngToContainerPoint(finishMarker.getLatLng()), 355 | potentialMarker = new L.Marker(potentialLatLng, { 356 | icon: this.options.icon, 357 | zIndexOffset: this.options.zIndexOffset * 2 358 | }); 359 | var potentialMarkerPint = this._map.latLngToContainerPoint(potentialMarker.getLatLng()); 360 | lastPtDistance = lastMarkerPoint.distanceTo(potentialMarkerPint); 361 | } else { 362 | lastPtDistance = Infinity; 363 | } 364 | return lastPtDistance; 365 | }, 366 | 367 | _updateFinishHandler: function () { 368 | var markerCount = this._markers.length; 369 | // The last marker should have a click handler to close the polyline 370 | if (markerCount > 1) { 371 | this._markers[markerCount - 1].on('click', this._finishShape, this); 372 | } 373 | 374 | // Remove the old marker click handler (as only the last point should close the polyline) 375 | if (markerCount > 2) { 376 | this._markers[markerCount - 2].off('click', this._finishShape, this); 377 | } 378 | }, 379 | 380 | _createMarker: function (latlng) { 381 | var marker = new L.Marker(latlng, { 382 | icon: this.options.icon, 383 | zIndexOffset: this.options.zIndexOffset * 2 384 | }); 385 | 386 | this._markerGroup.addLayer(marker); 387 | 388 | return marker; 389 | }, 390 | 391 | _updateGuide: function (newPos) { 392 | var markerCount = this._markers ? this._markers.length : 0; 393 | 394 | if (markerCount > 0) { 395 | newPos = newPos || this._map.latLngToLayerPoint(this._currentLatLng); 396 | 397 | // draw the guide line 398 | this._clearGuides(); 399 | this._drawGuide( 400 | this._map.latLngToLayerPoint(this._markers[markerCount - 1].getLatLng()), 401 | newPos 402 | ); 403 | } 404 | }, 405 | 406 | _updateTooltip: function (latLng) { 407 | var text = this._getTooltipText(); 408 | 409 | if (latLng) { 410 | this._tooltip.updatePosition(latLng); 411 | } 412 | 413 | if (!this._errorShown) { 414 | this._tooltip.updateContent(text); 415 | } 416 | }, 417 | 418 | _drawGuide: function (pointA, pointB) { 419 | var length = Math.floor(Math.sqrt(Math.pow((pointB.x - pointA.x), 2) + Math.pow((pointB.y - pointA.y), 2))), 420 | guidelineDistance = this.options.guidelineDistance, 421 | maxGuideLineLength = this.options.maxGuideLineLength, 422 | // Only draw a guideline with a max length 423 | i = length > maxGuideLineLength ? length - maxGuideLineLength : guidelineDistance, 424 | fraction, 425 | dashPoint, 426 | dash; 427 | 428 | //create the guides container if we haven't yet 429 | if (!this._guidesContainer) { 430 | this._guidesContainer = L.DomUtil.create('div', 'leaflet-draw-guides', this._overlayPane); 431 | } 432 | 433 | //draw a dash every GuildeLineDistance 434 | for (; i < length; i += this.options.guidelineDistance) { 435 | //work out fraction along line we are 436 | fraction = i / length; 437 | 438 | //calculate new x,y point 439 | dashPoint = { 440 | x: Math.floor((pointA.x * (1 - fraction)) + (fraction * pointB.x)), 441 | y: Math.floor((pointA.y * (1 - fraction)) + (fraction * pointB.y)) 442 | }; 443 | 444 | //add guide dash to guide container 445 | dash = L.DomUtil.create('div', 'leaflet-draw-guide-dash', this._guidesContainer); 446 | dash.style.backgroundColor = 447 | !this._errorShown ? this.options.shapeOptions.color : this.options.drawError.color; 448 | 449 | L.DomUtil.setPosition(dash, dashPoint); 450 | } 451 | }, 452 | 453 | _updateGuideColor: function (color) { 454 | if (this._guidesContainer) { 455 | for (var i = 0, l = this._guidesContainer.childNodes.length; i < l; i++) { 456 | this._guidesContainer.childNodes[i].style.backgroundColor = color; 457 | } 458 | } 459 | }, 460 | 461 | // removes all child elements (guide dashes) from the guides container 462 | _clearGuides: function () { 463 | if (this._guidesContainer) { 464 | while (this._guidesContainer.firstChild) { 465 | this._guidesContainer.removeChild(this._guidesContainer.firstChild); 466 | } 467 | } 468 | }, 469 | 470 | _getTooltipText: function () { 471 | var showLength = this.options.showLength, 472 | labelText, distanceStr; 473 | if (this._markers.length === 0) { 474 | labelText = { 475 | text: L.drawLocal.draw.handlers.polyline.tooltip.start 476 | }; 477 | } else { 478 | distanceStr = showLength ? this._getMeasurementString() : ''; 479 | 480 | if (this._markers.length === 1) { 481 | labelText = { 482 | text: L.drawLocal.draw.handlers.polyline.tooltip.cont, 483 | subtext: distanceStr 484 | }; 485 | } else { 486 | labelText = { 487 | text: L.drawLocal.draw.handlers.polyline.tooltip.end, 488 | subtext: distanceStr 489 | }; 490 | } 491 | } 492 | return labelText; 493 | }, 494 | 495 | _updateRunningMeasure: function (latlng, added) { 496 | var markersLength = this._markers.length, 497 | previousMarkerIndex, distance; 498 | 499 | if (this._markers.length === 1) { 500 | this._measurementRunningTotal = 0; 501 | } else { 502 | previousMarkerIndex = markersLength - (added ? 2 : 1); 503 | 504 | // Calculate the distance based on the version 505 | if (L.GeometryUtil.isVersion07x()) { 506 | distance = latlng.distanceTo(this._markers[previousMarkerIndex].getLatLng()) * (this.options.factor || 1); 507 | } else { 508 | distance = this._map.distance(latlng, this._markers[previousMarkerIndex].getLatLng()) * (this.options.factor || 1); 509 | } 510 | 511 | this._measurementRunningTotal += distance * (added ? 1 : -1); 512 | } 513 | }, 514 | 515 | _getMeasurementString: function () { 516 | var currentLatLng = this._currentLatLng, 517 | previousLatLng = this._markers[this._markers.length - 1].getLatLng(), 518 | distance; 519 | 520 | // Calculate the distance from the last fixed point to the mouse position based on the version 521 | if (L.GeometryUtil.isVersion07x()) { 522 | distance = previousLatLng && currentLatLng && currentLatLng.distanceTo ? this._measurementRunningTotal + currentLatLng.distanceTo(previousLatLng) * (this.options.factor || 1) : this._measurementRunningTotal || 0; 523 | } else { 524 | distance = previousLatLng && currentLatLng ? this._measurementRunningTotal + this._map.distance(currentLatLng, previousLatLng) * (this.options.factor || 1) : this._measurementRunningTotal || 0; 525 | } 526 | 527 | return L.GeometryUtil.readableDistance(distance, this.options.metric, this.options.feet, this.options.nautic, this.options.precision); 528 | }, 529 | 530 | _showErrorTooltip: function () { 531 | this._errorShown = true; 532 | 533 | // Update tooltip 534 | this._tooltip 535 | .showAsError() 536 | .updateContent({text: this.options.drawError.message}); 537 | 538 | // Update shape 539 | this._updateGuideColor(this.options.drawError.color); 540 | this._poly.setStyle({color: this.options.drawError.color}); 541 | 542 | // Hide the error after 2 seconds 543 | this._clearHideErrorTimeout(); 544 | this._hideErrorTimeout = setTimeout(L.Util.bind(this._hideErrorTooltip, this), this.options.drawError.timeout); 545 | }, 546 | 547 | _hideErrorTooltip: function () { 548 | this._errorShown = false; 549 | 550 | this._clearHideErrorTimeout(); 551 | 552 | // Revert tooltip 553 | this._tooltip 554 | .removeError() 555 | .updateContent(this._getTooltipText()); 556 | 557 | // Revert shape 558 | this._updateGuideColor(this.options.shapeOptions.color); 559 | this._poly.setStyle({color: this.options.shapeOptions.color}); 560 | }, 561 | 562 | _clearHideErrorTimeout: function () { 563 | if (this._hideErrorTimeout) { 564 | clearTimeout(this._hideErrorTimeout); 565 | this._hideErrorTimeout = null; 566 | } 567 | }, 568 | 569 | // disable new markers temporarily; 570 | // this is to prevent duplicated touch/click events in some browsers 571 | _disableNewMarkers: function () { 572 | this._disableMarkers = true; 573 | }, 574 | 575 | // see _disableNewMarkers 576 | _enableNewMarkers: function () { 577 | setTimeout(function () { 578 | this._disableMarkers = false; 579 | }.bind(this), 50); 580 | }, 581 | 582 | _cleanUpShape: function () { 583 | if (this._markers.length > 1) { 584 | this._markers[this._markers.length - 1].off('click', this._finishShape, this); 585 | } 586 | }, 587 | 588 | _fireCreatedEvent: function () { 589 | var poly = new this.Poly(this._poly.getLatLngs(), this.options.shapeOptions); 590 | L.Draw.Feature.prototype._fireCreatedEvent.call(this, poly); 591 | } 592 | }); 593 | -------------------------------------------------------------------------------- /static_files/draw_files/Draw.Rectangle.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @class L.Draw.Rectangle 3 | * @aka Draw.Rectangle 4 | * @inherits L.Draw.SimpleShape 5 | */ 6 | L.Draw.Rectangle = L.Draw.SimpleShape.extend({ 7 | statics: { 8 | TYPE: 'rectangle' 9 | }, 10 | 11 | options: { 12 | shapeOptions: { 13 | stroke: true, 14 | color: '#3388ff', 15 | weight: 4, 16 | opacity: 0.5, 17 | fill: true, 18 | fillColor: null, //same as color by default 19 | fillOpacity: 0.2, 20 | showArea: true, 21 | clickable: true 22 | }, 23 | metric: true // Whether to use the metric measurement system or imperial 24 | }, 25 | 26 | // @method initialize(): void 27 | initialize: function (map, options) { 28 | // Save the type so super can fire, need to do this as cannot do this.TYPE :( 29 | this.type = L.Draw.Rectangle.TYPE; 30 | 31 | this._initialLabelText = L.drawLocal.draw.handlers.rectangle.tooltip.start; 32 | 33 | L.Draw.SimpleShape.prototype.initialize.call(this, map, options); 34 | }, 35 | 36 | // @method disable(): void 37 | disable: function () { 38 | if (!this._enabled) { 39 | return; 40 | } 41 | 42 | this._isCurrentlyTwoClickDrawing = false; 43 | L.Draw.SimpleShape.prototype.disable.call(this); 44 | }, 45 | 46 | _onMouseUp: function (e) { 47 | if (!this._shape && !this._isCurrentlyTwoClickDrawing) { 48 | this._isCurrentlyTwoClickDrawing = true; 49 | return; 50 | } 51 | 52 | // Make sure closing click is on map 53 | if (this._isCurrentlyTwoClickDrawing && !_hasAncestor(e.target, 'leaflet-pane')) { 54 | return; 55 | } 56 | 57 | L.Draw.SimpleShape.prototype._onMouseUp.call(this); 58 | }, 59 | 60 | _drawShape: function (latlng) { 61 | if (!this._shape) { 62 | this._shape = new L.Rectangle(new L.LatLngBounds(this._startLatLng, latlng), this.options.shapeOptions); 63 | this._map.addLayer(this._shape); 64 | } else { 65 | this._shape.setBounds(new L.LatLngBounds(this._startLatLng, latlng)); 66 | } 67 | }, 68 | 69 | _fireCreatedEvent: function () { 70 | var rectangle = new L.Rectangle(this._shape.getBounds(), this.options.shapeOptions); 71 | L.Draw.SimpleShape.prototype._fireCreatedEvent.call(this, rectangle); 72 | }, 73 | 74 | _getTooltipText: function () { 75 | var tooltipText = L.Draw.SimpleShape.prototype._getTooltipText.call(this), 76 | shape = this._shape, 77 | showArea = this.options.showArea, 78 | latLngs, area, subtext; 79 | 80 | if (shape) { 81 | latLngs = this._shape._defaultShape ? this._shape._defaultShape() : this._shape.getLatLngs(); 82 | area = L.GeometryUtil.geodesicArea(latLngs); 83 | subtext = showArea ? L.GeometryUtil.readableArea(area, this.options.metric) : ''; 84 | } 85 | 86 | return { 87 | text: tooltipText.text, 88 | subtext: subtext 89 | }; 90 | } 91 | }); 92 | 93 | function _hasAncestor(el, cls) { 94 | while ((el = el.parentElement) && !el.classList.contains(cls)) { 95 | ; 96 | } 97 | return el; 98 | } 99 | -------------------------------------------------------------------------------- /static_files/draw_files/Draw.SimpleShape.js: -------------------------------------------------------------------------------- 1 | L.SimpleShape = {}; 2 | /** 3 | * @class L.Draw.SimpleShape 4 | * @aka Draw.SimpleShape 5 | * @inherits L.Draw.Feature 6 | */ 7 | L.Draw.SimpleShape = L.Draw.Feature.extend({ 8 | options: { 9 | repeatMode: false 10 | }, 11 | 12 | // @method initialize(): void 13 | initialize: function (map, options) { 14 | this._endLabelText = L.drawLocal.draw.handlers.simpleshape.tooltip.end; 15 | 16 | L.Draw.Feature.prototype.initialize.call(this, map, options); 17 | }, 18 | 19 | // @method addHooks(): void 20 | // Add listener hooks to this handler. 21 | addHooks: function () { 22 | L.Draw.Feature.prototype.addHooks.call(this); 23 | if (this._map) { 24 | this._mapDraggable = this._map.dragging.enabled(); 25 | 26 | if (this._mapDraggable) { 27 | this._map.dragging.disable(); 28 | } 29 | 30 | //TODO refactor: move cursor to styles 31 | this._container.style.cursor = 'crosshair'; 32 | 33 | this._tooltip.updateContent({text: this._initialLabelText}); 34 | 35 | this._map 36 | .on('mousedown', this._onMouseDown, this) 37 | .on('mousemove', this._onMouseMove, this) 38 | .on('touchstart', this._onMouseDown, this) 39 | .on('touchmove', this._onMouseMove, this); 40 | 41 | // we should prevent default, otherwise default behavior (scrolling) will fire, 42 | // and that will cause document.touchend to fire and will stop the drawing 43 | // (circle, rectangle) in touch mode. 44 | // (update): we have to send passive now to prevent scroll, because by default it is {passive: true} now, which means, 45 | // handler can't event.preventDefault 46 | // check the news https://developers.google.com/web/updates/2016/06/passive-event-listeners 47 | document.addEventListener('touchstart', L.DomEvent.preventDefault, {passive: false}); 48 | } 49 | }, 50 | 51 | // @method removeHooks(): void 52 | // Remove listener hooks from this handler. 53 | removeHooks: function () { 54 | L.Draw.Feature.prototype.removeHooks.call(this); 55 | if (this._map) { 56 | if (this._mapDraggable) { 57 | this._map.dragging.enable(); 58 | } 59 | 60 | //TODO refactor: move cursor to styles 61 | this._container.style.cursor = ''; 62 | 63 | this._map 64 | .off('mousedown', this._onMouseDown, this) 65 | .off('mousemove', this._onMouseMove, this) 66 | .off('touchstart', this._onMouseDown, this) 67 | .off('touchmove', this._onMouseMove, this); 68 | 69 | L.DomEvent.off(document, 'mouseup', this._onMouseUp, this); 70 | L.DomEvent.off(document, 'touchend', this._onMouseUp, this); 71 | 72 | document.removeEventListener('touchstart', L.DomEvent.preventDefault); 73 | 74 | // If the box element doesn't exist they must not have moved the mouse, so don't need to destroy/return 75 | if (this._shape) { 76 | this._map.removeLayer(this._shape); 77 | delete this._shape; 78 | } 79 | } 80 | this._isDrawing = false; 81 | }, 82 | 83 | _getTooltipText: function () { 84 | return { 85 | text: this._endLabelText 86 | }; 87 | }, 88 | 89 | _onMouseDown: function (e) { 90 | this._isDrawing = true; 91 | this._startLatLng = e.latlng; 92 | 93 | L.DomEvent 94 | .on(document, 'mouseup', this._onMouseUp, this) 95 | .on(document, 'touchend', this._onMouseUp, this) 96 | .preventDefault(e.originalEvent); 97 | }, 98 | 99 | _onMouseMove: function (e) { 100 | var latlng = e.latlng; 101 | 102 | this._tooltip.updatePosition(latlng); 103 | if (this._isDrawing) { 104 | this._tooltip.updateContent(this._getTooltipText()); 105 | this._drawShape(latlng); 106 | } 107 | }, 108 | 109 | _onMouseUp: function () { 110 | if (this._shape) { 111 | this._fireCreatedEvent(); 112 | } 113 | 114 | this.disable(); 115 | if (this.options.repeatMode) { 116 | this.enable(); 117 | } 118 | } 119 | }); 120 | -------------------------------------------------------------------------------- /static_files/draw_files/DrawToolbar.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @class L.DrawToolbar 3 | * @aka Toolbar 4 | */ 5 | L.DrawToolbar = L.Toolbar.extend({ 6 | 7 | statics: { 8 | TYPE: 'draw' 9 | }, 10 | 11 | options: { 12 | polyline: {}, 13 | polygon: {}, 14 | rectangle: {}, 15 | circle: {}, 16 | marker: {}, 17 | circlemarker: {} 18 | }, 19 | 20 | // @method initialize(): void 21 | initialize: function (options) { 22 | // Ensure that the options are merged correctly since L.extend is only shallow 23 | for (var type in this.options) { 24 | if (this.options.hasOwnProperty(type)) { 25 | if (options[type]) { 26 | options[type] = L.extend({}, this.options[type], options[type]); 27 | } 28 | } 29 | } 30 | 31 | this._toolbarClass = 'leaflet-draw-draw'; 32 | L.Toolbar.prototype.initialize.call(this, options); 33 | }, 34 | 35 | // @method getModeHandlers(): object 36 | // Get mode handlers information 37 | getModeHandlers: function (map) { 38 | return [ 39 | { 40 | enabled: this.options.polyline, 41 | handler: new L.Draw.Polyline(map, this.options.polyline), 42 | title: L.drawLocal.draw.toolbar.buttons.polyline 43 | }, 44 | { 45 | enabled: this.options.polygon, 46 | handler: new L.Draw.Polygon(map, this.options.polygon), 47 | title: L.drawLocal.draw.toolbar.buttons.polygon 48 | }, 49 | { 50 | enabled: this.options.rectangle, 51 | handler: new L.Draw.Rectangle(map, this.options.rectangle), 52 | title: L.drawLocal.draw.toolbar.buttons.rectangle 53 | }, 54 | { 55 | enabled: this.options.circle, 56 | handler: new L.Draw.Circle(map, this.options.circle), 57 | title: L.drawLocal.draw.toolbar.buttons.circle 58 | }, 59 | { 60 | enabled: this.options.marker, 61 | handler: new L.Draw.Marker(map, this.options.marker), 62 | title: L.drawLocal.draw.toolbar.buttons.marker 63 | }, 64 | { 65 | enabled: this.options.circlemarker, 66 | handler: new L.Draw.CircleMarker(map, this.options.circlemarker), 67 | title: L.drawLocal.draw.toolbar.buttons.circlemarker 68 | } 69 | ]; 70 | }, 71 | 72 | // @method getActions(): object 73 | // Get action information 74 | getActions: function (handler) { 75 | return [ 76 | { 77 | enabled: handler.completeShape, 78 | title: L.drawLocal.draw.toolbar.finish.title, 79 | text: L.drawLocal.draw.toolbar.finish.text, 80 | callback: handler.completeShape, 81 | context: handler 82 | }, 83 | { 84 | enabled: handler.deleteLastVertex, 85 | title: L.drawLocal.draw.toolbar.undo.title, 86 | text: L.drawLocal.draw.toolbar.undo.text, 87 | callback: handler.deleteLastVertex, 88 | context: handler 89 | }, 90 | { 91 | title: L.drawLocal.draw.toolbar.actions.title, 92 | text: L.drawLocal.draw.toolbar.actions.text, 93 | callback: this.disable, 94 | context: this 95 | } 96 | ]; 97 | }, 98 | 99 | // @method setOptions(): void 100 | // Sets the options to the toolbar 101 | setOptions: function (options) { 102 | L.setOptions(this, options); 103 | 104 | for (var type in this._modes) { 105 | if (this._modes.hasOwnProperty(type) && options.hasOwnProperty(type)) { 106 | this._modes[type].handler.setOptions(options[type]); 107 | } 108 | } 109 | } 110 | }); 111 | -------------------------------------------------------------------------------- /static_files/draw_files/Edit.Circle.js: -------------------------------------------------------------------------------- 1 | L.Edit = L.Edit || {}; 2 | /** 3 | * @class L.Edit.Circle 4 | * @aka Edit.Circle 5 | * @inherits L.Edit.CircleMarker 6 | */ 7 | L.Edit.Circle = L.Edit.CircleMarker.extend({ 8 | 9 | _createResizeMarker: function () { 10 | var center = this._shape.getLatLng(), 11 | resizemarkerPoint = this._getResizeMarkerPoint(center); 12 | 13 | this._resizeMarkers = []; 14 | this._resizeMarkers.push(this._createMarker(resizemarkerPoint, this.options.resizeIcon)); 15 | }, 16 | 17 | _getResizeMarkerPoint: function (latlng) { 18 | // From L.shape.getBounds() 19 | var delta = this._shape._radius * Math.cos(Math.PI / 4), 20 | point = this._map.project(latlng); 21 | return this._map.unproject([point.x + delta, point.y - delta]); 22 | }, 23 | 24 | _resize: function (latlng) { 25 | var moveLatLng = this._moveMarker.getLatLng(); 26 | 27 | // Calculate the radius based on the version 28 | if (L.GeometryUtil.isVersion07x()) { 29 | radius = moveLatLng.distanceTo(latlng); 30 | } else { 31 | radius = this._map.distance(moveLatLng, latlng); 32 | } 33 | this._shape.setRadius(radius); 34 | 35 | if (this._map.editTooltip) { 36 | this._map._editTooltip.updateContent({ 37 | text: L.drawLocal.edit.handlers.edit.tooltip.subtext + '
' + L.drawLocal.edit.handlers.edit.tooltip.text, 38 | subtext: L.drawLocal.draw.handlers.circle.radius + ': ' + 39 | L.GeometryUtil.readableDistance(radius, true, this.options.feet, this.options.nautic) 40 | }); 41 | } 42 | 43 | this._shape.setRadius(radius); 44 | 45 | this._map.fire(L.Draw.Event.EDITRESIZE, {layer: this._shape}); 46 | } 47 | }); 48 | 49 | L.Circle.addInitHook(function () { 50 | if (L.Edit.Circle) { 51 | this.editing = new L.Edit.Circle(this); 52 | 53 | if (this.options.editable) { 54 | this.editing.enable(); 55 | } 56 | } 57 | 58 | this.on('add', function () { 59 | if (this.editing && this.editing.enabled()) { 60 | this.editing.addHooks(); 61 | } 62 | }); 63 | 64 | this.on('remove', function () { 65 | if (this.editing && this.editing.enabled()) { 66 | this.editing.removeHooks(); 67 | } 68 | }); 69 | }); 70 | -------------------------------------------------------------------------------- /static_files/draw_files/Edit.CircleMarker.js: -------------------------------------------------------------------------------- 1 | L.Edit = L.Edit || {}; 2 | /** 3 | * @class L.Edit.CircleMarker 4 | * @aka Edit.Circle 5 | * @inherits L.Edit.SimpleShape 6 | */ 7 | L.Edit.CircleMarker = L.Edit.SimpleShape.extend({ 8 | _createMoveMarker: function () { 9 | var center = this._shape.getLatLng(); 10 | 11 | this._moveMarker = this._createMarker(center, this.options.moveIcon); 12 | }, 13 | 14 | _createResizeMarker: function () { 15 | // To avoid an undefined check in L.Edit.SimpleShape.removeHooks 16 | this._resizeMarkers = []; 17 | }, 18 | 19 | _move: function (latlng) { 20 | if (this._resizeMarkers.length) { 21 | var resizemarkerPoint = this._getResizeMarkerPoint(latlng); 22 | // Move the resize marker 23 | this._resizeMarkers[0].setLatLng(resizemarkerPoint); 24 | } 25 | 26 | // Move the circle 27 | this._shape.setLatLng(latlng); 28 | 29 | this._map.fire(L.Draw.Event.EDITMOVE, {layer: this._shape}); 30 | }, 31 | }); 32 | 33 | L.CircleMarker.addInitHook(function () { 34 | if (L.Edit.CircleMarker) { 35 | this.editing = new L.Edit.CircleMarker(this); 36 | 37 | if (this.options.editable) { 38 | this.editing.enable(); 39 | } 40 | } 41 | 42 | this.on('add', function () { 43 | if (this.editing && this.editing.enabled()) { 44 | this.editing.addHooks(); 45 | } 46 | }); 47 | 48 | this.on('remove', function () { 49 | if (this.editing && this.editing.enabled()) { 50 | this.editing.removeHooks(); 51 | } 52 | }); 53 | }); 54 | -------------------------------------------------------------------------------- /static_files/draw_files/Edit.Marker.js: -------------------------------------------------------------------------------- 1 | L.Edit = L.Edit || {}; 2 | 3 | /** 4 | * @class L.Edit.Marker 5 | * @aka Edit.Marker 6 | */ 7 | L.Edit.Marker = L.Handler.extend({ 8 | // @method initialize(): void 9 | initialize: function (marker, options) { 10 | this._marker = marker; 11 | L.setOptions(this, options); 12 | }, 13 | 14 | // @method addHooks(): void 15 | // Add listener hooks to this handler 16 | addHooks: function () { 17 | var marker = this._marker; 18 | 19 | marker.dragging.enable(); 20 | marker.on('dragend', this._onDragEnd, marker); 21 | this._toggleMarkerHighlight(); 22 | }, 23 | 24 | // @method removeHooks(): void 25 | // Remove listener hooks from this handler 26 | removeHooks: function () { 27 | var marker = this._marker; 28 | 29 | marker.dragging.disable(); 30 | marker.off('dragend', this._onDragEnd, marker); 31 | this._toggleMarkerHighlight(); 32 | }, 33 | 34 | _onDragEnd: function (e) { 35 | var layer = e.target; 36 | layer.edited = true; 37 | this._map.fire(L.Draw.Event.EDITMOVE, {layer: layer}); 38 | }, 39 | 40 | _toggleMarkerHighlight: function () { 41 | var icon = this._marker._icon; 42 | 43 | // Don't do anything if this layer is a marker but doesn't have an icon. Markers 44 | // should usually have icons. If using Leaflet.draw with Leaflet.markercluster there 45 | // is a chance that a marker doesn't. 46 | if (!icon) { 47 | return; 48 | } 49 | 50 | // This is quite naughty, but I don't see another way of doing it. (short of setting a new icon) 51 | icon.style.display = 'none'; 52 | 53 | if (L.DomUtil.hasClass(icon, 'leaflet-edit-marker-selected')) { 54 | L.DomUtil.removeClass(icon, 'leaflet-edit-marker-selected'); 55 | // Offset as the border will make the icon move. 56 | this._offsetMarker(icon, -4); 57 | 58 | } else { 59 | L.DomUtil.addClass(icon, 'leaflet-edit-marker-selected'); 60 | // Offset as the border will make the icon move. 61 | this._offsetMarker(icon, 4); 62 | } 63 | 64 | icon.style.display = ''; 65 | }, 66 | 67 | _offsetMarker: function (icon, offset) { 68 | var iconMarginTop = parseInt(icon.style.marginTop, 10) - offset, 69 | iconMarginLeft = parseInt(icon.style.marginLeft, 10) - offset; 70 | 71 | icon.style.marginTop = iconMarginTop + 'px'; 72 | icon.style.marginLeft = iconMarginLeft + 'px'; 73 | } 74 | }); 75 | 76 | L.Marker.addInitHook(function () { 77 | if (L.Edit.Marker) { 78 | this.editing = new L.Edit.Marker(this); 79 | 80 | if (this.options.editable) { 81 | this.editing.enable(); 82 | } 83 | } 84 | }); 85 | -------------------------------------------------------------------------------- /static_files/draw_files/Edit.Poly.js: -------------------------------------------------------------------------------- 1 | L.Edit = L.Edit || {}; 2 | 3 | /** 4 | * @class L.Edit.Polyline 5 | * @aka L.Edit.Poly 6 | * @aka Edit.Poly 7 | */ 8 | L.Edit.Poly = L.Handler.extend({ 9 | // @method initialize(): void 10 | initialize: function (poly) { 11 | 12 | this.latlngs = [poly._latlngs]; 13 | if (poly._holes) { 14 | this.latlngs = this.latlngs.concat(poly._holes); 15 | } 16 | 17 | this._poly = poly; 18 | 19 | this._poly.on('revert-edited', this._updateLatLngs, this); 20 | }, 21 | 22 | // Compatibility method to normalize Poly* objects 23 | // between 0.7.x and 1.0+ 24 | _defaultShape: function () { 25 | if (!L.Polyline._flat) { 26 | return this._poly._latlngs; 27 | } 28 | return L.Polyline._flat(this._poly._latlngs) ? this._poly._latlngs : this._poly._latlngs[0]; 29 | }, 30 | 31 | _eachVertexHandler: function (callback) { 32 | for (var i = 0; i < this._verticesHandlers.length; i++) { 33 | callback(this._verticesHandlers[i]); 34 | } 35 | }, 36 | 37 | // @method addHooks(): void 38 | // Add listener hooks to this handler 39 | addHooks: function () { 40 | this._initHandlers(); 41 | this._eachVertexHandler(function (handler) { 42 | handler.addHooks(); 43 | }); 44 | }, 45 | 46 | // @method removeHooks(): void 47 | // Remove listener hooks from this handler 48 | removeHooks: function () { 49 | this._eachVertexHandler(function (handler) { 50 | handler.removeHooks(); 51 | }); 52 | }, 53 | 54 | // @method updateMarkers(): void 55 | // Fire an update for each vertex handler 56 | updateMarkers: function () { 57 | this._eachVertexHandler(function (handler) { 58 | handler.updateMarkers(); 59 | }); 60 | }, 61 | 62 | _initHandlers: function () { 63 | this._verticesHandlers = []; 64 | for (var i = 0; i < this.latlngs.length; i++) { 65 | this._verticesHandlers.push(new L.Edit.PolyVerticesEdit(this._poly, this.latlngs[i], this._poly.options.poly)); 66 | } 67 | }, 68 | 69 | _updateLatLngs: function (e) { 70 | this.latlngs = [e.layer._latlngs]; 71 | if (e.layer._holes) { 72 | this.latlngs = this.latlngs.concat(e.layer._holes); 73 | } 74 | } 75 | 76 | }); 77 | 78 | /** 79 | * @class L.Edit.PolyVerticesEdit 80 | * @aka Edit.PolyVerticesEdit 81 | */ 82 | L.Edit.PolyVerticesEdit = L.Handler.extend({ 83 | options: { 84 | icon: new L.DivIcon({ 85 | iconSize: new L.Point(8, 8), 86 | className: 'leaflet-div-icon leaflet-editing-icon' 87 | }), 88 | touchIcon: new L.DivIcon({ 89 | iconSize: new L.Point(20, 20), 90 | className: 'leaflet-div-icon leaflet-editing-icon leaflet-touch-icon' 91 | }), 92 | drawError: { 93 | color: '#b00b00', 94 | timeout: 1000 95 | } 96 | 97 | 98 | }, 99 | 100 | // @method intialize(): void 101 | initialize: function (poly, latlngs, options) { 102 | // if touch, switch to touch icon 103 | if (L.Browser.touch) { 104 | this.options.icon = this.options.touchIcon; 105 | } 106 | this._poly = poly; 107 | 108 | if (options && options.drawError) { 109 | options.drawError = L.Util.extend({}, this.options.drawError, options.drawError); 110 | } 111 | 112 | this._latlngs = latlngs; 113 | 114 | L.setOptions(this, options); 115 | }, 116 | 117 | // Compatibility method to normalize Poly* objects 118 | // between 0.7.x and 1.0+ 119 | _defaultShape: function () { 120 | if (!L.Polyline._flat) { 121 | return this._latlngs; 122 | } 123 | return L.Polyline._flat(this._latlngs) ? this._latlngs : this._latlngs[0]; 124 | }, 125 | 126 | // @method addHooks(): void 127 | // Add listener hooks to this handler. 128 | addHooks: function () { 129 | var poly = this._poly; 130 | var path = poly._path; 131 | 132 | if (!(poly instanceof L.Polygon)) { 133 | poly.options.fill = false; 134 | if (poly.options.editing) { 135 | poly.options.editing.fill = false; 136 | } 137 | } 138 | 139 | if (path) { 140 | if (poly.options.editing && poly.options.editing.className) { 141 | if (poly.options.original.className) { 142 | poly.options.original.className.split(' ').forEach(function (className) { 143 | L.DomUtil.removeClass(path, className); 144 | }); 145 | } 146 | poly.options.editing.className.split(' ').forEach(function (className) { 147 | L.DomUtil.addClass(path, className); 148 | }); 149 | } 150 | } 151 | 152 | poly.setStyle(poly.options.editing); 153 | 154 | if (this._poly._map) { 155 | 156 | this._map = this._poly._map; // Set map 157 | 158 | if (!this._markerGroup) { 159 | this._initMarkers(); 160 | } 161 | this._poly._map.addLayer(this._markerGroup); 162 | } 163 | }, 164 | 165 | // @method removeHooks(): void 166 | // Remove listener hooks from this handler. 167 | removeHooks: function () { 168 | var poly = this._poly; 169 | var path = poly._path; 170 | 171 | if (path) { 172 | if (poly.options.editing && poly.options.editing.className) { 173 | poly.options.editing.className.split(' ').forEach(function (className) { 174 | L.DomUtil.removeClass(path, className); 175 | }); 176 | if (poly.options.original.className) { 177 | poly.options.original.className.split(' ').forEach(function (className) { 178 | L.DomUtil.addClass(path, className); 179 | }); 180 | } 181 | } 182 | } 183 | 184 | poly.setStyle(poly.options.original); 185 | 186 | if (poly._map) { 187 | poly._map.removeLayer(this._markerGroup); 188 | delete this._markerGroup; 189 | delete this._markers; 190 | } 191 | }, 192 | 193 | // @method updateMarkers(): void 194 | // Clear markers and update their location 195 | updateMarkers: function () { 196 | this._markerGroup.clearLayers(); 197 | this._initMarkers(); 198 | }, 199 | 200 | _initMarkers: function () { 201 | if (!this._markerGroup) { 202 | this._markerGroup = new L.LayerGroup(); 203 | } 204 | this._markers = []; 205 | 206 | var latlngs = this._defaultShape(), 207 | i, j, len, marker; 208 | 209 | for (i = 0, len = latlngs.length; i < len; i++) { 210 | 211 | marker = this._createMarker(latlngs[i], i); 212 | marker.on('click', this._onMarkerClick, this); 213 | marker.on('contextmenu', this._onContextMenu, this); 214 | this._markers.push(marker); 215 | } 216 | 217 | var markerLeft, markerRight; 218 | 219 | for (i = 0, j = len - 1; i < len; j = i++) { 220 | if (i === 0 && !(L.Polygon && (this._poly instanceof L.Polygon))) { 221 | continue; 222 | } 223 | 224 | markerLeft = this._markers[j]; 225 | markerRight = this._markers[i]; 226 | 227 | this._createMiddleMarker(markerLeft, markerRight); 228 | this._updatePrevNext(markerLeft, markerRight); 229 | } 230 | }, 231 | 232 | _createMarker: function (latlng, index) { 233 | // Extending L.Marker in TouchEvents.js to include touch. 234 | var marker = new L.Marker.Touch(latlng, { 235 | draggable: true, 236 | icon: this.options.icon, 237 | }); 238 | 239 | marker._origLatLng = latlng; 240 | marker._index = index; 241 | 242 | marker 243 | .on('dragstart', this._onMarkerDragStart, this) 244 | .on('drag', this._onMarkerDrag, this) 245 | .on('dragend', this._fireEdit, this) 246 | .on('touchmove', this._onTouchMove, this) 247 | .on('touchend', this._fireEdit, this) 248 | .on('MSPointerMove', this._onTouchMove, this) 249 | .on('MSPointerUp', this._fireEdit, this); 250 | 251 | this._markerGroup.addLayer(marker); 252 | 253 | return marker; 254 | }, 255 | 256 | _onMarkerDragStart: function () { 257 | this._poly.fire('editstart'); 258 | }, 259 | 260 | _spliceLatLngs: function () { 261 | var latlngs = this._defaultShape(); 262 | var removed = [].splice.apply(latlngs, arguments); 263 | this._poly._convertLatLngs(latlngs, true); 264 | this._poly.redraw(); 265 | return removed; 266 | }, 267 | 268 | _removeMarker: function (marker) { 269 | var i = marker._index; 270 | 271 | this._markerGroup.removeLayer(marker); 272 | this._markers.splice(i, 1); 273 | this._spliceLatLngs(i, 1); 274 | this._updateIndexes(i, -1); 275 | 276 | marker 277 | .off('dragstart', this._onMarkerDragStart, this) 278 | .off('drag', this._onMarkerDrag, this) 279 | .off('dragend', this._fireEdit, this) 280 | .off('touchmove', this._onMarkerDrag, this) 281 | .off('touchend', this._fireEdit, this) 282 | .off('click', this._onMarkerClick, this) 283 | .off('MSPointerMove', this._onTouchMove, this) 284 | .off('MSPointerUp', this._fireEdit, this); 285 | }, 286 | 287 | _fireEdit: function () { 288 | this._poly.edited = true; 289 | this._poly.fire('edit'); 290 | this._poly._map.fire(L.Draw.Event.EDITVERTEX, {layers: this._markerGroup, poly: this._poly}); 291 | }, 292 | 293 | _onMarkerDrag: function (e) { 294 | var marker = e.target; 295 | var poly = this._poly; 296 | 297 | L.extend(marker._origLatLng, marker._latlng); 298 | 299 | if (marker._middleLeft) { 300 | marker._middleLeft.setLatLng(this._getMiddleLatLng(marker._prev, marker)); 301 | } 302 | if (marker._middleRight) { 303 | marker._middleRight.setLatLng(this._getMiddleLatLng(marker, marker._next)); 304 | } 305 | 306 | if (poly.options.poly) { 307 | var tooltip = poly._map._editTooltip; // Access the tooltip 308 | 309 | // If we don't allow intersections and the polygon intersects 310 | if (!poly.options.poly.allowIntersection && poly.intersects()) { 311 | 312 | var originalColor = poly.options.color; 313 | poly.setStyle({color: this.options.drawError.color}); 314 | 315 | // Manually trigger 'dragend' behavior on marker we are about to remove 316 | // WORKAROUND: introduced in 1.0.0-rc2, may be related to #4484 317 | if (L.version.indexOf('0.7') !== 0) { 318 | marker.dragging._draggable._onUp(e); 319 | } 320 | this._onMarkerClick(e); // Remove violating marker 321 | // FIXME: Reset the marker to it's original position (instead of remove) 322 | 323 | if (tooltip) { 324 | tooltip.updateContent({ 325 | text: L.drawLocal.draw.handlers.polyline.error 326 | }); 327 | } 328 | 329 | // Reset everything back to normal after a second 330 | setTimeout(function () { 331 | poly.setStyle({color: originalColor}); 332 | if (tooltip) { 333 | tooltip.updateContent({ 334 | text: L.drawLocal.edit.handlers.edit.tooltip.text, 335 | subtext: L.drawLocal.edit.handlers.edit.tooltip.subtext 336 | }); 337 | } 338 | }, 1000); 339 | } 340 | } 341 | //refresh the bounds when draging 342 | this._poly._bounds._southWest = L.latLng(Infinity, Infinity); 343 | this._poly._bounds._northEast = L.latLng(-Infinity, -Infinity); 344 | var latlngs = this._poly.getLatLngs(); 345 | this._poly._convertLatLngs(latlngs, true); 346 | this._poly.redraw(); 347 | this._poly.fire('editdrag'); 348 | }, 349 | 350 | _onMarkerClick: function (e) { 351 | 352 | var minPoints = L.Polygon && (this._poly instanceof L.Polygon) ? 4 : 3, 353 | marker = e.target; 354 | 355 | // If removing this point would create an invalid polyline/polygon don't remove 356 | if (this._defaultShape().length < minPoints) { 357 | return; 358 | } 359 | 360 | // remove the marker 361 | this._removeMarker(marker); 362 | 363 | // update prev/next links of adjacent markers 364 | this._updatePrevNext(marker._prev, marker._next); 365 | 366 | // remove ghost markers near the removed marker 367 | if (marker._middleLeft) { 368 | this._markerGroup.removeLayer(marker._middleLeft); 369 | } 370 | if (marker._middleRight) { 371 | this._markerGroup.removeLayer(marker._middleRight); 372 | } 373 | 374 | // create a ghost marker in place of the removed one 375 | if (marker._prev && marker._next) { 376 | this._createMiddleMarker(marker._prev, marker._next); 377 | 378 | } else if (!marker._prev) { 379 | marker._next._middleLeft = null; 380 | 381 | } else if (!marker._next) { 382 | marker._prev._middleRight = null; 383 | } 384 | 385 | this._fireEdit(); 386 | }, 387 | 388 | _onContextMenu: function (e) { 389 | var marker = e.target; 390 | var poly = this._poly; 391 | this._poly._map.fire(L.Draw.Event.MARKERCONTEXT, {marker: marker, layers: this._markerGroup, poly: this._poly}); 392 | L.DomEvent.stopPropagation; 393 | }, 394 | 395 | _onTouchMove: function (e) { 396 | 397 | var layerPoint = this._map.mouseEventToLayerPoint(e.originalEvent.touches[0]), 398 | latlng = this._map.layerPointToLatLng(layerPoint), 399 | marker = e.target; 400 | 401 | L.extend(marker._origLatLng, latlng); 402 | 403 | if (marker._middleLeft) { 404 | marker._middleLeft.setLatLng(this._getMiddleLatLng(marker._prev, marker)); 405 | } 406 | if (marker._middleRight) { 407 | marker._middleRight.setLatLng(this._getMiddleLatLng(marker, marker._next)); 408 | } 409 | 410 | this._poly.redraw(); 411 | this.updateMarkers(); 412 | }, 413 | 414 | _updateIndexes: function (index, delta) { 415 | this._markerGroup.eachLayer(function (marker) { 416 | if (marker._index > index) { 417 | marker._index += delta; 418 | } 419 | }); 420 | }, 421 | 422 | _createMiddleMarker: function (marker1, marker2) { 423 | var latlng = this._getMiddleLatLng(marker1, marker2), 424 | marker = this._createMarker(latlng), 425 | onClick, 426 | onDragStart, 427 | onDragEnd; 428 | 429 | marker.setOpacity(0.6); 430 | 431 | marker1._middleRight = marker2._middleLeft = marker; 432 | 433 | onDragStart = function () { 434 | marker.off('touchmove', onDragStart, this); 435 | var i = marker2._index; 436 | 437 | marker._index = i; 438 | 439 | marker 440 | .off('click', onClick, this) 441 | .on('click', this._onMarkerClick, this); 442 | 443 | latlng.lat = marker.getLatLng().lat; 444 | latlng.lng = marker.getLatLng().lng; 445 | this._spliceLatLngs(i, 0, latlng); 446 | this._markers.splice(i, 0, marker); 447 | 448 | marker.setOpacity(1); 449 | 450 | this._updateIndexes(i, 1); 451 | marker2._index++; 452 | this._updatePrevNext(marker1, marker); 453 | this._updatePrevNext(marker, marker2); 454 | 455 | this._poly.fire('editstart'); 456 | }; 457 | 458 | onDragEnd = function () { 459 | marker.off('dragstart', onDragStart, this); 460 | marker.off('dragend', onDragEnd, this); 461 | marker.off('touchmove', onDragStart, this); 462 | 463 | this._createMiddleMarker(marker1, marker); 464 | this._createMiddleMarker(marker, marker2); 465 | }; 466 | 467 | onClick = function () { 468 | onDragStart.call(this); 469 | onDragEnd.call(this); 470 | this._fireEdit(); 471 | }; 472 | 473 | marker 474 | .on('click', onClick, this) 475 | .on('dragstart', onDragStart, this) 476 | .on('dragend', onDragEnd, this) 477 | .on('touchmove', onDragStart, this); 478 | 479 | this._markerGroup.addLayer(marker); 480 | }, 481 | 482 | _updatePrevNext: function (marker1, marker2) { 483 | if (marker1) { 484 | marker1._next = marker2; 485 | } 486 | if (marker2) { 487 | marker2._prev = marker1; 488 | } 489 | }, 490 | 491 | _getMiddleLatLng: function (marker1, marker2) { 492 | var map = this._poly._map, 493 | p1 = map.project(marker1.getLatLng()), 494 | p2 = map.project(marker2.getLatLng()); 495 | 496 | return map.unproject(p1._add(p2)._divideBy(2)); 497 | } 498 | }); 499 | 500 | L.Polyline.addInitHook(function () { 501 | 502 | // Check to see if handler has already been initialized. This is to support versions of Leaflet that still have L.Handler.PolyEdit 503 | if (this.editing) { 504 | return; 505 | } 506 | 507 | if (L.Edit.Poly) { 508 | 509 | this.editing = new L.Edit.Poly(this); 510 | 511 | if (this.options.editable) { 512 | this.editing.enable(); 513 | } 514 | } 515 | 516 | this.on('add', function () { 517 | if (this.editing && this.editing.enabled()) { 518 | this.editing.addHooks(); 519 | } 520 | }); 521 | 522 | this.on('remove', function () { 523 | if (this.editing && this.editing.enabled()) { 524 | this.editing.removeHooks(); 525 | } 526 | }); 527 | }); 528 | -------------------------------------------------------------------------------- /static_files/draw_files/Edit.Rectangle.js: -------------------------------------------------------------------------------- 1 | L.Edit = L.Edit || {}; 2 | /** 3 | * @class L.Edit.Rectangle 4 | * @aka Edit.Rectangle 5 | * @inherits L.Edit.SimpleShape 6 | */ 7 | L.Edit.Rectangle = L.Edit.SimpleShape.extend({ 8 | _createMoveMarker: function () { 9 | var bounds = this._shape.getBounds(), 10 | center = bounds.getCenter(); 11 | 12 | this._moveMarker = this._createMarker(center, this.options.moveIcon); 13 | }, 14 | 15 | _createResizeMarker: function () { 16 | var corners = this._getCorners(); 17 | 18 | this._resizeMarkers = []; 19 | 20 | for (var i = 0, l = corners.length; i < l; i++) { 21 | this._resizeMarkers.push(this._createMarker(corners[i], this.options.resizeIcon)); 22 | // Monkey in the corner index as we will need to know this for dragging 23 | this._resizeMarkers[i]._cornerIndex = i; 24 | } 25 | }, 26 | 27 | _onMarkerDragStart: function (e) { 28 | L.Edit.SimpleShape.prototype._onMarkerDragStart.call(this, e); 29 | 30 | // Save a reference to the opposite point 31 | var corners = this._getCorners(), 32 | marker = e.target, 33 | currentCornerIndex = marker._cornerIndex; 34 | 35 | this._oppositeCorner = corners[(currentCornerIndex + 2) % 4]; 36 | 37 | this._toggleCornerMarkers(0, currentCornerIndex); 38 | }, 39 | 40 | _onMarkerDragEnd: function (e) { 41 | var marker = e.target, 42 | bounds, center; 43 | 44 | // Reset move marker position to the center 45 | if (marker === this._moveMarker) { 46 | bounds = this._shape.getBounds(); 47 | center = bounds.getCenter(); 48 | 49 | marker.setLatLng(center); 50 | } 51 | 52 | this._toggleCornerMarkers(1); 53 | 54 | this._repositionCornerMarkers(); 55 | 56 | L.Edit.SimpleShape.prototype._onMarkerDragEnd.call(this, e); 57 | }, 58 | 59 | _move: function (newCenter) { 60 | var latlngs = this._shape._defaultShape ? this._shape._defaultShape() : this._shape.getLatLngs(), 61 | bounds = this._shape.getBounds(), 62 | center = bounds.getCenter(), 63 | offset, newLatLngs = []; 64 | 65 | // Offset the latlngs to the new center 66 | for (var i = 0, l = latlngs.length; i < l; i++) { 67 | offset = [latlngs[i].lat - center.lat, latlngs[i].lng - center.lng]; 68 | newLatLngs.push([newCenter.lat + offset[0], newCenter.lng + offset[1]]); 69 | } 70 | 71 | this._shape.setLatLngs(newLatLngs); 72 | 73 | // Reposition the resize markers 74 | this._repositionCornerMarkers(); 75 | 76 | this._map.fire(L.Draw.Event.EDITMOVE, {layer: this._shape}); 77 | }, 78 | 79 | _resize: function (latlng) { 80 | var bounds; 81 | 82 | // Update the shape based on the current position of this corner and the opposite point 83 | this._shape.setBounds(L.latLngBounds(latlng, this._oppositeCorner)); 84 | 85 | // Reposition the move marker 86 | bounds = this._shape.getBounds(); 87 | this._moveMarker.setLatLng(bounds.getCenter()); 88 | 89 | this._map.fire(L.Draw.Event.EDITRESIZE, {layer: this._shape}); 90 | }, 91 | 92 | _getCorners: function () { 93 | var bounds = this._shape.getBounds(), 94 | nw = bounds.getNorthWest(), 95 | ne = bounds.getNorthEast(), 96 | se = bounds.getSouthEast(), 97 | sw = bounds.getSouthWest(); 98 | 99 | return [nw, ne, se, sw]; 100 | }, 101 | 102 | _toggleCornerMarkers: function (opacity) { 103 | for (var i = 0, l = this._resizeMarkers.length; i < l; i++) { 104 | this._resizeMarkers[i].setOpacity(opacity); 105 | } 106 | }, 107 | 108 | _repositionCornerMarkers: function () { 109 | var corners = this._getCorners(); 110 | 111 | for (var i = 0, l = this._resizeMarkers.length; i < l; i++) { 112 | this._resizeMarkers[i].setLatLng(corners[i]); 113 | } 114 | } 115 | }); 116 | 117 | L.Rectangle.addInitHook(function () { 118 | if (L.Edit.Rectangle) { 119 | this.editing = new L.Edit.Rectangle(this); 120 | 121 | if (this.options.editable) { 122 | this.editing.enable(); 123 | } 124 | } 125 | }); 126 | -------------------------------------------------------------------------------- /static_files/draw_files/Edit.SimpleShape.js: -------------------------------------------------------------------------------- 1 | L.Edit = L.Edit || {}; 2 | /** 3 | * @class L.Edit.SimpleShape 4 | * @aka Edit.SimpleShape 5 | */ 6 | L.Edit.SimpleShape = L.Handler.extend({ 7 | options: { 8 | moveIcon: new L.DivIcon({ 9 | iconSize: new L.Point(8, 8), 10 | className: 'leaflet-div-icon leaflet-editing-icon leaflet-edit-move' 11 | }), 12 | resizeIcon: new L.DivIcon({ 13 | iconSize: new L.Point(8, 8), 14 | className: 'leaflet-div-icon leaflet-editing-icon leaflet-edit-resize' 15 | }), 16 | touchMoveIcon: new L.DivIcon({ 17 | iconSize: new L.Point(20, 20), 18 | className: 'leaflet-div-icon leaflet-editing-icon leaflet-edit-move leaflet-touch-icon' 19 | }), 20 | touchResizeIcon: new L.DivIcon({ 21 | iconSize: new L.Point(20, 20), 22 | className: 'leaflet-div-icon leaflet-editing-icon leaflet-edit-resize leaflet-touch-icon' 23 | }), 24 | }, 25 | 26 | // @method intialize(): void 27 | initialize: function (shape, options) { 28 | // if touch, switch to touch icon 29 | if (L.Browser.touch) { 30 | this.options.moveIcon = this.options.touchMoveIcon; 31 | this.options.resizeIcon = this.options.touchResizeIcon; 32 | } 33 | 34 | this._shape = shape; 35 | L.Util.setOptions(this, options); 36 | }, 37 | 38 | // @method addHooks(): void 39 | // Add listener hooks to this handler 40 | addHooks: function () { 41 | var shape = this._shape; 42 | if (this._shape._map) { 43 | this._map = this._shape._map; 44 | shape.setStyle(shape.options.editing); 45 | 46 | if (shape._map) { 47 | this._map = shape._map; 48 | if (!this._markerGroup) { 49 | this._initMarkers(); 50 | } 51 | this._map.addLayer(this._markerGroup); 52 | } 53 | } 54 | }, 55 | 56 | // @method removeHooks(): void 57 | // Remove listener hooks from this handler 58 | removeHooks: function () { 59 | var shape = this._shape; 60 | 61 | shape.setStyle(shape.options.original); 62 | 63 | if (shape._map) { 64 | this._unbindMarker(this._moveMarker); 65 | 66 | for (var i = 0, l = this._resizeMarkers.length; i < l; i++) { 67 | this._unbindMarker(this._resizeMarkers[i]); 68 | } 69 | this._resizeMarkers = null; 70 | 71 | this._map.removeLayer(this._markerGroup); 72 | delete this._markerGroup; 73 | } 74 | 75 | this._map = null; 76 | }, 77 | 78 | // @method updateMarkers(): void 79 | // Remove the edit markers from this layer 80 | updateMarkers: function () { 81 | this._markerGroup.clearLayers(); 82 | this._initMarkers(); 83 | }, 84 | 85 | _initMarkers: function () { 86 | if (!this._markerGroup) { 87 | this._markerGroup = new L.LayerGroup(); 88 | } 89 | 90 | // Create center marker 91 | this._createMoveMarker(); 92 | 93 | // Create edge marker 94 | this._createResizeMarker(); 95 | }, 96 | 97 | _createMoveMarker: function () { 98 | // Children override 99 | }, 100 | 101 | _createResizeMarker: function () { 102 | // Children override 103 | }, 104 | 105 | _createMarker: function (latlng, icon) { 106 | // Extending L.Marker in TouchEvents.js to include touch. 107 | var marker = new L.Marker.Touch(latlng, { 108 | draggable: true, 109 | icon: icon, 110 | zIndexOffset: 10 111 | }); 112 | 113 | this._bindMarker(marker); 114 | 115 | this._markerGroup.addLayer(marker); 116 | 117 | return marker; 118 | }, 119 | 120 | _bindMarker: function (marker) { 121 | marker 122 | .on('dragstart', this._onMarkerDragStart, this) 123 | .on('drag', this._onMarkerDrag, this) 124 | .on('dragend', this._onMarkerDragEnd, this) 125 | .on('touchstart', this._onTouchStart, this) 126 | .on('touchmove', this._onTouchMove, this) 127 | .on('MSPointerMove', this._onTouchMove, this) 128 | .on('touchend', this._onTouchEnd, this) 129 | .on('MSPointerUp', this._onTouchEnd, this); 130 | }, 131 | 132 | _unbindMarker: function (marker) { 133 | marker 134 | .off('dragstart', this._onMarkerDragStart, this) 135 | .off('drag', this._onMarkerDrag, this) 136 | .off('dragend', this._onMarkerDragEnd, this) 137 | .off('touchstart', this._onTouchStart, this) 138 | .off('touchmove', this._onTouchMove, this) 139 | .off('MSPointerMove', this._onTouchMove, this) 140 | .off('touchend', this._onTouchEnd, this) 141 | .off('MSPointerUp', this._onTouchEnd, this); 142 | }, 143 | 144 | _onMarkerDragStart: function (e) { 145 | var marker = e.target; 146 | marker.setOpacity(0); 147 | 148 | this._shape.fire('editstart'); 149 | }, 150 | 151 | _fireEdit: function () { 152 | this._shape.edited = true; 153 | this._shape.fire('edit'); 154 | }, 155 | 156 | _onMarkerDrag: function (e) { 157 | var marker = e.target, 158 | latlng = marker.getLatLng(); 159 | 160 | if (marker === this._moveMarker) { 161 | this._move(latlng); 162 | } else { 163 | this._resize(latlng); 164 | } 165 | 166 | this._shape.redraw(); 167 | this._shape.fire('editdrag'); 168 | }, 169 | 170 | _onMarkerDragEnd: function (e) { 171 | var marker = e.target; 172 | marker.setOpacity(1); 173 | 174 | this._fireEdit(); 175 | }, 176 | 177 | _onTouchStart: function (e) { 178 | L.Edit.SimpleShape.prototype._onMarkerDragStart.call(this, e); 179 | 180 | if (typeof(this._getCorners) === 'function') { 181 | // Save a reference to the opposite point 182 | var corners = this._getCorners(), 183 | marker = e.target, 184 | currentCornerIndex = marker._cornerIndex; 185 | 186 | marker.setOpacity(0); 187 | 188 | // Copyed from Edit.Rectangle.js line 23 _onMarkerDragStart() 189 | // Latlng is null otherwise. 190 | this._oppositeCorner = corners[(currentCornerIndex + 2) % 4]; 191 | this._toggleCornerMarkers(0, currentCornerIndex); 192 | } 193 | 194 | this._shape.fire('editstart'); 195 | }, 196 | 197 | _onTouchMove: function (e) { 198 | var layerPoint = this._map.mouseEventToLayerPoint(e.originalEvent.touches[0]), 199 | latlng = this._map.layerPointToLatLng(layerPoint), 200 | marker = e.target; 201 | 202 | if (marker === this._moveMarker) { 203 | this._move(latlng); 204 | } else { 205 | this._resize(latlng); 206 | } 207 | 208 | this._shape.redraw(); 209 | 210 | // prevent touchcancel in IOS 211 | // e.preventDefault(); 212 | return false; 213 | }, 214 | 215 | _onTouchEnd: function (e) { 216 | var marker = e.target; 217 | marker.setOpacity(1); 218 | this.updateMarkers(); 219 | this._fireEdit(); 220 | }, 221 | 222 | _move: function () { 223 | // Children override 224 | }, 225 | 226 | _resize: function () { 227 | // Children override 228 | } 229 | }); 230 | -------------------------------------------------------------------------------- /static_files/draw_files/EditToolbar.Delete.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @class L.EditToolbar.Delete 3 | * @aka EditToolbar.Delete 4 | */ 5 | L.EditToolbar.Delete = L.Handler.extend({ 6 | statics: { 7 | TYPE: 'remove' // not delete as delete is reserved in js 8 | }, 9 | 10 | // @method intialize(): void 11 | initialize: function (map, options) { 12 | L.Handler.prototype.initialize.call(this, map); 13 | 14 | L.Util.setOptions(this, options); 15 | 16 | // Store the selectable layer group for ease of access 17 | this._deletableLayers = this.options.featureGroup; 18 | 19 | if (!(this._deletableLayers instanceof L.FeatureGroup)) { 20 | throw new Error('options.featureGroup must be a L.FeatureGroup'); 21 | } 22 | 23 | // Save the type so super can fire, need to do this as cannot do this.TYPE :( 24 | this.type = L.EditToolbar.Delete.TYPE; 25 | 26 | var version = L.version.split('.'); 27 | //If Version is >= 1.2.0 28 | if (parseInt(version[0], 10) === 1 && parseInt(version[1], 10) >= 2) { 29 | L.EditToolbar.Delete.include(L.Evented.prototype); 30 | } else { 31 | L.EditToolbar.Delete.include(L.Mixin.Events); 32 | } 33 | 34 | }, 35 | 36 | // @method enable(): void 37 | // Enable the delete toolbar 38 | enable: function () { 39 | if (this._enabled || !this._hasAvailableLayers()) { 40 | return; 41 | } 42 | this.fire('enabled', {handler: this.type}); 43 | 44 | this._map.fire(L.Draw.Event.DELETESTART, {handler: this.type}); 45 | 46 | L.Handler.prototype.enable.call(this); 47 | 48 | this._deletableLayers 49 | .on('layeradd', this._enableLayerDelete, this) 50 | .on('layerremove', this._disableLayerDelete, this); 51 | }, 52 | 53 | // @method disable(): void 54 | // Disable the delete toolbar 55 | disable: function () { 56 | if (!this._enabled) { 57 | return; 58 | } 59 | 60 | this._deletableLayers 61 | .off('layeradd', this._enableLayerDelete, this) 62 | .off('layerremove', this._disableLayerDelete, this); 63 | 64 | L.Handler.prototype.disable.call(this); 65 | 66 | this._map.fire(L.Draw.Event.DELETESTOP, {handler: this.type}); 67 | 68 | this.fire('disabled', {handler: this.type}); 69 | }, 70 | 71 | // @method addHooks(): void 72 | // Add listener hooks to this handler 73 | addHooks: function () { 74 | var map = this._map; 75 | 76 | if (map) { 77 | map.getContainer().focus(); 78 | 79 | this._deletableLayers.eachLayer(this._enableLayerDelete, this); 80 | this._deletedLayers = new L.LayerGroup(); 81 | 82 | this._tooltip = new L.Draw.Tooltip(this._map); 83 | this._tooltip.updateContent({text: L.drawLocal.edit.handlers.remove.tooltip.text}); 84 | 85 | this._map.on('mousemove', this._onMouseMove, this); 86 | } 87 | }, 88 | 89 | // @method removeHooks(): void 90 | // Remove listener hooks from this handler 91 | removeHooks: function () { 92 | if (this._map) { 93 | this._deletableLayers.eachLayer(this._disableLayerDelete, this); 94 | this._deletedLayers = null; 95 | 96 | this._tooltip.dispose(); 97 | this._tooltip = null; 98 | 99 | this._map.off('mousemove', this._onMouseMove, this); 100 | } 101 | }, 102 | 103 | // @method revertLayers(): void 104 | // Revert the deleted layers back to their prior state. 105 | revertLayers: function () { 106 | // Iterate of the deleted layers and add them back into the featureGroup 107 | this._deletedLayers.eachLayer(function (layer) { 108 | this._deletableLayers.addLayer(layer); 109 | layer.fire('revert-deleted', {layer: layer}); 110 | }, this); 111 | }, 112 | 113 | // @method save(): void 114 | // Save deleted layers 115 | save: function () { 116 | this._map.fire(L.Draw.Event.DELETED, {layers: this._deletedLayers}); 117 | }, 118 | 119 | // @method removeAllLayers(): void 120 | // Remove all delateable layers 121 | removeAllLayers: function () { 122 | // Iterate of the delateable layers and add remove them 123 | this._deletableLayers.eachLayer(function (layer) { 124 | this._removeLayer({layer: layer}); 125 | }, this); 126 | this.save(); 127 | }, 128 | 129 | _enableLayerDelete: function (e) { 130 | var layer = e.layer || e.target || e; 131 | 132 | layer.on('click', this._removeLayer, this); 133 | }, 134 | 135 | _disableLayerDelete: function (e) { 136 | var layer = e.layer || e.target || e; 137 | 138 | layer.off('click', this._removeLayer, this); 139 | 140 | // Remove from the deleted layers so we can't accidentally revert if the user presses cancel 141 | this._deletedLayers.removeLayer(layer); 142 | }, 143 | 144 | _removeLayer: function (e) { 145 | var layer = e.layer || e.target || e; 146 | 147 | this._deletableLayers.removeLayer(layer); 148 | 149 | this._deletedLayers.addLayer(layer); 150 | 151 | layer.fire('deleted'); 152 | }, 153 | 154 | _onMouseMove: function (e) { 155 | this._tooltip.updatePosition(e.latlng); 156 | }, 157 | 158 | _hasAvailableLayers: function () { 159 | return this._deletableLayers.getLayers().length !== 0; 160 | } 161 | }); 162 | -------------------------------------------------------------------------------- /static_files/draw_files/EditToolbar.Edit.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @class L.EditToolbar.Edit 3 | * @aka EditToolbar.Edit 4 | */ 5 | L.EditToolbar.Edit = L.Handler.extend({ 6 | statics: { 7 | TYPE: 'edit' 8 | }, 9 | 10 | // @method intialize(): void 11 | initialize: function (map, options) { 12 | L.Handler.prototype.initialize.call(this, map); 13 | 14 | L.setOptions(this, options); 15 | 16 | // Store the selectable layer group for ease of access 17 | this._featureGroup = options.featureGroup; 18 | 19 | if (!(this._featureGroup instanceof L.FeatureGroup)) { 20 | throw new Error('options.featureGroup must be a L.FeatureGroup'); 21 | } 22 | 23 | this._uneditedLayerProps = {}; 24 | 25 | // Save the type so super can fire, need to do this as cannot do this.TYPE :( 26 | this.type = L.EditToolbar.Edit.TYPE; 27 | 28 | var version = L.version.split('.'); 29 | //If Version is >= 1.2.0 30 | if (parseInt(version[0], 10) === 1 && parseInt(version[1], 10) >= 2) { 31 | L.EditToolbar.Edit.include(L.Evented.prototype); 32 | } else { 33 | L.EditToolbar.Edit.include(L.Mixin.Events); 34 | } 35 | }, 36 | 37 | // @method enable(): void 38 | // Enable the edit toolbar 39 | enable: function () { 40 | if (this._enabled || !this._hasAvailableLayers()) { 41 | return; 42 | } 43 | this.fire('enabled', {handler: this.type}); 44 | //this disable other handlers 45 | 46 | this._map.fire(L.Draw.Event.EDITSTART, {handler: this.type}); 47 | //allow drawLayer to be updated before beginning edition. 48 | 49 | L.Handler.prototype.enable.call(this); 50 | this._featureGroup 51 | .on('layeradd', this._enableLayerEdit, this) 52 | .on('layerremove', this._disableLayerEdit, this); 53 | }, 54 | 55 | // @method disable(): void 56 | // Disable the edit toolbar 57 | disable: function () { 58 | if (!this._enabled) { 59 | return; 60 | } 61 | this._featureGroup 62 | .off('layeradd', this._enableLayerEdit, this) 63 | .off('layerremove', this._disableLayerEdit, this); 64 | L.Handler.prototype.disable.call(this); 65 | this._map.fire(L.Draw.Event.EDITSTOP, {handler: this.type}); 66 | this.fire('disabled', {handler: this.type}); 67 | }, 68 | 69 | // @method addHooks(): void 70 | // Add listener hooks for this handler 71 | addHooks: function () { 72 | var map = this._map; 73 | 74 | if (map) { 75 | map.getContainer().focus(); 76 | 77 | this._featureGroup.eachLayer(this._enableLayerEdit, this); 78 | 79 | this._tooltip = new L.Draw.Tooltip(this._map); 80 | this._tooltip.updateContent({ 81 | text: L.drawLocal.edit.handlers.edit.tooltip.text, 82 | subtext: L.drawLocal.edit.handlers.edit.tooltip.subtext 83 | }); 84 | 85 | // Quickly access the tooltip to update for intersection checking 86 | map._editTooltip = this._tooltip; 87 | 88 | this._updateTooltip(); 89 | 90 | this._map 91 | .on('mousemove', this._onMouseMove, this) 92 | .on('touchmove', this._onMouseMove, this) 93 | .on('MSPointerMove', this._onMouseMove, this) 94 | .on(L.Draw.Event.EDITVERTEX, this._updateTooltip, this); 95 | } 96 | }, 97 | 98 | // @method removeHooks(): void 99 | // Remove listener hooks for this handler 100 | removeHooks: function () { 101 | if (this._map) { 102 | // Clean up selected layers. 103 | this._featureGroup.eachLayer(this._disableLayerEdit, this); 104 | 105 | // Clear the backups of the original layers 106 | this._uneditedLayerProps = {}; 107 | 108 | this._tooltip.dispose(); 109 | this._tooltip = null; 110 | 111 | this._map 112 | .off('mousemove', this._onMouseMove, this) 113 | .off('touchmove', this._onMouseMove, this) 114 | .off('MSPointerMove', this._onMouseMove, this) 115 | .off(L.Draw.Event.EDITVERTEX, this._updateTooltip, this); 116 | } 117 | }, 118 | 119 | // @method revertLayers(): void 120 | // Revert each layer's geometry changes 121 | revertLayers: function () { 122 | this._featureGroup.eachLayer(function (layer) { 123 | this._revertLayer(layer); 124 | }, this); 125 | }, 126 | 127 | // @method save(): void 128 | // Save the layer geometries 129 | save: function () { 130 | var editedLayers = new L.LayerGroup(); 131 | this._featureGroup.eachLayer(function (layer) { 132 | if (layer.edited) { 133 | editedLayers.addLayer(layer); 134 | layer.edited = false; 135 | } 136 | }); 137 | this._map.fire(L.Draw.Event.EDITED, {layers: editedLayers}); 138 | }, 139 | 140 | _backupLayer: function (layer) { 141 | var id = L.Util.stamp(layer); 142 | 143 | if (!this._uneditedLayerProps[id]) { 144 | // Polyline, Polygon or Rectangle 145 | if (layer instanceof L.Polyline || layer instanceof L.Polygon || layer instanceof L.Rectangle) { 146 | this._uneditedLayerProps[id] = { 147 | latlngs: L.LatLngUtil.cloneLatLngs(layer.getLatLngs()) 148 | }; 149 | } else if (layer instanceof L.Circle) { 150 | this._uneditedLayerProps[id] = { 151 | latlng: L.LatLngUtil.cloneLatLng(layer.getLatLng()), 152 | radius: layer.getRadius() 153 | }; 154 | } else if (layer instanceof L.Marker || layer instanceof L.CircleMarker) { // Marker 155 | this._uneditedLayerProps[id] = { 156 | latlng: L.LatLngUtil.cloneLatLng(layer.getLatLng()) 157 | }; 158 | } 159 | } 160 | }, 161 | 162 | _getTooltipText: function () { 163 | return ({ 164 | text: L.drawLocal.edit.handlers.edit.tooltip.text, 165 | subtext: L.drawLocal.edit.handlers.edit.tooltip.subtext 166 | }); 167 | }, 168 | 169 | _updateTooltip: function () { 170 | this._tooltip.updateContent(this._getTooltipText()); 171 | }, 172 | 173 | _revertLayer: function (layer) { 174 | var id = L.Util.stamp(layer); 175 | layer.edited = false; 176 | if (this._uneditedLayerProps.hasOwnProperty(id)) { 177 | // Polyline, Polygon or Rectangle 178 | if (layer instanceof L.Polyline || layer instanceof L.Polygon || layer instanceof L.Rectangle) { 179 | layer.setLatLngs(this._uneditedLayerProps[id].latlngs); 180 | } else if (layer instanceof L.Circle) { 181 | layer.setLatLng(this._uneditedLayerProps[id].latlng); 182 | layer.setRadius(this._uneditedLayerProps[id].radius); 183 | } else if (layer instanceof L.Marker || layer instanceof L.CircleMarker) { // Marker or CircleMarker 184 | layer.setLatLng(this._uneditedLayerProps[id].latlng); 185 | } 186 | 187 | layer.fire('revert-edited', {layer: layer}); 188 | } 189 | }, 190 | 191 | _enableLayerEdit: function (e) { 192 | var layer = e.layer || e.target || e, 193 | pathOptions, poly; 194 | 195 | // Back up this layer (if haven't before) 196 | this._backupLayer(layer); 197 | 198 | if (this.options.poly) { 199 | poly = L.Util.extend({}, this.options.poly); 200 | layer.options.poly = poly; 201 | } 202 | 203 | // Set different style for editing mode 204 | if (this.options.selectedPathOptions) { 205 | pathOptions = L.Util.extend({}, this.options.selectedPathOptions); 206 | 207 | // Use the existing color of the layer 208 | if (pathOptions.maintainColor) { 209 | pathOptions.color = layer.options.color; 210 | pathOptions.fillColor = layer.options.fillColor; 211 | } 212 | 213 | layer.options.original = L.extend({}, layer.options); 214 | layer.options.editing = pathOptions; 215 | 216 | } 217 | 218 | if (layer instanceof L.Marker) { 219 | if (layer.editing) { 220 | layer.editing.enable(); 221 | } 222 | layer.dragging.enable(); 223 | layer 224 | .on('dragend', this._onMarkerDragEnd) 225 | // #TODO: remove when leaflet finally fixes their draggable so it's touch friendly again. 226 | .on('touchmove', this._onTouchMove, this) 227 | .on('MSPointerMove', this._onTouchMove, this) 228 | .on('touchend', this._onMarkerDragEnd, this) 229 | .on('MSPointerUp', this._onMarkerDragEnd, this); 230 | } else { 231 | layer.editing.enable(); 232 | } 233 | }, 234 | 235 | _disableLayerEdit: function (e) { 236 | var layer = e.layer || e.target || e; 237 | 238 | layer.edited = false; 239 | if (layer.editing) { 240 | layer.editing.disable(); 241 | } 242 | 243 | delete layer.options.editing; 244 | delete layer.options.original; 245 | // Reset layer styles to that of before select 246 | if (this._selectedPathOptions) { 247 | if (layer instanceof L.Marker) { 248 | this._toggleMarkerHighlight(layer); 249 | } else { 250 | // reset the layer style to what is was before being selected 251 | layer.setStyle(layer.options.previousOptions); 252 | // remove the cached options for the layer object 253 | delete layer.options.previousOptions; 254 | } 255 | } 256 | 257 | if (layer instanceof L.Marker) { 258 | layer.dragging.disable(); 259 | layer 260 | .off('dragend', this._onMarkerDragEnd, this) 261 | .off('touchmove', this._onTouchMove, this) 262 | .off('MSPointerMove', this._onTouchMove, this) 263 | .off('touchend', this._onMarkerDragEnd, this) 264 | .off('MSPointerUp', this._onMarkerDragEnd, this); 265 | } else { 266 | layer.editing.disable(); 267 | } 268 | }, 269 | 270 | _onMouseMove: function (e) { 271 | this._tooltip.updatePosition(e.latlng); 272 | }, 273 | 274 | _onMarkerDragEnd: function (e) { 275 | var layer = e.target; 276 | layer.edited = true; 277 | this._map.fire(L.Draw.Event.EDITMOVE, {layer: layer}); 278 | }, 279 | 280 | _onTouchMove: function (e) { 281 | var touchEvent = e.originalEvent.changedTouches[0], 282 | layerPoint = this._map.mouseEventToLayerPoint(touchEvent), 283 | latlng = this._map.layerPointToLatLng(layerPoint); 284 | e.target.setLatLng(latlng); 285 | }, 286 | 287 | _hasAvailableLayers: function () { 288 | return this._featureGroup.getLayers().length !== 0; 289 | } 290 | }); 291 | -------------------------------------------------------------------------------- /static_files/draw_files/EditToolbar.js: -------------------------------------------------------------------------------- 1 | /*L.Map.mergeOptions({ 2 | editControl: true 3 | });*/ 4 | /** 5 | * @class L.EditToolbar 6 | * @aka EditToolbar 7 | */ 8 | L.EditToolbar = L.Toolbar.extend({ 9 | statics: { 10 | TYPE: 'edit' 11 | }, 12 | 13 | options: { 14 | edit: { 15 | selectedPathOptions: { 16 | dashArray: '10, 10', 17 | 18 | fill: true, 19 | fillColor: '#fe57a1', 20 | fillOpacity: 0.1, 21 | 22 | // Whether to user the existing layers color 23 | maintainColor: false 24 | } 25 | }, 26 | remove: {}, 27 | poly: null, 28 | featureGroup: null /* REQUIRED! TODO: perhaps if not set then all layers on the map are selectable? */ 29 | }, 30 | 31 | // @method intialize(): void 32 | initialize: function (options) { 33 | // Need to set this manually since null is an acceptable value here 34 | if (options.edit) { 35 | if (typeof options.edit.selectedPathOptions === 'undefined') { 36 | options.edit.selectedPathOptions = this.options.edit.selectedPathOptions; 37 | } 38 | options.edit.selectedPathOptions = L.extend({}, this.options.edit.selectedPathOptions, options.edit.selectedPathOptions); 39 | } 40 | 41 | if (options.remove) { 42 | options.remove = L.extend({}, this.options.remove, options.remove); 43 | } 44 | 45 | if (options.poly) { 46 | options.poly = L.extend({}, this.options.poly, options.poly); 47 | } 48 | 49 | this._toolbarClass = 'leaflet-draw-edit'; 50 | L.Toolbar.prototype.initialize.call(this, options); 51 | 52 | this._selectedFeatureCount = 0; 53 | }, 54 | 55 | // @method getModeHandlers(): object 56 | // Get mode handlers information 57 | getModeHandlers: function (map) { 58 | var featureGroup = this.options.featureGroup; 59 | return [ 60 | { 61 | enabled: this.options.edit, 62 | handler: new L.EditToolbar.Edit(map, { 63 | featureGroup: featureGroup, 64 | selectedPathOptions: this.options.edit.selectedPathOptions, 65 | poly: this.options.poly 66 | }), 67 | title: L.drawLocal.edit.toolbar.buttons.edit 68 | }, 69 | { 70 | enabled: this.options.remove, 71 | handler: new L.EditToolbar.Delete(map, { 72 | featureGroup: featureGroup 73 | }), 74 | title: L.drawLocal.edit.toolbar.buttons.remove 75 | } 76 | ]; 77 | }, 78 | 79 | // @method getActions(): object 80 | // Get actions information 81 | getActions: function (handler) { 82 | var actions = [ 83 | { 84 | title: L.drawLocal.edit.toolbar.actions.save.title, 85 | text: L.drawLocal.edit.toolbar.actions.save.text, 86 | callback: this._save, 87 | context: this 88 | }, 89 | { 90 | title: L.drawLocal.edit.toolbar.actions.cancel.title, 91 | text: L.drawLocal.edit.toolbar.actions.cancel.text, 92 | callback: this.disable, 93 | context: this 94 | } 95 | ]; 96 | 97 | if (handler.removeAllLayers) { 98 | actions.push({ 99 | title: L.drawLocal.edit.toolbar.actions.clearAll.title, 100 | text: L.drawLocal.edit.toolbar.actions.clearAll.text, 101 | callback: this._clearAllLayers, 102 | context: this 103 | }); 104 | } 105 | 106 | return actions; 107 | }, 108 | 109 | // @method addToolbar(map): L.DomUtil 110 | // Adds the toolbar to the map 111 | addToolbar: function (map) { 112 | var container = L.Toolbar.prototype.addToolbar.call(this, map); 113 | 114 | this._checkDisabled(); 115 | 116 | this.options.featureGroup.on('layeradd layerremove', this._checkDisabled, this); 117 | 118 | return container; 119 | }, 120 | 121 | // @method removeToolbar(): void 122 | // Removes the toolbar from the map 123 | removeToolbar: function () { 124 | this.options.featureGroup.off('layeradd layerremove', this._checkDisabled, this); 125 | 126 | L.Toolbar.prototype.removeToolbar.call(this); 127 | }, 128 | 129 | // @method disable(): void 130 | // Disables the toolbar 131 | disable: function () { 132 | if (!this.enabled()) { 133 | return; 134 | } 135 | 136 | this._activeMode.handler.revertLayers(); 137 | 138 | L.Toolbar.prototype.disable.call(this); 139 | }, 140 | 141 | _save: function () { 142 | this._activeMode.handler.save(); 143 | if (this._activeMode) { 144 | this._activeMode.handler.disable(); 145 | } 146 | }, 147 | 148 | _clearAllLayers: function () { 149 | this._activeMode.handler.removeAllLayers(); 150 | if (this._activeMode) { 151 | this._activeMode.handler.disable(); 152 | } 153 | }, 154 | 155 | _checkDisabled: function () { 156 | var featureGroup = this.options.featureGroup, 157 | hasLayers = featureGroup.getLayers().length !== 0, 158 | button; 159 | 160 | if (this.options.edit) { 161 | button = this._modes[L.EditToolbar.Edit.TYPE].button; 162 | 163 | if (hasLayers) { 164 | L.DomUtil.removeClass(button, 'leaflet-disabled'); 165 | } else { 166 | L.DomUtil.addClass(button, 'leaflet-disabled'); 167 | } 168 | 169 | button.setAttribute( 170 | 'title', 171 | hasLayers ? 172 | L.drawLocal.edit.toolbar.buttons.edit 173 | : L.drawLocal.edit.toolbar.buttons.editDisabled 174 | ); 175 | } 176 | 177 | if (this.options.remove) { 178 | button = this._modes[L.EditToolbar.Delete.TYPE].button; 179 | 180 | if (hasLayers) { 181 | L.DomUtil.removeClass(button, 'leaflet-disabled'); 182 | } else { 183 | L.DomUtil.addClass(button, 'leaflet-disabled'); 184 | } 185 | 186 | button.setAttribute( 187 | 'title', 188 | hasLayers ? 189 | L.drawLocal.edit.toolbar.buttons.remove 190 | : L.drawLocal.edit.toolbar.buttons.removeDisabled 191 | ); 192 | } 193 | } 194 | }); 195 | -------------------------------------------------------------------------------- /static_files/draw_files/GeometryUtil.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 3 | var defaultPrecision = { 4 | km: 2, 5 | ha: 2, 6 | m: 0, 7 | mi: 2, 8 | ac: 2, 9 | yd: 0, 10 | ft: 0, 11 | nm: 2 12 | }; 13 | 14 | 15 | /** 16 | * @class L.GeometryUtil 17 | * @aka GeometryUtil 18 | */ 19 | L.GeometryUtil = L.extend(L.GeometryUtil || {}, { 20 | // Ported from the OpenLayers implementation. See https://github.com/openlayers/openlayers/blob/master/lib/OpenLayers/Geometry/LinearRing.js#L270 21 | 22 | // @method geodesicArea(): number 23 | geodesicArea: function (latLngs) { 24 | var pointsCount = latLngs.length, 25 | area = 0.0, 26 | d2r = Math.PI / 180, 27 | p1, p2; 28 | 29 | if (pointsCount > 2) { 30 | for (var i = 0; i < pointsCount; i++) { 31 | p1 = latLngs[i]; 32 | p2 = latLngs[(i + 1) % pointsCount]; 33 | area += ((p2.lng - p1.lng) * d2r) * 34 | (2 + Math.sin(p1.lat * d2r) + Math.sin(p2.lat * d2r)); 35 | } 36 | area = area * 6378137.0 * 6378137.0 / 2.0; 37 | } 38 | 39 | return Math.abs(area); 40 | }, 41 | 42 | // @method formattedNumber(n, precision): string 43 | // Returns n in specified number format (if defined) and precision 44 | formattedNumber: function (n, precision) { 45 | var formatted = parseFloat(n).toFixed(precision), 46 | format = L.drawLocal.format && L.drawLocal.format.numeric, 47 | delimiters = format && format.delimiters, 48 | thousands = delimiters && delimiters.thousands, 49 | decimal = delimiters && delimiters.decimal; 50 | 51 | if (thousands || decimal) { 52 | var splitValue = formatted.split('.'); 53 | formatted = thousands ? splitValue[0].replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1' + thousands) : splitValue[0]; 54 | decimal = decimal || '.'; 55 | if (splitValue.length > 1) { 56 | formatted = formatted + decimal + splitValue[1]; 57 | } 58 | } 59 | 60 | return formatted; 61 | }, 62 | 63 | // @method readableArea(area, isMetric, precision): string 64 | // Returns a readable area string in yards or metric. 65 | // The value will be rounded as defined by the precision option object. 66 | readableArea: function (area, isMetric, precision) { 67 | var areaStr, 68 | units, 69 | precision = L.Util.extend({}, defaultPrecision, precision); 70 | 71 | if (isMetric) { 72 | units = ['ha', 'm']; 73 | type = typeof isMetric; 74 | if (type === 'string') { 75 | units = [isMetric]; 76 | } else if (type !== 'boolean') { 77 | units = isMetric; 78 | } 79 | 80 | if (area >= 1000000 && units.indexOf('km') !== -1) { 81 | areaStr = L.GeometryUtil.formattedNumber(area * 0.000001, precision['km']) + ' km²'; 82 | } else if (area >= 10000 && units.indexOf('ha') !== -1) { 83 | areaStr = L.GeometryUtil.formattedNumber(area * 0.0001, precision['ha']) + ' ha'; 84 | } else { 85 | areaStr = L.GeometryUtil.formattedNumber(area, precision['m']) + ' m²'; 86 | } 87 | } else { 88 | area /= 0.836127; // Square yards in 1 meter 89 | 90 | if (area >= 3097600) { //3097600 square yards in 1 square mile 91 | areaStr = L.GeometryUtil.formattedNumber(area / 3097600, precision['mi']) + ' mi²'; 92 | } else if (area >= 4840) { //4840 square yards in 1 acre 93 | areaStr = L.GeometryUtil.formattedNumber(area / 4840, precision['ac']) + ' acres'; 94 | } else { 95 | areaStr = L.GeometryUtil.formattedNumber(area, precision['yd']) + ' yd²'; 96 | } 97 | } 98 | 99 | return areaStr; 100 | }, 101 | 102 | // @method readableDistance(distance, units): string 103 | // Converts a metric distance to one of [ feet, nauticalMile, metric or yards ] string 104 | // 105 | // @alternative 106 | // @method readableDistance(distance, isMetric, useFeet, isNauticalMile, precision): string 107 | // Converts metric distance to distance string. 108 | // The value will be rounded as defined by the precision option object. 109 | readableDistance: function (distance, isMetric, isFeet, isNauticalMile, precision) { 110 | var distanceStr, 111 | units, 112 | precision = L.Util.extend({}, defaultPrecision, precision); 113 | 114 | if (isMetric) { 115 | units = typeof isMetric == 'string' ? isMetric : 'metric'; 116 | } else if (isFeet) { 117 | units = 'feet'; 118 | } else if (isNauticalMile) { 119 | units = 'nauticalMile'; 120 | } else { 121 | units = 'yards'; 122 | } 123 | 124 | switch (units) { 125 | case 'metric': 126 | // show metres when distance is < 1km, then show km 127 | if (distance > 1000) { 128 | distanceStr = L.GeometryUtil.formattedNumber(distance / 1000, precision['km']) + ' km'; 129 | } else { 130 | distanceStr = L.GeometryUtil.formattedNumber(distance, precision['m']) + ' m'; 131 | } 132 | break; 133 | case 'feet': 134 | distance *= 1.09361 * 3; 135 | distanceStr = L.GeometryUtil.formattedNumber(distance, precision['ft']) + ' ft'; 136 | 137 | break; 138 | case 'nauticalMile': 139 | distance *= 0.53996; 140 | distanceStr = L.GeometryUtil.formattedNumber(distance / 1000, precision['nm']) + ' nm'; 141 | break; 142 | case 'yards': 143 | default: 144 | distance *= 1.09361; 145 | 146 | if (distance > 1760) { 147 | distanceStr = L.GeometryUtil.formattedNumber(distance / 1760, precision['mi']) + ' miles'; 148 | } else { 149 | distanceStr = L.GeometryUtil.formattedNumber(distance, precision['yd']) + ' yd'; 150 | } 151 | break; 152 | } 153 | return distanceStr; 154 | }, 155 | 156 | // @method isVersion07x(): boolean 157 | // Returns true if the Leaflet version is 0.7.x, false otherwise. 158 | isVersion07x: function () { 159 | var version = L.version.split('.'); 160 | //If Version is == 0.7.* 161 | return parseInt(version[0], 10) === 0 && parseInt(version[1], 10) === 7; 162 | }, 163 | }); 164 | 165 | })(); 166 | -------------------------------------------------------------------------------- /static_files/draw_files/LatLngUtil.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @class L.LatLngUtil 3 | * @aka LatLngUtil 4 | */ 5 | L.LatLngUtil = { 6 | // Clones a LatLngs[], returns [][] 7 | 8 | // @method cloneLatLngs(LatLngs[]): L.LatLngs[] 9 | // Clone the latLng point or points or nested points and return an array with those points 10 | cloneLatLngs: function (latlngs) { 11 | var clone = []; 12 | for (var i = 0, l = latlngs.length; i < l; i++) { 13 | // Check for nested array (Polyline/Polygon) 14 | if (Array.isArray(latlngs[i])) { 15 | clone.push(L.LatLngUtil.cloneLatLngs(latlngs[i])); 16 | } else { 17 | clone.push(this.cloneLatLng(latlngs[i])); 18 | } 19 | } 20 | return clone; 21 | }, 22 | 23 | // @method cloneLatLng(LatLng): L.LatLng 24 | // Clone the latLng and return a new LatLng object. 25 | cloneLatLng: function (latlng) { 26 | return L.latLng(latlng.lat, latlng.lng); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /static_files/draw_files/Leaflet.Draw.Event.js: -------------------------------------------------------------------------------- 1 | /** 2 | * ### Events 3 | * Once you have successfully added the Leaflet.draw plugin to your map you will want to respond to the different 4 | * actions users can initiate. The following events will be triggered on the map: 5 | * 6 | * @class L.Draw.Event 7 | * @aka Draw.Event 8 | * 9 | * Use `L.Draw.Event.EVENTNAME` constants to ensure events are correct. 10 | * 11 | * @example 12 | * ```js 13 | * map.on(L.Draw.Event.CREATED; function (e) { 14 | * var type = e.layerType, 15 | * layer = e.layer; 16 | * 17 | * if (type === 'marker') { 18 | * // Do marker specific actions 19 | * } 20 | * 21 | * // Do whatever else you need to. (save to db; add to map etc) 22 | * map.addLayer(layer); 23 | *}); 24 | * ``` 25 | */ 26 | L.Draw.Event = {}; 27 | /** 28 | * @event draw:created: PolyLine; Polygon; Rectangle; Circle; Marker | String 29 | * 30 | * Layer that was just created. 31 | * The type of layer this is. One of: `polyline`; `polygon`; `rectangle`; `circle`; `marker` 32 | * Triggered when a new vector or marker has been created. 33 | * 34 | */ 35 | L.Draw.Event.CREATED = 'draw:created'; 36 | 37 | /** 38 | * @event draw:edited: LayerGroup 39 | * 40 | * List of all layers just edited on the map. 41 | * 42 | * 43 | * Triggered when layers in the FeatureGroup; initialised with the plugin; have been edited and saved. 44 | * 45 | * @example 46 | * ```js 47 | * map.on('draw:edited', function (e) { 48 | * var layers = e.layers; 49 | * layers.eachLayer(function (layer) { 50 | * //do whatever you want; most likely save back to db 51 | * }); 52 | * }); 53 | * ``` 54 | */ 55 | L.Draw.Event.EDITED = 'draw:edited'; 56 | 57 | /** 58 | * @event draw:deleted: LayerGroup 59 | * 60 | * List of all layers just removed from the map. 61 | * 62 | * Triggered when layers have been removed (and saved) from the FeatureGroup. 63 | */ 64 | L.Draw.Event.DELETED = 'draw:deleted'; 65 | 66 | /** 67 | * @event draw:drawstart: String 68 | * 69 | * The type of layer this is. One of:`polyline`; `polygon`; `rectangle`; `circle`; `marker` 70 | * 71 | * Triggered when the user has chosen to draw a particular vector or marker. 72 | */ 73 | L.Draw.Event.DRAWSTART = 'draw:drawstart'; 74 | 75 | /** 76 | * @event draw:drawstop: String 77 | * 78 | * The type of layer this is. One of: `polyline`; `polygon`; `rectangle`; `circle`; `marker` 79 | * 80 | * Triggered when the user has finished a particular vector or marker. 81 | */ 82 | 83 | L.Draw.Event.DRAWSTOP = 'draw:drawstop'; 84 | 85 | /** 86 | * @event draw:drawvertex: LayerGroup 87 | * 88 | * List of all layers just being added from the map. 89 | * 90 | * Triggered when a vertex is created on a polyline or polygon. 91 | */ 92 | L.Draw.Event.DRAWVERTEX = 'draw:drawvertex'; 93 | 94 | /** 95 | * @event draw:editstart: String 96 | * 97 | * The type of edit this is. One of: `edit` 98 | * 99 | * Triggered when the user starts edit mode by clicking the edit tool button. 100 | */ 101 | 102 | L.Draw.Event.EDITSTART = 'draw:editstart'; 103 | 104 | /** 105 | * @event draw:editmove: ILayer 106 | * 107 | * Layer that was just moved. 108 | * 109 | * Triggered as the user moves a rectangle; circle or marker. 110 | */ 111 | L.Draw.Event.EDITMOVE = 'draw:editmove'; 112 | 113 | /** 114 | * @event draw:editresize: ILayer 115 | * 116 | * Layer that was just moved. 117 | * 118 | * Triggered as the user resizes a rectangle or circle. 119 | */ 120 | L.Draw.Event.EDITRESIZE = 'draw:editresize'; 121 | 122 | /** 123 | * @event draw:editvertex: LayerGroup 124 | * 125 | * List of all layers just being edited from the map. 126 | * 127 | * Triggered when a vertex is edited on a polyline or polygon. 128 | */ 129 | L.Draw.Event.EDITVERTEX = 'draw:editvertex'; 130 | 131 | /** 132 | * @event draw:editstop: String 133 | * 134 | * The type of edit this is. One of: `edit` 135 | * 136 | * Triggered when the user has finshed editing (edit mode) and saves edits. 137 | */ 138 | L.Draw.Event.EDITSTOP = 'draw:editstop'; 139 | 140 | /** 141 | * @event draw:deletestart: String 142 | * 143 | * The type of edit this is. One of: `remove` 144 | * 145 | * Triggered when the user starts remove mode by clicking the remove tool button. 146 | */ 147 | L.Draw.Event.DELETESTART = 'draw:deletestart'; 148 | 149 | /** 150 | * @event draw:deletestop: String 151 | * 152 | * The type of edit this is. One of: `remove` 153 | * 154 | * Triggered when the user has finished removing shapes (remove mode) and saves. 155 | */ 156 | L.Draw.Event.DELETESTOP = 'draw:deletestop'; 157 | 158 | /** 159 | * @event draw:toolbaropened: String 160 | * 161 | * Triggered when a toolbar is opened. 162 | */ 163 | L.Draw.Event.TOOLBAROPENED = 'draw:toolbaropened'; 164 | 165 | /** 166 | * @event draw:toolbarclosed: String 167 | * 168 | * Triggered when a toolbar is closed. 169 | */ 170 | L.Draw.Event.TOOLBARCLOSED = 'draw:toolbarclosed'; 171 | 172 | /** 173 | * @event draw:markercontext: String 174 | * 175 | * Triggered when a marker is right clicked. 176 | */ 177 | L.Draw.Event.MARKERCONTEXT = 'draw:markercontext'; -------------------------------------------------------------------------------- /static_files/draw_files/Leaflet.draw.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Leaflet.draw assumes that you have already included the Leaflet library. 3 | */ 4 | L.drawVersion = '0.4.2'; 5 | /** 6 | * @class L.Draw 7 | * @aka Draw 8 | * 9 | * 10 | * To add the draw toolbar set the option drawControl: true in the map options. 11 | * 12 | * @example 13 | * ```js 14 | * var map = L.map('map', {drawControl: true}).setView([51.505, -0.09], 13); 15 | * 16 | * L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', { 17 | * attribution: '© OpenStreetMap contributors' 18 | * }).addTo(map); 19 | * ``` 20 | * 21 | * ### Adding the edit toolbar 22 | * To use the edit toolbar you must initialise the Leaflet.draw control and manually add it to the map. 23 | * 24 | * ```js 25 | * var map = L.map('map').setView([51.505, -0.09], 13); 26 | * 27 | * L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', { 28 | * attribution: '© OpenStreetMap contributors' 29 | * }).addTo(map); 30 | * 31 | * // FeatureGroup is to store editable layers 32 | * var drawnItems = new L.FeatureGroup(); 33 | * map.addLayer(drawnItems); 34 | * 35 | * var drawControl = new L.Control.Draw({ 36 | * edit: { 37 | * featureGroup: drawnItems 38 | * } 39 | * }); 40 | * map.addControl(drawControl); 41 | * ``` 42 | * 43 | * The key here is the featureGroup option. This tells the plugin which FeatureGroup contains the layers that 44 | * should be editable. The featureGroup can contain 0 or more features with geometry types Point, LineString, and Polygon. 45 | * Leaflet.draw does not work with multigeometry features such as MultiPoint, MultiLineString, MultiPolygon, 46 | * or GeometryCollection. If you need to add multigeometry features to the draw plugin, convert them to a 47 | * FeatureCollection of non-multigeometries (Points, LineStrings, or Polygons). 48 | */ 49 | L.Draw = {}; 50 | 51 | /** 52 | * @class L.drawLocal 53 | * @aka L.drawLocal 54 | * 55 | * The core toolbar class of the API — it is used to create the toolbar ui 56 | * 57 | * @example 58 | * ```js 59 | * var modifiedDraw = L.drawLocal.extend({ 60 | * draw: { 61 | * toolbar: { 62 | * buttons: { 63 | * polygon: 'Draw an awesome polygon' 64 | * } 65 | * } 66 | * } 67 | * }); 68 | * ``` 69 | * 70 | * The default state for the control is the draw toolbar just below the zoom control. 71 | * This will allow map users to draw vectors and markers. 72 | * **Please note the edit toolbar is not enabled by default.** 73 | */ 74 | L.drawLocal = { 75 | // format: { 76 | // numeric: { 77 | // delimiters: { 78 | // thousands: ',', 79 | // decimal: '.' 80 | // } 81 | // } 82 | // }, 83 | draw: { 84 | toolbar: { 85 | // #TODO: this should be reorganized where actions are nested in actions 86 | // ex: actions.undo or actions.cancel 87 | actions: { 88 | title: 'Cancel drawing', 89 | text: 'Cancel' 90 | }, 91 | finish: { 92 | title: 'Finish drawing', 93 | text: 'Finish' 94 | }, 95 | undo: { 96 | title: 'Delete last point drawn', 97 | text: 'Delete last point' 98 | }, 99 | buttons: { 100 | polyline: 'Draw a polyline', 101 | polygon: 'Draw a polygon', 102 | rectangle: 'Draw a rectangle', 103 | circle: 'Draw a circle', 104 | marker: 'Draw a marker', 105 | circlemarker: 'Draw a circlemarker' 106 | } 107 | }, 108 | handlers: { 109 | circle: { 110 | tooltip: { 111 | start: 'Click and drag to draw circle.' 112 | }, 113 | radius: 'Radius' 114 | }, 115 | circlemarker: { 116 | tooltip: { 117 | start: 'Click map to place circle marker.' 118 | } 119 | }, 120 | marker: { 121 | tooltip: { 122 | start: 'Click map to place marker.' 123 | } 124 | }, 125 | polygon: { 126 | tooltip: { 127 | start: 'Click to start drawing shape.', 128 | cont: 'Click to continue drawing shape.', 129 | end: 'Click first point to close this shape.' 130 | } 131 | }, 132 | polyline: { 133 | error: 'Error: shape edges cannot cross!', 134 | tooltip: { 135 | start: 'Click to start drawing line.', 136 | cont: 'Click to continue drawing line.', 137 | end: 'Click last point to finish line.' 138 | } 139 | }, 140 | rectangle: { 141 | tooltip: { 142 | start: 'Click and drag to draw rectangle.' 143 | } 144 | }, 145 | simpleshape: { 146 | tooltip: { 147 | end: 'Release mouse to finish drawing.' 148 | } 149 | } 150 | } 151 | }, 152 | edit: { 153 | toolbar: { 154 | actions: { 155 | save: { 156 | title: 'Save changes', 157 | text: 'Save' 158 | }, 159 | cancel: { 160 | title: 'Cancel editing, discards all changes', 161 | text: 'Cancel' 162 | }, 163 | clearAll: { 164 | title: 'Clear all layers', 165 | text: 'Clear All' 166 | } 167 | }, 168 | buttons: { 169 | edit: 'Edit layers', 170 | editDisabled: 'No layers to edit', 171 | remove: 'Delete layers', 172 | removeDisabled: 'No layers to delete' 173 | } 174 | }, 175 | handlers: { 176 | edit: { 177 | tooltip: { 178 | text: 'Drag handles or markers to edit features.', 179 | subtext: 'Click cancel to undo changes.' 180 | } 181 | }, 182 | remove: { 183 | tooltip: { 184 | text: 'Click on a feature to remove.' 185 | } 186 | } 187 | } 188 | } 189 | }; 190 | -------------------------------------------------------------------------------- /static_files/draw_files/LineUtil.Intersect.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @class L.LineUtil 3 | * @aka Util 4 | * @aka L.Utils 5 | */ 6 | L.Util.extend(L.LineUtil, { 7 | 8 | // @method segmentsIntersect(): boolean 9 | // Checks to see if two line segments intersect. Does not handle degenerate cases. 10 | // http://compgeom.cs.uiuc.edu/~jeffe/teaching/373/notes/x06-sweepline.pdf 11 | segmentsIntersect: function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2, /*Point*/ p3) { 12 | return this._checkCounterclockwise(p, p2, p3) !== 13 | this._checkCounterclockwise(p1, p2, p3) && 14 | this._checkCounterclockwise(p, p1, p2) !== 15 | this._checkCounterclockwise(p, p1, p3); 16 | }, 17 | 18 | // check to see if points are in counterclockwise order 19 | _checkCounterclockwise: function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2) { 20 | return (p2.y - p.y) * (p1.x - p.x) > (p1.y - p.y) * (p2.x - p.x); 21 | } 22 | }); 23 | -------------------------------------------------------------------------------- /static_files/draw_files/Polygon.Intersect.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @class L.Polygon 3 | * @aka Polygon 4 | */ 5 | L.Polygon.include({ 6 | 7 | // @method intersects(): boolean 8 | // Checks a polygon for any intersecting line segments. Ignores holes. 9 | intersects: function () { 10 | var polylineIntersects, 11 | points = this._getProjectedPoints(), 12 | len, firstPoint, lastPoint, maxIndex; 13 | 14 | if (this._tooFewPointsForIntersection()) { 15 | return false; 16 | } 17 | 18 | polylineIntersects = L.Polyline.prototype.intersects.call(this); 19 | 20 | // If already found an intersection don't need to check for any more. 21 | if (polylineIntersects) { 22 | return true; 23 | } 24 | 25 | len = points.length; 26 | firstPoint = points[0]; 27 | lastPoint = points[len - 1]; 28 | maxIndex = len - 2; 29 | 30 | // Check the line segment between last and first point. Don't need to check the first line segment (minIndex = 1) 31 | return this._lineSegmentsIntersectsRange(lastPoint, firstPoint, maxIndex, 1); 32 | } 33 | }); 34 | -------------------------------------------------------------------------------- /static_files/draw_files/Polyline.Intersect.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @class L.Polyline 3 | * @aka Polyline 4 | */ 5 | L.Polyline.include({ 6 | 7 | // @method intersects(): boolean 8 | // Check to see if this polyline has any linesegments that intersect. 9 | // NOTE: does not support detecting intersection for degenerate cases. 10 | intersects: function () { 11 | var points = this._getProjectedPoints(), 12 | len = points ? points.length : 0, 13 | i, p, p1; 14 | 15 | if (this._tooFewPointsForIntersection()) { 16 | return false; 17 | } 18 | 19 | for (i = len - 1; i >= 3; i--) { 20 | p = points[i - 1]; 21 | p1 = points[i]; 22 | 23 | 24 | if (this._lineSegmentsIntersectsRange(p, p1, i - 2)) { 25 | return true; 26 | } 27 | } 28 | 29 | return false; 30 | }, 31 | 32 | // @method newLatLngIntersects(): boolean 33 | // Check for intersection if new latlng was added to this polyline. 34 | // NOTE: does not support detecting intersection for degenerate cases. 35 | newLatLngIntersects: function (latlng, skipFirst) { 36 | // Cannot check a polyline for intersecting lats/lngs when not added to the map 37 | if (!this._map) { 38 | return false; 39 | } 40 | 41 | return this.newPointIntersects(this._map.latLngToLayerPoint(latlng), skipFirst); 42 | }, 43 | 44 | // @method newPointIntersects(): boolean 45 | // Check for intersection if new point was added to this polyline. 46 | // newPoint must be a layer point. 47 | // NOTE: does not support detecting intersection for degenerate cases. 48 | newPointIntersects: function (newPoint, skipFirst) { 49 | var points = this._getProjectedPoints(), 50 | len = points ? points.length : 0, 51 | lastPoint = points ? points[len - 1] : null, 52 | // The previous previous line segment. Previous line segment doesn't need testing. 53 | maxIndex = len - 2; 54 | 55 | if (this._tooFewPointsForIntersection(1)) { 56 | return false; 57 | } 58 | 59 | return this._lineSegmentsIntersectsRange(lastPoint, newPoint, maxIndex, skipFirst ? 1 : 0); 60 | }, 61 | 62 | // Polylines with 2 sides can only intersect in cases where points are collinear (we don't support detecting these). 63 | // Cannot have intersection when < 3 line segments (< 4 points) 64 | _tooFewPointsForIntersection: function (extraPoints) { 65 | var points = this._getProjectedPoints(), 66 | len = points ? points.length : 0; 67 | // Increment length by extraPoints if present 68 | len += extraPoints || 0; 69 | 70 | return !points || len <= 3; 71 | }, 72 | 73 | // Checks a line segment intersections with any line segments before its predecessor. 74 | // Don't need to check the predecessor as will never intersect. 75 | _lineSegmentsIntersectsRange: function (p, p1, maxIndex, minIndex) { 76 | var points = this._getProjectedPoints(), 77 | p2, p3; 78 | 79 | minIndex = minIndex || 0; 80 | 81 | // Check all previous line segments (beside the immediately previous) for intersections 82 | for (var j = maxIndex; j > minIndex; j--) { 83 | p2 = points[j - 1]; 84 | p3 = points[j]; 85 | 86 | if (L.LineUtil.segmentsIntersect(p, p1, p2, p3)) { 87 | return true; 88 | } 89 | } 90 | 91 | return false; 92 | }, 93 | 94 | _getProjectedPoints: function () { 95 | if (!this._defaultShape) { 96 | return this._originalPoints; 97 | } 98 | var points = [], 99 | _shape = this._defaultShape(); 100 | 101 | for (var i = 0; i < _shape.length; i++) { 102 | points.push(this._map.latLngToLayerPoint(_shape[i])); 103 | } 104 | return points; 105 | } 106 | }); 107 | -------------------------------------------------------------------------------- /static_files/draw_files/Toolbar.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @class L.Draw.Toolbar 3 | * @aka Toolbar 4 | * 5 | * The toolbar class of the API — it is used to create the ui 6 | * This will be depreciated 7 | * 8 | * @example 9 | * 10 | * ```js 11 | * var toolbar = L.Toolbar(); 12 | * toolbar.addToolbar(map); 13 | * ``` 14 | * 15 | * ### Disabling a toolbar 16 | * 17 | * If you do not want a particular toolbar in your app you can turn it off by setting the toolbar to false. 18 | * 19 | * ```js 20 | * var drawControl = new L.Control.Draw({ 21 | * draw: false, 22 | * edit: { 23 | * featureGroup: editableLayers 24 | * } 25 | * }); 26 | * ``` 27 | * 28 | * ### Disabling a toolbar item 29 | * 30 | * If you want to turn off a particular toolbar item, set it to false. The following disables drawing polygons and 31 | * markers. It also turns off the ability to edit layers. 32 | * 33 | * ```js 34 | * var drawControl = new L.Control.Draw({ 35 | * draw: { 36 | * polygon: false, 37 | * marker: false 38 | * }, 39 | * edit: { 40 | * featureGroup: editableLayers, 41 | * edit: false 42 | * } 43 | * }); 44 | * ``` 45 | */ 46 | L.Toolbar = L.Class.extend({ 47 | // @section Methods for modifying the toolbar 48 | 49 | // @method initialize(options): void 50 | // Toolbar constructor 51 | initialize: function (options) { 52 | L.setOptions(this, options); 53 | 54 | this._modes = {}; 55 | this._actionButtons = []; 56 | this._activeMode = null; 57 | 58 | var version = L.version.split('.'); 59 | //If Version is >= 1.2.0 60 | if (parseInt(version[0], 10) === 1 && parseInt(version[1], 10) >= 2) { 61 | L.Toolbar.include(L.Evented.prototype); 62 | } else { 63 | L.Toolbar.include(L.Mixin.Events); 64 | } 65 | }, 66 | 67 | // @method enabled(): boolean 68 | // Gets a true/false of whether the toolbar is enabled 69 | enabled: function () { 70 | return this._activeMode !== null; 71 | }, 72 | 73 | // @method disable(): void 74 | // Disables the toolbar 75 | disable: function () { 76 | if (!this.enabled()) { 77 | return; 78 | } 79 | 80 | this._activeMode.handler.disable(); 81 | }, 82 | 83 | // @method addToolbar(map): L.DomUtil 84 | // Adds the toolbar to the map and returns the toolbar dom element 85 | addToolbar: function (map) { 86 | var container = L.DomUtil.create('div', 'leaflet-draw-section'), 87 | buttonIndex = 0, 88 | buttonClassPrefix = this._toolbarClass || '', 89 | modeHandlers = this.getModeHandlers(map), 90 | i; 91 | 92 | this._toolbarContainer = L.DomUtil.create('div', 'leaflet-draw-toolbar leaflet-bar'); 93 | this._map = map; 94 | 95 | for (i = 0; i < modeHandlers.length; i++) { 96 | if (modeHandlers[i].enabled) { 97 | this._initModeHandler( 98 | modeHandlers[i].handler, 99 | this._toolbarContainer, 100 | buttonIndex++, 101 | buttonClassPrefix, 102 | modeHandlers[i].title 103 | ); 104 | } 105 | } 106 | 107 | // if no buttons were added, do not add the toolbar 108 | if (!buttonIndex) { 109 | return; 110 | } 111 | 112 | // Save button index of the last button, -1 as we would have ++ after the last button 113 | this._lastButtonIndex = --buttonIndex; 114 | 115 | // Create empty actions part of the toolbar 116 | this._actionsContainer = L.DomUtil.create('ul', 'leaflet-draw-actions'); 117 | 118 | // Add draw and cancel containers to the control container 119 | container.appendChild(this._toolbarContainer); 120 | container.appendChild(this._actionsContainer); 121 | 122 | return container; 123 | }, 124 | 125 | // @method removeToolbar(): void 126 | // Removes the toolbar and drops the handler event listeners 127 | removeToolbar: function () { 128 | // Dispose each handler 129 | for (var handlerId in this._modes) { 130 | if (this._modes.hasOwnProperty(handlerId)) { 131 | // Unbind handler button 132 | this._disposeButton( 133 | this._modes[handlerId].button, 134 | this._modes[handlerId].handler.enable, 135 | this._modes[handlerId].handler 136 | ); 137 | 138 | // Make sure is disabled 139 | this._modes[handlerId].handler.disable(); 140 | 141 | // Unbind handler 142 | this._modes[handlerId].handler 143 | .off('enabled', this._handlerActivated, this) 144 | .off('disabled', this._handlerDeactivated, this); 145 | } 146 | } 147 | this._modes = {}; 148 | 149 | // Dispose the actions toolbar 150 | for (var i = 0, l = this._actionButtons.length; i < l; i++) { 151 | this._disposeButton( 152 | this._actionButtons[i].button, 153 | this._actionButtons[i].callback, 154 | this 155 | ); 156 | } 157 | this._actionButtons = []; 158 | this._actionsContainer = null; 159 | }, 160 | 161 | _initModeHandler: function (handler, container, buttonIndex, classNamePredix, buttonTitle) { 162 | var type = handler.type; 163 | 164 | this._modes[type] = {}; 165 | 166 | this._modes[type].handler = handler; 167 | 168 | this._modes[type].button = this._createButton({ 169 | type: type, 170 | title: buttonTitle, 171 | className: classNamePredix + '-' + type, 172 | container: container, 173 | callback: this._modes[type].handler.enable, 174 | context: this._modes[type].handler 175 | }); 176 | 177 | this._modes[type].buttonIndex = buttonIndex; 178 | 179 | this._modes[type].handler 180 | .on('enabled', this._handlerActivated, this) 181 | .on('disabled', this._handlerDeactivated, this); 182 | }, 183 | 184 | /* Detect iOS based on browser User Agent, based on: 185 | * http://stackoverflow.com/a/9039885 */ 186 | _detectIOS: function () { 187 | var iOS = (/iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream); 188 | return iOS; 189 | }, 190 | 191 | _createButton: function (options) { 192 | 193 | var link = L.DomUtil.create('a', options.className || '', options.container); 194 | // Screen reader tag 195 | var sr = L.DomUtil.create('span', 'sr-only', options.container); 196 | 197 | link.href = '#'; 198 | link.appendChild(sr); 199 | 200 | if (options.title) { 201 | link.title = options.title; 202 | sr.innerHTML = options.title; 203 | } 204 | 205 | if (options.text) { 206 | link.innerHTML = options.text; 207 | sr.innerHTML = options.text; 208 | } 209 | 210 | /* iOS does not use click events */ 211 | var buttonEvent = this._detectIOS() ? 'touchstart' : 'click'; 212 | 213 | L.DomEvent 214 | .on(link, 'click', L.DomEvent.stopPropagation) 215 | .on(link, 'mousedown', L.DomEvent.stopPropagation) 216 | .on(link, 'dblclick', L.DomEvent.stopPropagation) 217 | .on(link, 'touchstart', L.DomEvent.stopPropagation) 218 | .on(link, 'click', L.DomEvent.preventDefault) 219 | .on(link, buttonEvent, options.callback, options.context); 220 | 221 | return link; 222 | }, 223 | 224 | _disposeButton: function (button, callback) { 225 | /* iOS does not use click events */ 226 | var buttonEvent = this._detectIOS() ? 'touchstart' : 'click'; 227 | 228 | L.DomEvent 229 | .off(button, 'click', L.DomEvent.stopPropagation) 230 | .off(button, 'mousedown', L.DomEvent.stopPropagation) 231 | .off(button, 'dblclick', L.DomEvent.stopPropagation) 232 | .off(button, 'touchstart', L.DomEvent.stopPropagation) 233 | .off(button, 'click', L.DomEvent.preventDefault) 234 | .off(button, buttonEvent, callback); 235 | }, 236 | 237 | _handlerActivated: function (e) { 238 | // Disable active mode (if present) 239 | this.disable(); 240 | 241 | // Cache new active feature 242 | this._activeMode = this._modes[e.handler]; 243 | 244 | L.DomUtil.addClass(this._activeMode.button, 'leaflet-draw-toolbar-button-enabled'); 245 | 246 | this._showActionsToolbar(); 247 | 248 | this.fire('enable'); 249 | }, 250 | 251 | _handlerDeactivated: function () { 252 | this._hideActionsToolbar(); 253 | 254 | L.DomUtil.removeClass(this._activeMode.button, 'leaflet-draw-toolbar-button-enabled'); 255 | 256 | this._activeMode = null; 257 | 258 | this.fire('disable'); 259 | }, 260 | 261 | _createActions: function (handler) { 262 | var container = this._actionsContainer, 263 | buttons = this.getActions(handler), 264 | l = buttons.length, 265 | li, di, dl, button; 266 | 267 | // Dispose the actions toolbar (todo: dispose only not used buttons) 268 | for (di = 0, dl = this._actionButtons.length; di < dl; di++) { 269 | this._disposeButton(this._actionButtons[di].button, this._actionButtons[di].callback); 270 | } 271 | this._actionButtons = []; 272 | 273 | // Remove all old buttons 274 | while (container.firstChild) { 275 | container.removeChild(container.firstChild); 276 | } 277 | 278 | for (var i = 0; i < l; i++) { 279 | if ('enabled' in buttons[i] && !buttons[i].enabled) { 280 | continue; 281 | } 282 | 283 | li = L.DomUtil.create('li', '', container); 284 | 285 | button = this._createButton({ 286 | title: buttons[i].title, 287 | text: buttons[i].text, 288 | container: li, 289 | callback: buttons[i].callback, 290 | context: buttons[i].context 291 | }); 292 | 293 | this._actionButtons.push({ 294 | button: button, 295 | callback: buttons[i].callback 296 | }); 297 | } 298 | }, 299 | 300 | _showActionsToolbar: function () { 301 | var buttonIndex = this._activeMode.buttonIndex, 302 | lastButtonIndex = this._lastButtonIndex, 303 | toolbarPosition = this._activeMode.button.offsetTop - 1; 304 | 305 | // Recreate action buttons on every click 306 | this._createActions(this._activeMode.handler); 307 | 308 | // Correctly position the cancel button 309 | this._actionsContainer.style.top = toolbarPosition + 'px'; 310 | 311 | if (buttonIndex === 0) { 312 | L.DomUtil.addClass(this._toolbarContainer, 'leaflet-draw-toolbar-notop'); 313 | L.DomUtil.addClass(this._actionsContainer, 'leaflet-draw-actions-top'); 314 | } 315 | 316 | if (buttonIndex === lastButtonIndex) { 317 | L.DomUtil.addClass(this._toolbarContainer, 'leaflet-draw-toolbar-nobottom'); 318 | L.DomUtil.addClass(this._actionsContainer, 'leaflet-draw-actions-bottom'); 319 | } 320 | 321 | this._actionsContainer.style.display = 'block'; 322 | this._map.fire(L.Draw.Event.TOOLBAROPENED); 323 | }, 324 | 325 | _hideActionsToolbar: function () { 326 | this._actionsContainer.style.display = 'none'; 327 | 328 | L.DomUtil.removeClass(this._toolbarContainer, 'leaflet-draw-toolbar-notop'); 329 | L.DomUtil.removeClass(this._toolbarContainer, 'leaflet-draw-toolbar-nobottom'); 330 | L.DomUtil.removeClass(this._actionsContainer, 'leaflet-draw-actions-top'); 331 | L.DomUtil.removeClass(this._actionsContainer, 'leaflet-draw-actions-bottom'); 332 | this._map.fire(L.Draw.Event.TOOLBARCLOSED); 333 | } 334 | }); 335 | -------------------------------------------------------------------------------- /static_files/draw_files/Tooltip.js: -------------------------------------------------------------------------------- 1 | L.Draw = L.Draw || {}; 2 | /** 3 | * @class L.Draw.Tooltip 4 | * @aka Tooltip 5 | * 6 | * The tooltip class — it is used to display the tooltip while drawing 7 | * This will be depreciated 8 | * 9 | * @example 10 | * 11 | * ```js 12 | * var tooltip = L.Draw.Tooltip(); 13 | * ``` 14 | * 15 | */ 16 | L.Draw.Tooltip = L.Class.extend({ 17 | 18 | // @section Methods for modifying draw state 19 | 20 | // @method initialize(map): void 21 | // Tooltip constructor 22 | initialize: function (map) { 23 | this._map = map; 24 | this._popupPane = map._panes.popupPane; 25 | this._visible = false; 26 | 27 | this._container = map.options.drawControlTooltips ? 28 | L.DomUtil.create('div', 'leaflet-draw-tooltip', this._popupPane) : null; 29 | this._singleLineLabel = false; 30 | 31 | this._map.on('mouseout', this._onMouseOut, this); 32 | }, 33 | 34 | // @method dispose(): void 35 | // Remove Tooltip DOM and unbind events 36 | dispose: function () { 37 | this._map.off('mouseout', this._onMouseOut, this); 38 | 39 | if (this._container) { 40 | this._popupPane.removeChild(this._container); 41 | this._container = null; 42 | } 43 | }, 44 | 45 | // @method updateContent(labelText): this 46 | // Changes the tooltip text to string in function call 47 | updateContent: function (labelText) { 48 | if (!this._container) { 49 | return this; 50 | } 51 | labelText.subtext = labelText.subtext || ''; 52 | 53 | // update the vertical position (only if changed) 54 | if (labelText.subtext.length === 0 && !this._singleLineLabel) { 55 | L.DomUtil.addClass(this._container, 'leaflet-draw-tooltip-single'); 56 | this._singleLineLabel = true; 57 | } 58 | else if (labelText.subtext.length > 0 && this._singleLineLabel) { 59 | L.DomUtil.removeClass(this._container, 'leaflet-draw-tooltip-single'); 60 | this._singleLineLabel = false; 61 | } 62 | 63 | this._container.innerHTML = 64 | (labelText.subtext.length > 0 ? 65 | '' + labelText.subtext + '' + '
' : '') + 66 | '' + labelText.text + ''; 67 | 68 | if (!labelText.text && !labelText.subtext) { 69 | this._visible = false; 70 | this._container.style.visibility = 'hidden'; 71 | } else { 72 | this._visible = true; 73 | this._container.style.visibility = 'inherit'; 74 | } 75 | 76 | return this; 77 | }, 78 | 79 | // @method updatePosition(latlng): this 80 | // Changes the location of the tooltip 81 | updatePosition: function (latlng) { 82 | var pos = this._map.latLngToLayerPoint(latlng), 83 | tooltipContainer = this._container; 84 | 85 | if (this._container) { 86 | if (this._visible) { 87 | tooltipContainer.style.visibility = 'inherit'; 88 | } 89 | L.DomUtil.setPosition(tooltipContainer, pos); 90 | } 91 | 92 | return this; 93 | }, 94 | 95 | // @method showAsError(): this 96 | // Applies error class to tooltip 97 | showAsError: function () { 98 | if (this._container) { 99 | L.DomUtil.addClass(this._container, 'leaflet-error-draw-tooltip'); 100 | } 101 | return this; 102 | }, 103 | 104 | // @method removeError(): this 105 | // Removes the error class from the tooltip 106 | removeError: function () { 107 | if (this._container) { 108 | L.DomUtil.removeClass(this._container, 'leaflet-error-draw-tooltip'); 109 | } 110 | return this; 111 | }, 112 | 113 | _onMouseOut: function () { 114 | if (this._container) { 115 | this._container.style.visibility = 'hidden'; 116 | } 117 | } 118 | }); 119 | -------------------------------------------------------------------------------- /static_files/draw_files/TouchEvents.js: -------------------------------------------------------------------------------- 1 | L.Map.mergeOptions({ 2 | touchExtend: true 3 | }); 4 | 5 | /** 6 | * @class L.Map.TouchExtend 7 | * @aka TouchExtend 8 | */ 9 | L.Map.TouchExtend = L.Handler.extend({ 10 | 11 | // @method initialize(): void 12 | // Sets TouchExtend private accessor variables 13 | initialize: function (map) { 14 | this._map = map; 15 | this._container = map._container; 16 | this._pane = map._panes.overlayPane; 17 | }, 18 | 19 | // @method addHooks(): void 20 | // Adds dom listener events to the map container 21 | addHooks: function () { 22 | L.DomEvent.on(this._container, 'touchstart', this._onTouchStart, this); 23 | L.DomEvent.on(this._container, 'touchend', this._onTouchEnd, this); 24 | L.DomEvent.on(this._container, 'touchmove', this._onTouchMove, this); 25 | if (this._detectIE()) { 26 | L.DomEvent.on(this._container, 'MSPointerDown', this._onTouchStart, this); 27 | L.DomEvent.on(this._container, 'MSPointerUp', this._onTouchEnd, this); 28 | L.DomEvent.on(this._container, 'MSPointerMove', this._onTouchMove, this); 29 | L.DomEvent.on(this._container, 'MSPointerCancel', this._onTouchCancel, this); 30 | 31 | } else { 32 | L.DomEvent.on(this._container, 'touchcancel', this._onTouchCancel, this); 33 | L.DomEvent.on(this._container, 'touchleave', this._onTouchLeave, this); 34 | } 35 | }, 36 | 37 | // @method removeHooks(): void 38 | // Removes dom listener events from the map container 39 | removeHooks: function () { 40 | L.DomEvent.off(this._container, 'touchstart', this._onTouchStart); 41 | L.DomEvent.off(this._container, 'touchend', this._onTouchEnd); 42 | L.DomEvent.off(this._container, 'touchmove', this._onTouchMove); 43 | if (this._detectIE()) { 44 | L.DomEvent.off(this._container, 'MSPointerDowm', this._onTouchStart); 45 | L.DomEvent.off(this._container, 'MSPointerUp', this._onTouchEnd); 46 | L.DomEvent.off(this._container, 'MSPointerMove', this._onTouchMove); 47 | L.DomEvent.off(this._container, 'MSPointerCancel', this._onTouchCancel); 48 | } else { 49 | L.DomEvent.off(this._container, 'touchcancel', this._onTouchCancel); 50 | L.DomEvent.off(this._container, 'touchleave', this._onTouchLeave); 51 | } 52 | }, 53 | 54 | _touchEvent: function (e, type) { 55 | // #TODO: fix the pageX error that is do a bug in Android where a single touch triggers two click events 56 | // _filterClick is what leaflet uses as a workaround. 57 | // This is a problem with more things than just android. Another problem is touchEnd has no touches in 58 | // its touch list. 59 | var touchEvent = {}; 60 | if (typeof e.touches !== 'undefined') { 61 | if (!e.touches.length) { 62 | return; 63 | } 64 | touchEvent = e.touches[0]; 65 | } else if (e.pointerType === 'touch') { 66 | touchEvent = e; 67 | if (!this._filterClick(e)) { 68 | return; 69 | } 70 | } else { 71 | return; 72 | } 73 | 74 | var containerPoint = this._map.mouseEventToContainerPoint(touchEvent), 75 | layerPoint = this._map.mouseEventToLayerPoint(touchEvent), 76 | latlng = this._map.layerPointToLatLng(layerPoint); 77 | 78 | this._map.fire(type, { 79 | latlng: latlng, 80 | layerPoint: layerPoint, 81 | containerPoint: containerPoint, 82 | pageX: touchEvent.pageX, 83 | pageY: touchEvent.pageY, 84 | originalEvent: e 85 | }); 86 | }, 87 | 88 | /** Borrowed from Leaflet and modified for bool ops **/ 89 | _filterClick: function (e) { 90 | var timeStamp = (e.timeStamp || e.originalEvent.timeStamp), 91 | elapsed = L.DomEvent._lastClick && (timeStamp - L.DomEvent._lastClick); 92 | 93 | // are they closer together than 500ms yet more than 100ms? 94 | // Android typically triggers them ~300ms apart while multiple listeners 95 | // on the same event should be triggered far faster; 96 | // or check if click is simulated on the element, and if it is, reject any non-simulated events 97 | if ((elapsed && elapsed > 100 && elapsed < 500) || (e.target._simulatedClick && !e._simulated)) { 98 | L.DomEvent.stop(e); 99 | return false; 100 | } 101 | L.DomEvent._lastClick = timeStamp; 102 | return true; 103 | }, 104 | 105 | _onTouchStart: function (e) { 106 | if (!this._map._loaded) { 107 | return; 108 | } 109 | 110 | var type = 'touchstart'; 111 | this._touchEvent(e, type); 112 | 113 | }, 114 | 115 | _onTouchEnd: function (e) { 116 | if (!this._map._loaded) { 117 | return; 118 | } 119 | 120 | var type = 'touchend'; 121 | this._touchEvent(e, type); 122 | }, 123 | 124 | _onTouchCancel: function (e) { 125 | if (!this._map._loaded) { 126 | return; 127 | } 128 | 129 | var type = 'touchcancel'; 130 | if (this._detectIE()) { 131 | type = 'pointercancel'; 132 | } 133 | this._touchEvent(e, type); 134 | }, 135 | 136 | _onTouchLeave: function (e) { 137 | if (!this._map._loaded) { 138 | return; 139 | } 140 | 141 | var type = 'touchleave'; 142 | this._touchEvent(e, type); 143 | }, 144 | 145 | _onTouchMove: function (e) { 146 | if (!this._map._loaded) { 147 | return; 148 | } 149 | 150 | var type = 'touchmove'; 151 | this._touchEvent(e, type); 152 | }, 153 | 154 | _detectIE: function () { 155 | var ua = window.navigator.userAgent; 156 | 157 | var msie = ua.indexOf('MSIE '); 158 | if (msie > 0) { 159 | // IE 10 or older => return version number 160 | return parseInt(ua.substring(msie + 5, ua.indexOf('.', msie)), 10); 161 | } 162 | 163 | var trident = ua.indexOf('Trident/'); 164 | if (trident > 0) { 165 | // IE 11 => return version number 166 | var rv = ua.indexOf('rv:'); 167 | return parseInt(ua.substring(rv + 3, ua.indexOf('.', rv)), 10); 168 | } 169 | 170 | var edge = ua.indexOf('Edge/'); 171 | if (edge > 0) { 172 | // IE 12 => return version number 173 | return parseInt(ua.substring(edge + 5, ua.indexOf('.', edge)), 10); 174 | } 175 | 176 | // other browser 177 | return false; 178 | } 179 | }); 180 | 181 | L.Map.addInitHook('addHandler', 'touchExtend', L.Map.TouchExtend); 182 | 183 | 184 | /** 185 | * @class L.Marker.Touch 186 | * @aka Marker.Touch 187 | * 188 | * This isn't full Touch support. This is just to get markers to also support dom touch events after creation 189 | * #TODO: find a better way of getting markers to support touch. 190 | */ 191 | L.Marker.Touch = L.Marker.extend({ 192 | 193 | _initInteraction: function () { 194 | if (!this.addInteractiveTarget) { 195 | // 0.7.x support 196 | return this._initInteractionLegacy(); 197 | } 198 | // TODO this may need be updated to re-add touch events for 1.0+ 199 | return L.Marker.prototype._initInteraction.apply(this); 200 | }, 201 | 202 | // This is an exact copy of https://github.com/Leaflet/Leaflet/blob/v0.7/src/layer/marker/Marker.js 203 | // with the addition of the touch events 204 | _initInteractionLegacy: function () { 205 | 206 | if (!this.options.clickable) { 207 | return; 208 | } 209 | 210 | // TODO refactor into something shared with Map/Path/etc. to DRY it up 211 | 212 | var icon = this._icon, 213 | events = ['dblclick', 214 | 'mousedown', 215 | 'mouseover', 216 | 'mouseout', 217 | 'contextmenu', 218 | 'touchstart', 219 | 'touchend', 220 | 'touchmove']; 221 | if (this._detectIE) { 222 | events.concat(['MSPointerDown', 223 | 'MSPointerUp', 224 | 'MSPointerMove', 225 | 'MSPointerCancel']); 226 | } else { 227 | events.concat(['touchcancel']); 228 | } 229 | 230 | L.DomUtil.addClass(icon, 'leaflet-clickable'); 231 | L.DomEvent.on(icon, 'click', this._onMouseClick, this); 232 | L.DomEvent.on(icon, 'keypress', this._onKeyPress, this); 233 | 234 | for (var i = 0; i < events.length; i++) { 235 | L.DomEvent.on(icon, events[i], this._fireMouseEvent, this); 236 | } 237 | 238 | if (L.Handler.MarkerDrag) { 239 | this.dragging = new L.Handler.MarkerDrag(this); 240 | 241 | if (this.options.draggable) { 242 | this.dragging.enable(); 243 | } 244 | } 245 | }, 246 | 247 | _detectIE: function () { 248 | var ua = window.navigator.userAgent; 249 | 250 | var msie = ua.indexOf('MSIE '); 251 | if (msie > 0) { 252 | // IE 10 or older => return version number 253 | return parseInt(ua.substring(msie + 5, ua.indexOf('.', msie)), 10); 254 | } 255 | 256 | var trident = ua.indexOf('Trident/'); 257 | if (trident > 0) { 258 | // IE 11 => return version number 259 | var rv = ua.indexOf('rv:'); 260 | return parseInt(ua.substring(rv + 3, ua.indexOf('.', rv)), 10); 261 | } 262 | 263 | var edge = ua.indexOf('Edge/'); 264 | if (edge > 0) { 265 | // IE 12 => return version number 266 | return parseInt(ua.substring(edge + 5, ua.indexOf('.', edge)), 10); 267 | } 268 | 269 | // other browser 270 | return false; 271 | } 272 | }); 273 | -------------------------------------------------------------------------------- /static_files/draw_files/jquery.cookie.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery Cookie Plugin v1.4.0 3 | * https://github.com/carhartl/jquery-cookie 4 | * 5 | * Copyright 2013 Klaus Hartl 6 | * Released under the MIT license 7 | */ 8 | (function (factory) { 9 | if (typeof define === 'function' && define.amd) { 10 | // AMD. Register as anonymous module. 11 | define(['jquery'], factory); 12 | } else { 13 | // Browser globals. 14 | factory(jQuery); 15 | } 16 | }(function ($) { 17 | 18 | var pluses = /\+/g; 19 | 20 | function encode(s) { 21 | return config.raw ? s : encodeURIComponent(s); 22 | } 23 | 24 | function decode(s) { 25 | return config.raw ? s : decodeURIComponent(s); 26 | } 27 | 28 | function stringifyCookieValue(value) { 29 | return encode(config.json ? JSON.stringify(value) : String(value)); 30 | } 31 | 32 | function parseCookieValue(s) { 33 | if (s.indexOf('"') === 0) { 34 | // This is a quoted cookie as according to RFC2068, unescape... 35 | s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\'); 36 | } 37 | 38 | try { 39 | // Replace server-side written pluses with spaces. 40 | // If we can't decode the cookie, ignore it, it's unusable. 41 | s = decodeURIComponent(s.replace(pluses, ' ')); 42 | } catch(e) { 43 | return; 44 | } 45 | 46 | try { 47 | // If we can't parse the cookie, ignore it, it's unusable. 48 | return config.json ? JSON.parse(s) : s; 49 | } catch(e) {} 50 | } 51 | 52 | function read(s, converter) { 53 | var value = config.raw ? s : parseCookieValue(s); 54 | return $.isFunction(converter) ? converter(value) : value; 55 | } 56 | 57 | var config = $.cookie = function (key, value, options) { 58 | 59 | // Write 60 | if (value !== undefined && !$.isFunction(value)) { 61 | options = $.extend({}, config.defaults, options); 62 | 63 | if (typeof options.expires === 'number') { 64 | var days = options.expires, t = options.expires = new Date(); 65 | t.setDate(t.getDate() + days); 66 | } 67 | 68 | return (document.cookie = [ 69 | encode(key), '=', stringifyCookieValue(value), 70 | options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE 71 | options.path ? '; path=' + options.path : '', 72 | options.domain ? '; domain=' + options.domain : '', 73 | options.secure ? '; secure' : '' 74 | ].join('')); 75 | } 76 | 77 | // Read 78 | 79 | var result = key ? undefined : {}; 80 | 81 | // To prevent the for loop in the first place assign an empty array 82 | // in case there are no cookies at all. Also prevents odd result when 83 | // calling $.cookie(). 84 | var cookies = document.cookie ? document.cookie.split('; ') : []; 85 | 86 | for (var i = 0, l = cookies.length; i < l; i++) { 87 | var parts = cookies[i].split('='); 88 | var name = decode(parts.shift()); 89 | var cookie = parts.join('='); 90 | 91 | if (key && key === name) { 92 | // If second argument (value) is a function it's a converter... 93 | result = read(cookie, value); 94 | break; 95 | } 96 | 97 | // Prevent storing a cookie that we couldn't decode. 98 | if (!key && (cookie = read(cookie)) !== undefined) { 99 | result[name] = cookie; 100 | } 101 | } 102 | 103 | return result; 104 | }; 105 | 106 | config.defaults = {}; 107 | 108 | $.removeCookie = function (key, options) { 109 | if ($.cookie(key) !== undefined) { 110 | // Must not alter options, thus extending a fresh object... 111 | $.cookie(key, '', $.extend({}, options, { expires: -1 })); 112 | return true; 113 | } 114 | return false; 115 | }; 116 | 117 | })); 118 | -------------------------------------------------------------------------------- /static_files/draw_files/leaflet.css: -------------------------------------------------------------------------------- 1 | /* required styles */ 2 | 3 | .leaflet-pane, 4 | .leaflet-tile, 5 | .leaflet-marker-icon, 6 | .leaflet-marker-shadow, 7 | .leaflet-tile-container, 8 | .leaflet-pane > svg, 9 | .leaflet-pane > canvas, 10 | .leaflet-zoom-box, 11 | .leaflet-image-layer, 12 | .leaflet-layer { 13 | position: absolute; 14 | left: 0; 15 | top: 0; 16 | } 17 | .leaflet-container { 18 | overflow: hidden; 19 | } 20 | .leaflet-tile, 21 | .leaflet-marker-icon, 22 | .leaflet-marker-shadow { 23 | -webkit-user-select: none; 24 | -moz-user-select: none; 25 | user-select: none; 26 | -webkit-user-drag: none; 27 | } 28 | /* Safari renders non-retina tile on retina better with this, but Chrome is worse */ 29 | .leaflet-safari .leaflet-tile { 30 | image-rendering: -webkit-optimize-contrast; 31 | } 32 | /* hack that prevents hw layers "stretching" when loading new tiles */ 33 | .leaflet-safari .leaflet-tile-container { 34 | width: 1600px; 35 | height: 1600px; 36 | -webkit-transform-origin: 0 0; 37 | } 38 | .leaflet-marker-icon, 39 | .leaflet-marker-shadow { 40 | display: block; 41 | } 42 | /* .leaflet-container svg: reset svg max-width decleration shipped in Joomla! (joomla.org) 3.x */ 43 | /* .leaflet-container img: map is broken in FF if you have max-width: 100% on tiles */ 44 | .leaflet-container .leaflet-overlay-pane svg, 45 | .leaflet-container .leaflet-marker-pane img, 46 | .leaflet-container .leaflet-shadow-pane img, 47 | .leaflet-container .leaflet-tile-pane img, 48 | .leaflet-container img.leaflet-image-layer { 49 | max-width: none !important; 50 | max-height: none !important; 51 | } 52 | 53 | .leaflet-container.leaflet-touch-zoom { 54 | -ms-touch-action: pan-x pan-y; 55 | touch-action: pan-x pan-y; 56 | } 57 | .leaflet-container.leaflet-touch-drag { 58 | -ms-touch-action: pinch-zoom; 59 | /* Fallback for FF which doesn't support pinch-zoom */ 60 | touch-action: none; 61 | touch-action: pinch-zoom; 62 | } 63 | .leaflet-container.leaflet-touch-drag.leaflet-touch-zoom { 64 | -ms-touch-action: none; 65 | touch-action: none; 66 | } 67 | .leaflet-container { 68 | -webkit-tap-highlight-color: transparent; 69 | } 70 | .leaflet-container a { 71 | -webkit-tap-highlight-color: rgba(51, 181, 229, 0.4); 72 | } 73 | .leaflet-tile { 74 | filter: inherit; 75 | visibility: hidden; 76 | } 77 | .leaflet-tile-loaded { 78 | visibility: inherit; 79 | } 80 | .leaflet-zoom-box { 81 | width: 0; 82 | height: 0; 83 | -moz-box-sizing: border-box; 84 | box-sizing: border-box; 85 | z-index: 800; 86 | } 87 | /* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */ 88 | .leaflet-overlay-pane svg { 89 | -moz-user-select: none; 90 | } 91 | 92 | .leaflet-pane { z-index: 400; } 93 | 94 | .leaflet-tile-pane { z-index: 200; } 95 | .leaflet-overlay-pane { z-index: 400; } 96 | .leaflet-shadow-pane { z-index: 500; } 97 | .leaflet-marker-pane { z-index: 600; } 98 | .leaflet-tooltip-pane { z-index: 650; } 99 | .leaflet-popup-pane { z-index: 700; } 100 | 101 | .leaflet-map-pane canvas { z-index: 100; } 102 | .leaflet-map-pane svg { z-index: 200; } 103 | 104 | .leaflet-vml-shape { 105 | width: 1px; 106 | height: 1px; 107 | } 108 | .lvml { 109 | behavior: url(#default#VML); 110 | display: inline-block; 111 | position: absolute; 112 | } 113 | 114 | 115 | /* control positioning */ 116 | 117 | .leaflet-control { 118 | position: relative; 119 | z-index: 800; 120 | pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */ 121 | pointer-events: auto; 122 | } 123 | .leaflet-top, 124 | .leaflet-bottom { 125 | position: absolute; 126 | z-index: 1000; 127 | pointer-events: none; 128 | } 129 | .leaflet-top { 130 | top: 0; 131 | } 132 | .leaflet-right { 133 | right: 0; 134 | } 135 | .leaflet-bottom { 136 | bottom: 0; 137 | } 138 | .leaflet-left { 139 | left: 0; 140 | } 141 | .leaflet-control { 142 | float: left; 143 | clear: both; 144 | } 145 | .leaflet-right .leaflet-control { 146 | float: right; 147 | } 148 | .leaflet-top .leaflet-control { 149 | margin-top: 10px; 150 | } 151 | .leaflet-bottom .leaflet-control { 152 | margin-bottom: 10px; 153 | } 154 | .leaflet-left .leaflet-control { 155 | margin-left: 10px; 156 | } 157 | .leaflet-right .leaflet-control { 158 | margin-right: 10px; 159 | } 160 | 161 | 162 | /* zoom and fade animations */ 163 | 164 | .leaflet-fade-anim .leaflet-tile { 165 | will-change: opacity; 166 | } 167 | .leaflet-fade-anim .leaflet-popup { 168 | opacity: 0; 169 | -webkit-transition: opacity 0.2s linear; 170 | -moz-transition: opacity 0.2s linear; 171 | -o-transition: opacity 0.2s linear; 172 | transition: opacity 0.2s linear; 173 | } 174 | .leaflet-fade-anim .leaflet-map-pane .leaflet-popup { 175 | opacity: 1; 176 | } 177 | .leaflet-zoom-animated { 178 | -webkit-transform-origin: 0 0; 179 | -ms-transform-origin: 0 0; 180 | transform-origin: 0 0; 181 | } 182 | .leaflet-zoom-anim .leaflet-zoom-animated { 183 | will-change: transform; 184 | } 185 | .leaflet-zoom-anim .leaflet-zoom-animated { 186 | -webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1); 187 | -moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1); 188 | -o-transition: -o-transform 0.25s cubic-bezier(0,0,0.25,1); 189 | transition: transform 0.25s cubic-bezier(0,0,0.25,1); 190 | } 191 | .leaflet-zoom-anim .leaflet-tile, 192 | .leaflet-pan-anim .leaflet-tile { 193 | -webkit-transition: none; 194 | -moz-transition: none; 195 | -o-transition: none; 196 | transition: none; 197 | } 198 | 199 | .leaflet-zoom-anim .leaflet-zoom-hide { 200 | visibility: hidden; 201 | } 202 | 203 | 204 | /* cursors */ 205 | 206 | .leaflet-interactive { 207 | cursor: pointer; 208 | } 209 | .leaflet-grab { 210 | cursor: -webkit-grab; 211 | cursor: -moz-grab; 212 | } 213 | .leaflet-crosshair, 214 | .leaflet-crosshair .leaflet-interactive { 215 | cursor: crosshair; 216 | } 217 | .leaflet-popup-pane, 218 | .leaflet-control { 219 | cursor: auto; 220 | } 221 | .leaflet-dragging .leaflet-grab, 222 | .leaflet-dragging .leaflet-grab .leaflet-interactive, 223 | .leaflet-dragging .leaflet-marker-draggable { 224 | cursor: move; 225 | cursor: -webkit-grabbing; 226 | cursor: -moz-grabbing; 227 | } 228 | 229 | /* marker & overlays interactivity */ 230 | .leaflet-marker-icon, 231 | .leaflet-marker-shadow, 232 | .leaflet-image-layer, 233 | .leaflet-pane > svg path, 234 | .leaflet-tile-container { 235 | pointer-events: none; 236 | } 237 | 238 | .leaflet-marker-icon.leaflet-interactive, 239 | .leaflet-image-layer.leaflet-interactive, 240 | .leaflet-pane > svg path.leaflet-interactive { 241 | pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */ 242 | pointer-events: auto; 243 | } 244 | 245 | /* visual tweaks */ 246 | 247 | .leaflet-container { 248 | background: #ddd; 249 | outline: 0; 250 | } 251 | .leaflet-container a { 252 | color: #0078A8; 253 | } 254 | .leaflet-container a.leaflet-active { 255 | outline: 2px solid orange; 256 | } 257 | .leaflet-zoom-box { 258 | border: 2px dotted #38f; 259 | background: rgba(255,255,255,0.5); 260 | } 261 | 262 | 263 | /* general typography */ 264 | .leaflet-container { 265 | font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif; 266 | } 267 | 268 | 269 | /* general toolbar styles */ 270 | 271 | .leaflet-bar { 272 | box-shadow: 0 1px 5px rgba(0,0,0,0.65); 273 | border-radius: 4px; 274 | } 275 | .leaflet-bar a, 276 | .leaflet-bar a:hover { 277 | background-color: #fff; 278 | border-bottom: 1px solid #ccc; 279 | width: 26px; 280 | height: 26px; 281 | line-height: 26px; 282 | display: block; 283 | text-align: center; 284 | text-decoration: none; 285 | color: black; 286 | } 287 | .leaflet-bar a, 288 | .leaflet-control-layers-toggle { 289 | background-position: 50% 50%; 290 | background-repeat: no-repeat; 291 | display: block; 292 | } 293 | .leaflet-bar a:hover { 294 | background-color: #f4f4f4; 295 | } 296 | .leaflet-bar a:first-child { 297 | border-top-left-radius: 4px; 298 | border-top-right-radius: 4px; 299 | } 300 | .leaflet-bar a:last-child { 301 | border-bottom-left-radius: 4px; 302 | border-bottom-right-radius: 4px; 303 | border-bottom: none; 304 | } 305 | .leaflet-bar a.leaflet-disabled { 306 | cursor: default; 307 | background-color: #f4f4f4; 308 | color: #bbb; 309 | } 310 | 311 | .leaflet-touch .leaflet-bar a { 312 | width: 30px; 313 | height: 30px; 314 | line-height: 30px; 315 | } 316 | .leaflet-touch .leaflet-bar a:first-child { 317 | border-top-left-radius: 2px; 318 | border-top-right-radius: 2px; 319 | } 320 | .leaflet-touch .leaflet-bar a:last-child { 321 | border-bottom-left-radius: 2px; 322 | border-bottom-right-radius: 2px; 323 | } 324 | 325 | /* zoom control */ 326 | 327 | .leaflet-control-zoom-in, 328 | .leaflet-control-zoom-out { 329 | font: bold 18px 'Lucida Console', Monaco, monospace; 330 | text-indent: 1px; 331 | } 332 | 333 | .leaflet-touch .leaflet-control-zoom-in, .leaflet-touch .leaflet-control-zoom-out { 334 | font-size: 22px; 335 | } 336 | 337 | 338 | /* layers control */ 339 | 340 | .leaflet-control-layers { 341 | box-shadow: 0 1px 5px rgba(0,0,0,0.4); 342 | background: #fff; 343 | border-radius: 5px; 344 | } 345 | .leaflet-control-layers-toggle { 346 | background-image: url(images/layers.png); 347 | width: 36px; 348 | height: 36px; 349 | } 350 | .leaflet-retina .leaflet-control-layers-toggle { 351 | background-image: url(images/layers-2x.png); 352 | background-size: 26px 26px; 353 | } 354 | .leaflet-touch .leaflet-control-layers-toggle { 355 | width: 44px; 356 | height: 44px; 357 | } 358 | .leaflet-control-layers .leaflet-control-layers-list, 359 | .leaflet-control-layers-expanded .leaflet-control-layers-toggle { 360 | display: none; 361 | } 362 | .leaflet-control-layers-expanded .leaflet-control-layers-list { 363 | display: block; 364 | position: relative; 365 | } 366 | .leaflet-control-layers-expanded { 367 | padding: 6px 10px 6px 6px; 368 | color: #333; 369 | background: #fff; 370 | } 371 | .leaflet-control-layers-scrollbar { 372 | overflow-y: scroll; 373 | overflow-x: hidden; 374 | padding-right: 5px; 375 | } 376 | .leaflet-control-layers-selector { 377 | margin-top: 2px; 378 | position: relative; 379 | top: 1px; 380 | } 381 | .leaflet-control-layers label { 382 | display: block; 383 | } 384 | .leaflet-control-layers-separator { 385 | height: 0; 386 | border-top: 1px solid #ddd; 387 | margin: 5px -10px 5px -6px; 388 | } 389 | 390 | /* Default icon URLs */ 391 | .leaflet-default-icon-path { 392 | background-image: url(images/marker-icon.png); 393 | } 394 | 395 | 396 | /* attribution and scale controls */ 397 | 398 | .leaflet-container .leaflet-control-attribution { 399 | background: #fff; 400 | background: rgba(255, 255, 255, 0.7); 401 | margin: 0; 402 | } 403 | .leaflet-control-attribution, 404 | .leaflet-control-scale-line { 405 | padding: 0 5px; 406 | color: #333; 407 | } 408 | .leaflet-control-attribution a { 409 | text-decoration: none; 410 | } 411 | .leaflet-control-attribution a:hover { 412 | text-decoration: underline; 413 | } 414 | .leaflet-container .leaflet-control-attribution, 415 | .leaflet-container .leaflet-control-scale { 416 | font-size: 11px; 417 | } 418 | .leaflet-left .leaflet-control-scale { 419 | margin-left: 5px; 420 | } 421 | .leaflet-bottom .leaflet-control-scale { 422 | margin-bottom: 5px; 423 | } 424 | .leaflet-control-scale-line { 425 | border: 2px solid #777; 426 | border-top: none; 427 | line-height: 1.1; 428 | padding: 2px 5px 1px; 429 | font-size: 11px; 430 | white-space: nowrap; 431 | overflow: hidden; 432 | -moz-box-sizing: border-box; 433 | box-sizing: border-box; 434 | 435 | background: #fff; 436 | background: rgba(255, 255, 255, 0.5); 437 | } 438 | .leaflet-control-scale-line:not(:first-child) { 439 | border-top: 2px solid #777; 440 | border-bottom: none; 441 | margin-top: -2px; 442 | } 443 | .leaflet-control-scale-line:not(:first-child):not(:last-child) { 444 | border-bottom: 2px solid #777; 445 | } 446 | 447 | .leaflet-touch .leaflet-control-attribution, 448 | .leaflet-touch .leaflet-control-layers, 449 | .leaflet-touch .leaflet-bar { 450 | box-shadow: none; 451 | } 452 | .leaflet-touch .leaflet-control-layers, 453 | .leaflet-touch .leaflet-bar { 454 | border: 2px solid rgba(0,0,0,0.2); 455 | background-clip: padding-box; 456 | } 457 | 458 | 459 | /* popup */ 460 | 461 | .leaflet-popup { 462 | position: absolute; 463 | text-align: center; 464 | margin-bottom: 20px; 465 | } 466 | .leaflet-popup-content-wrapper { 467 | padding: 1px; 468 | text-align: left; 469 | border-radius: 12px; 470 | } 471 | .leaflet-popup-content { 472 | margin: 13px 19px; 473 | line-height: 1.4; 474 | } 475 | .leaflet-popup-content p { 476 | margin: 18px 0; 477 | } 478 | .leaflet-popup-tip-container { 479 | width: 40px; 480 | height: 20px; 481 | position: absolute; 482 | left: 50%; 483 | margin-left: -20px; 484 | overflow: hidden; 485 | pointer-events: none; 486 | } 487 | .leaflet-popup-tip { 488 | width: 17px; 489 | height: 17px; 490 | padding: 1px; 491 | 492 | margin: -10px auto 0; 493 | 494 | -webkit-transform: rotate(45deg); 495 | -moz-transform: rotate(45deg); 496 | -ms-transform: rotate(45deg); 497 | -o-transform: rotate(45deg); 498 | transform: rotate(45deg); 499 | } 500 | .leaflet-popup-content-wrapper, 501 | .leaflet-popup-tip { 502 | background: white; 503 | color: #333; 504 | box-shadow: 0 3px 14px rgba(0,0,0,0.4); 505 | } 506 | .leaflet-container a.leaflet-popup-close-button { 507 | position: absolute; 508 | top: 0; 509 | right: 0; 510 | padding: 4px 4px 0 0; 511 | border: none; 512 | text-align: center; 513 | width: 18px; 514 | height: 14px; 515 | font: 16px/14px Tahoma, Verdana, sans-serif; 516 | color: #c3c3c3; 517 | text-decoration: none; 518 | font-weight: bold; 519 | background: transparent; 520 | } 521 | .leaflet-container a.leaflet-popup-close-button:hover { 522 | color: #999; 523 | } 524 | .leaflet-popup-scrolled { 525 | overflow: auto; 526 | border-bottom: 1px solid #ddd; 527 | border-top: 1px solid #ddd; 528 | } 529 | 530 | .leaflet-oldie .leaflet-popup-content-wrapper { 531 | zoom: 1; 532 | } 533 | .leaflet-oldie .leaflet-popup-tip { 534 | width: 24px; 535 | margin: 0 auto; 536 | 537 | -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)"; 538 | filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678); 539 | } 540 | .leaflet-oldie .leaflet-popup-tip-container { 541 | margin-top: -1px; 542 | } 543 | 544 | .leaflet-oldie .leaflet-control-zoom, 545 | .leaflet-oldie .leaflet-control-layers, 546 | .leaflet-oldie .leaflet-popup-content-wrapper, 547 | .leaflet-oldie .leaflet-popup-tip { 548 | border: 1px solid #999; 549 | } 550 | 551 | 552 | /* div icon */ 553 | 554 | .leaflet-div-icon { 555 | background: #fff; 556 | border: 1px solid #666; 557 | } 558 | 559 | 560 | /* Tooltip */ 561 | /* Base styles for the element that has a tooltip */ 562 | .leaflet-tooltip { 563 | position: absolute; 564 | padding: 6px; 565 | background-color: #fff; 566 | border: 1px solid #fff; 567 | border-radius: 3px; 568 | color: #222; 569 | white-space: nowrap; 570 | -webkit-user-select: none; 571 | -moz-user-select: none; 572 | -ms-user-select: none; 573 | user-select: none; 574 | pointer-events: none; 575 | box-shadow: 0 1px 3px rgba(0,0,0,0.4); 576 | } 577 | .leaflet-tooltip.leaflet-clickable { 578 | cursor: pointer; 579 | pointer-events: auto; 580 | } 581 | .leaflet-tooltip-top:before, 582 | .leaflet-tooltip-bottom:before, 583 | .leaflet-tooltip-left:before, 584 | .leaflet-tooltip-right:before { 585 | position: absolute; 586 | pointer-events: none; 587 | border: 6px solid transparent; 588 | background: transparent; 589 | content: ""; 590 | } 591 | 592 | /* Directions */ 593 | 594 | .leaflet-tooltip-bottom { 595 | margin-top: 6px; 596 | } 597 | .leaflet-tooltip-top { 598 | margin-top: -6px; 599 | } 600 | .leaflet-tooltip-bottom:before, 601 | .leaflet-tooltip-top:before { 602 | left: 50%; 603 | margin-left: -6px; 604 | } 605 | .leaflet-tooltip-top:before { 606 | bottom: 0; 607 | margin-bottom: -12px; 608 | border-top-color: #fff; 609 | } 610 | .leaflet-tooltip-bottom:before { 611 | top: 0; 612 | margin-top: -12px; 613 | margin-left: -6px; 614 | border-bottom-color: #fff; 615 | } 616 | .leaflet-tooltip-left { 617 | margin-left: -6px; 618 | } 619 | .leaflet-tooltip-right { 620 | margin-left: 6px; 621 | } 622 | .leaflet-tooltip-left:before, 623 | .leaflet-tooltip-right:before { 624 | top: 50%; 625 | margin-top: -6px; 626 | } 627 | .leaflet-tooltip-left:before { 628 | right: 0; 629 | margin-right: -12px; 630 | border-left-color: #fff; 631 | } 632 | .leaflet-tooltip-right:before { 633 | left: 0; 634 | margin-left: -12px; 635 | border-right-color: #fff; 636 | } 637 | -------------------------------------------------------------------------------- /static_files/draw_files/leaflet.draw.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('./spritesheet.png'); 27 | background-image: linear-gradient(transparent, transparent), url('./spritesheet.svg'); 28 | background-repeat: no-repeat; 29 | background-size: 300px 30px; 30 | background-clip: padding-box; 31 | } 32 | 33 | .leaflet-retina .leaflet-draw-toolbar a { 34 | background-image: url('./spritesheet-2x.png'); 35 | background-image: linear-gradient(transparent, transparent), url('./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 | .leaflet-draw-toolbar .leaflet-draw-draw-circlemarker { 193 | background-position: -273px -2px; 194 | } 195 | 196 | .leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-circlemarker { 197 | background-position: -271px -1px; 198 | } 199 | 200 | /* ================================================================== */ 201 | /* Edit toolbar 202 | /* ================================================================== */ 203 | 204 | .leaflet-draw-toolbar .leaflet-draw-edit-edit { 205 | background-position: -152px -2px; 206 | } 207 | 208 | .leaflet-touch .leaflet-draw-toolbar .leaflet-draw-edit-edit { 209 | background-position: -150px -1px; 210 | } 211 | 212 | .leaflet-draw-toolbar .leaflet-draw-edit-remove { 213 | background-position: -182px -2px; 214 | } 215 | 216 | .leaflet-touch .leaflet-draw-toolbar .leaflet-draw-edit-remove { 217 | background-position: -180px -1px; 218 | } 219 | 220 | .leaflet-draw-toolbar .leaflet-draw-edit-edit.leaflet-disabled { 221 | background-position: -212px -2px; 222 | } 223 | 224 | .leaflet-touch .leaflet-draw-toolbar .leaflet-draw-edit-edit.leaflet-disabled { 225 | background-position: -210px -1px; 226 | } 227 | 228 | .leaflet-draw-toolbar .leaflet-draw-edit-remove.leaflet-disabled { 229 | background-position: -242px -2px; 230 | } 231 | 232 | .leaflet-touch .leaflet-draw-toolbar .leaflet-draw-edit-remove.leaflet-disabled { 233 | background-position: -240px -2px; 234 | } 235 | 236 | /* ================================================================== */ 237 | /* Drawing styles 238 | /* ================================================================== */ 239 | 240 | .leaflet-mouse-marker { 241 | background-color: #fff; 242 | cursor: crosshair; 243 | } 244 | 245 | .leaflet-draw-tooltip { 246 | background: rgb(54, 54, 54); 247 | background: rgba(0, 0, 0, 0.5); 248 | border: 1px solid transparent; 249 | -webkit-border-radius: 4px; 250 | border-radius: 4px; 251 | color: #fff; 252 | font: 12px/18px "Helvetica Neue", Arial, Helvetica, sans-serif; 253 | margin-left: 20px; 254 | margin-top: -21px; 255 | padding: 4px 8px; 256 | position: absolute; 257 | visibility: hidden; 258 | white-space: nowrap; 259 | z-index: 6; 260 | } 261 | 262 | .leaflet-draw-tooltip:before { 263 | border-right: 6px solid black; 264 | border-right-color: rgba(0, 0, 0, 0.5); 265 | border-top: 6px solid transparent; 266 | border-bottom: 6px solid transparent; 267 | content: ""; 268 | position: absolute; 269 | top: 7px; 270 | left: -7px; 271 | } 272 | 273 | .leaflet-error-draw-tooltip { 274 | background-color: #F2DEDE; 275 | border: 1px solid #E6B6BD; 276 | color: #B94A48; 277 | } 278 | 279 | .leaflet-error-draw-tooltip:before { 280 | border-right-color: #E6B6BD; 281 | } 282 | 283 | .leaflet-draw-tooltip-single { 284 | margin-top: -12px 285 | } 286 | 287 | .leaflet-draw-tooltip-subtext { 288 | color: #f8d5e4; 289 | } 290 | 291 | .leaflet-draw-guide-dash { 292 | font-size: 1%; 293 | opacity: 0.6; 294 | position: absolute; 295 | width: 5px; 296 | height: 5px; 297 | } 298 | 299 | /* ================================================================== */ 300 | /* Edit styles 301 | /* ================================================================== */ 302 | 303 | .leaflet-edit-marker-selected { 304 | background-color: rgba(254, 87, 161, 0.1); 305 | border: 4px dashed rgba(254, 87, 161, 0.6); 306 | -webkit-border-radius: 4px; 307 | border-radius: 4px; 308 | box-sizing: content-box; 309 | } 310 | 311 | .leaflet-edit-move { 312 | cursor: move; 313 | } 314 | 315 | .leaflet-edit-resize { 316 | cursor: pointer; 317 | } 318 | 319 | /* ================================================================== */ 320 | /* Old IE styles 321 | /* ================================================================== */ 322 | 323 | .leaflet-oldie .leaflet-draw-toolbar { 324 | border: 1px solid #999; 325 | } 326 | -------------------------------------------------------------------------------- /static_files/draw_files/spritesheet-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yimengyao13/geoserver_django_leaflet/2efa16ab245481391434ffa644658ced04ce07ba/static_files/draw_files/spritesheet-2x.png -------------------------------------------------------------------------------- /static_files/draw_files/spritesheet.svg: -------------------------------------------------------------------------------- 1 | 2 | 20 | 22 | 23 | 25 | image/svg+xml 26 | 28 | 29 | 30 | 31 | 32 | 34 | 54 | 57 | 60 | 65 | 70 | 75 | 76 | 81 | 86 | 91 | 96 | 99 | 104 | 109 | 115 | 116 | 119 | 124 | 129 | 130 | 131 | 135 | 142 | 149 | 150 | 155 | 156 | -------------------------------------------------------------------------------- /static_files/leaflet.css: -------------------------------------------------------------------------------- 1 | /* required styles 2 | 所需风格*/ 3 | 4 | .leaflet-pane, 5 | .leaflet-tile, 6 | .leaflet-marker-icon, 7 | .leaflet-marker-shadow, 8 | .leaflet-tile-container, 9 | .leaflet-pane > svg, 10 | .leaflet-pane > canvas, 11 | .leaflet-zoom-box, 12 | .leaflet-image-layer, 13 | .leaflet-layer { 14 | position: absolute; 15 | left: 0; 16 | top: 0; 17 | } 18 | .leaflet-container { 19 | overflow: hidden; 20 | } 21 | .leaflet-tile, 22 | .leaflet-marker-icon, 23 | .leaflet-marker-shadow { 24 | -webkit-user-select: none; 25 | -moz-user-select: none; 26 | user-select: none; 27 | -webkit-user-drag: none; 28 | } 29 | /* Prevents IE11 from highlighting tiles in blue 30 | 防止IE11用蓝色高亮显示tile */ 31 | .leaflet-tile::selection { 32 | background: transparent; 33 | } 34 | /* Safari renders non-retina tile on retina better with this, but Chrome is worse 35 | Safari用这个在视网膜上显示非视网膜贴图效果更好,但Chrome就更差了 */ 36 | .leaflet-safari .leaflet-tile { 37 | image-rendering: -webkit-optimize-contrast; 38 | } 39 | /* hack that prevents hw layers "stretching" when loading new tiles 40 | hack防止hw层“拉伸”时加载新瓷砖 */ 41 | .leaflet-safari .leaflet-tile-container { 42 | width: 1600px; 43 | height: 1600px; 44 | -webkit-transform-origin: 0 0; 45 | } 46 | .leaflet-marker-icon, 47 | .leaflet-marker-shadow { 48 | display: block; 49 | } 50 | /* .leaflet-container svg: 51 | reset svg max-width decleration shipped in Joomla! (joomla.org) 3.x 52 | 在Joomla中运行的时候,重置svg的max-width*/ 53 | /* .leaflet-container img: 54 | map is broken in FF if you have max-width: 100% on tiles 55 | 如果在瓦片中使用max-width=100%,地图就会在FF中中断*/ 56 | .leaflet-container .leaflet-overlay-pane svg, 57 | .leaflet-container .leaflet-marker-pane img, 58 | .leaflet-container .leaflet-shadow-pane img, 59 | .leaflet-container .leaflet-tile-pane img, 60 | .leaflet-container img.leaflet-image-layer, 61 | .leaflet-container .leaflet-tile { 62 | max-width: none !important; 63 | max-height: none !important; 64 | } 65 | 66 | .leaflet-container.leaflet-touch-zoom { 67 | -ms-touch-action: pan-x pan-y; 68 | touch-action: pan-x pan-y; 69 | } 70 | .leaflet-container.leaflet-touch-drag { 71 | -ms-touch-action: pinch-zoom; 72 | /* Fallback for FF which doesn't support pinch-zoom 73 | FF的Fallback不支持缩放 */ 74 | touch-action: none; 75 | touch-action: pinch-zoom; 76 | } 77 | .leaflet-container.leaflet-touch-drag.leaflet-touch-zoom { 78 | -ms-touch-action: none; 79 | touch-action: none; 80 | } 81 | .leaflet-container { 82 | -webkit-tap-highlight-color: transparent; 83 | } 84 | .leaflet-container a { 85 | -webkit-tap-highlight-color: rgba(51, 181, 229, 0.4); 86 | } 87 | .leaflet-tile { 88 | filter: inherit; 89 | visibility: hidden; 90 | } 91 | .leaflet-tile-loaded { 92 | visibility: inherit; 93 | } 94 | .leaflet-zoom-box { 95 | width: 0; 96 | height: 0; 97 | -moz-box-sizing: border-box; 98 | box-sizing: border-box; 99 | z-index: 800; 100 | } 101 | /* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 102 | 解决方案见网址 */ 103 | .leaflet-overlay-pane svg { 104 | -moz-user-select: none; 105 | } 106 | 107 | .leaflet-pane { z-index: 400; } 108 | 109 | .leaflet-tile-pane { z-index: 200; } 110 | .leaflet-overlay-pane { z-index: 400; } 111 | .leaflet-shadow-pane { z-index: 500; } 112 | .leaflet-marker-pane { z-index: 600; } 113 | .leaflet-tooltip-pane { z-index: 650; } 114 | .leaflet-popup-pane { z-index: 700; } 115 | 116 | .leaflet-map-pane canvas { z-index: 100; } 117 | .leaflet-map-pane svg { z-index: 200; } 118 | 119 | .leaflet-vml-shape { 120 | width: 1px; 121 | height: 1px; 122 | } 123 | .lvml { 124 | behavior: url(#default#VML); 125 | display: inline-block; 126 | position: absolute; 127 | } 128 | 129 | 130 | /* control positioning 131 | 控制控件位置*/ 132 | 133 | .leaflet-control { 134 | position: relative; 135 | z-index: 800; 136 | pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */ 137 | pointer-events: auto; 138 | } 139 | .leaflet-top, 140 | .leaflet-bottom { 141 | position: absolute; 142 | z-index: 1000; 143 | pointer-events: none; 144 | } 145 | .leaflet-top { 146 | top: 0; 147 | } 148 | .leaflet-right { 149 | right: 0; 150 | } 151 | .leaflet-bottom { 152 | bottom: 0; 153 | } 154 | .leaflet-left { 155 | left: 0; 156 | } 157 | .leaflet-control { 158 | float: left; 159 | clear: both; 160 | } 161 | .leaflet-right .leaflet-control { 162 | float: right; 163 | } 164 | .leaflet-top .leaflet-control { 165 | margin-top: 10px; 166 | } 167 | .leaflet-bottom .leaflet-control { 168 | margin-bottom: 10px; 169 | } 170 | .leaflet-left .leaflet-control { 171 | margin-left: 10px; 172 | } 173 | .leaflet-right .leaflet-control { 174 | margin-right: 10px; 175 | } 176 | 177 | 178 | /* zoom and fade animations 179 | 缩放和淡出 */ 180 | 181 | .leaflet-fade-anim .leaflet-tile { 182 | will-change: opacity; 183 | } 184 | .leaflet-fade-anim .leaflet-popup { 185 | opacity: 0; 186 | -webkit-transition: opacity 0.2s linear; 187 | -moz-transition: opacity 0.2s linear; 188 | transition: opacity 0.2s linear; 189 | } 190 | .leaflet-fade-anim .leaflet-map-pane .leaflet-popup { 191 | opacity: 1; 192 | } 193 | .leaflet-zoom-animated { 194 | -webkit-transform-origin: 0 0; 195 | -ms-transform-origin: 0 0; 196 | transform-origin: 0 0; 197 | } 198 | .leaflet-zoom-anim .leaflet-zoom-animated { 199 | will-change: transform; 200 | } 201 | .leaflet-zoom-anim .leaflet-zoom-animated { 202 | -webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1); 203 | -moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1); 204 | transition: transform 0.25s cubic-bezier(0,0,0.25,1); 205 | } 206 | .leaflet-zoom-anim .leaflet-tile, 207 | .leaflet-pan-anim .leaflet-tile { 208 | -webkit-transition: none; 209 | -moz-transition: none; 210 | transition: none; 211 | } 212 | 213 | .leaflet-zoom-anim .leaflet-zoom-hide { 214 | visibility: hidden; 215 | } 216 | 217 | 218 | /* cursors游标 */ 219 | 220 | .leaflet-interactive { 221 | cursor: pointer; 222 | } 223 | .leaflet-grab { 224 | cursor: -webkit-grab; 225 | cursor: -moz-grab; 226 | cursor: grab; 227 | } 228 | .leaflet-crosshair, 229 | .leaflet-crosshair .leaflet-interactive { 230 | cursor: crosshair; 231 | } 232 | .leaflet-popup-pane, 233 | .leaflet-control { 234 | cursor: auto; 235 | } 236 | .leaflet-dragging .leaflet-grab, 237 | .leaflet-dragging .leaflet-grab .leaflet-interactive, 238 | .leaflet-dragging .leaflet-marker-draggable { 239 | cursor: move; 240 | cursor: -webkit-grabbing; 241 | cursor: -moz-grabbing; 242 | cursor: grabbing; 243 | } 244 | 245 | /* marker & overlays interactivity 246 | 标记&覆盖的交互性*/ 247 | .leaflet-marker-icon, 248 | .leaflet-marker-shadow, 249 | .leaflet-image-layer, 250 | .leaflet-pane > svg path, 251 | .leaflet-tile-container { 252 | pointer-events: none; 253 | } 254 | 255 | .leaflet-marker-icon.leaflet-interactive, 256 | .leaflet-image-layer.leaflet-interactive, 257 | .leaflet-pane > svg path.leaflet-interactive, 258 | svg.leaflet-image-layer.leaflet-interactive path { 259 | pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */ 260 | pointer-events: auto; 261 | } 262 | 263 | /* visual tweaks视觉调整 */ 264 | 265 | .leaflet-container { 266 | background: #ddd; 267 | outline: 0; 268 | } 269 | .leaflet-container a { 270 | color: #0078A8; 271 | } 272 | .leaflet-container a.leaflet-active { 273 | outline: 2px solid orange; 274 | } 275 | .leaflet-zoom-box { 276 | border: 2px dotted #38f; 277 | background: rgba(255,255,255,0.5); 278 | } 279 | 280 | 281 | /* general typography 一般的排版*/ 282 | .leaflet-container { 283 | font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif; 284 | } 285 | 286 | 287 | /* general toolbar styles 一般的工具条样式 */ 288 | 289 | .leaflet-bar { 290 | box-shadow: 0 1px 5px rgba(0,0,0,0.65); 291 | border-radius: 4px; 292 | } 293 | .leaflet-bar a, 294 | .leaflet-bar a:hover { 295 | background-color: #fff; 296 | border-bottom: 1px solid #ccc; 297 | width: 26px; 298 | height: 26px; 299 | line-height: 26px; 300 | display: block; 301 | text-align: center; 302 | text-decoration: none; 303 | color: black; 304 | } 305 | .leaflet-bar a, 306 | .leaflet-control-layers-toggle { 307 | background-position: 50% 50%; 308 | background-repeat: no-repeat; 309 | display: block; 310 | } 311 | .leaflet-bar a:hover { 312 | background-color: #f4f4f4; 313 | } 314 | .leaflet-bar a:first-child { 315 | border-top-left-radius: 4px; 316 | border-top-right-radius: 4px; 317 | } 318 | .leaflet-bar a:last-child { 319 | border-bottom-left-radius: 4px; 320 | border-bottom-right-radius: 4px; 321 | border-bottom: none; 322 | } 323 | .leaflet-bar a.leaflet-disabled { 324 | cursor: default; 325 | background-color: #f4f4f4; 326 | color: #bbb; 327 | } 328 | 329 | .leaflet-touch .leaflet-bar a { 330 | width: 30px; 331 | height: 30px; 332 | line-height: 30px; 333 | } 334 | .leaflet-touch .leaflet-bar a:first-child { 335 | border-top-left-radius: 2px; 336 | border-top-right-radius: 2px; 337 | } 338 | .leaflet-touch .leaflet-bar a:last-child { 339 | border-bottom-left-radius: 2px; 340 | border-bottom-right-radius: 2px; 341 | } 342 | 343 | /* zoom control 缩放控件*/ 344 | 345 | .leaflet-control-zoom-in, 346 | .leaflet-control-zoom-out { 347 | font: bold 18px 'Lucida Console', Monaco, monospace; 348 | text-indent: 1px; 349 | } 350 | 351 | .leaflet-touch .leaflet-control-zoom-in, .leaflet-touch .leaflet-control-zoom-out { 352 | font-size: 22px; 353 | } 354 | 355 | 356 | /* layers control 图层控件*/ 357 | 358 | .leaflet-control-layers { 359 | box-shadow: 0 1px 5px rgba(0,0,0,0.4); 360 | background: #fff; 361 | border-radius: 5px; 362 | } 363 | .leaflet-control-layers-toggle { 364 | background-image: url(images/layers.png); 365 | width: 36px; 366 | height: 36px; 367 | } 368 | .leaflet-retina .leaflet-control-layers-toggle { 369 | background-image: url(images/layers-2x.png); 370 | background-size: 26px 26px; 371 | } 372 | .leaflet-touch .leaflet-control-layers-toggle { 373 | width: 44px; 374 | height: 44px; 375 | } 376 | .leaflet-control-layers .leaflet-control-layers-list, 377 | .leaflet-control-layers-expanded .leaflet-control-layers-toggle { 378 | display: none; 379 | } 380 | .leaflet-control-layers-expanded .leaflet-control-layers-list { 381 | display: block; 382 | position: relative; 383 | } 384 | .leaflet-control-layers-expanded { 385 | padding: 6px 10px 6px 6px; 386 | color: #333; 387 | background: #fff; 388 | } 389 | .leaflet-control-layers-scrollbar { 390 | overflow-y: scroll; 391 | overflow-x: hidden; 392 | padding-right: 5px; 393 | } 394 | .leaflet-control-layers-selector { 395 | margin-top: 2px; 396 | position: relative; 397 | top: 1px; 398 | } 399 | .leaflet-control-layers label { 400 | display: block; 401 | } 402 | .leaflet-control-layers-separator { 403 | height: 0; 404 | border-top: 1px solid #ddd; 405 | margin: 5px -10px 5px -6px; 406 | } 407 | 408 | /* Default icon URLs 默认的icon url */ 409 | .leaflet-default-icon-path { 410 | background-image: url(images/marker-icon.png); 411 | } 412 | 413 | 414 | /* attribution and scale controls 归因和比例尺控件*/ 415 | 416 | .leaflet-container .leaflet-control-attribution { 417 | background: #fff; 418 | background: rgba(255, 255, 255, 0.7); 419 | margin: 0; 420 | } 421 | .leaflet-control-attribution, 422 | .leaflet-control-scale-line { 423 | padding: 0 5px; 424 | color: #333; 425 | } 426 | .leaflet-control-attribution a { 427 | text-decoration: none; 428 | } 429 | .leaflet-control-attribution a:hover { 430 | text-decoration: underline; 431 | } 432 | .leaflet-container .leaflet-control-attribution, 433 | .leaflet-container .leaflet-control-scale { 434 | font-size: 11px; 435 | } 436 | .leaflet-left .leaflet-control-scale { 437 | margin-left: 5px; 438 | } 439 | .leaflet-bottom .leaflet-control-scale { 440 | margin-bottom: 5px; 441 | } 442 | .leaflet-control-scale-line { 443 | border: 2px solid #777; 444 | border-top: none; 445 | line-height: 1.1; 446 | padding: 2px 5px 1px; 447 | font-size: 11px; 448 | white-space: nowrap; 449 | overflow: hidden; 450 | -moz-box-sizing: border-box; 451 | box-sizing: border-box; 452 | 453 | background: #fff; 454 | background: rgba(255, 255, 255, 0.5); 455 | } 456 | .leaflet-control-scale-line:not(:first-child) { 457 | border-top: 2px solid #777; 458 | border-bottom: none; 459 | margin-top: -2px; 460 | } 461 | .leaflet-control-scale-line:not(:first-child):not(:last-child) { 462 | border-bottom: 2px solid #777; 463 | } 464 | 465 | .leaflet-touch .leaflet-control-attribution, 466 | .leaflet-touch .leaflet-control-layers, 467 | .leaflet-touch .leaflet-bar { 468 | box-shadow: none; 469 | } 470 | .leaflet-touch .leaflet-control-layers, 471 | .leaflet-touch .leaflet-bar { 472 | border: 2px solid rgba(0,0,0,0.2); 473 | background-clip: padding-box; 474 | } 475 | 476 | 477 | /* popup 弹窗 */ 478 | 479 | .leaflet-popup { 480 | position: absolute; 481 | text-align: center; 482 | margin-bottom: 20px; 483 | } 484 | .leaflet-popup-content-wrapper { 485 | padding: 1px; 486 | text-align: left; 487 | border-radius: 12px; 488 | } 489 | .leaflet-popup-content { 490 | margin: 13px 19px; 491 | line-height: 1.4; 492 | } 493 | .leaflet-popup-content p { 494 | margin: 18px 0; 495 | } 496 | .leaflet-popup-tip-container { 497 | width: 40px; 498 | height: 20px; 499 | position: absolute; 500 | left: 50%; 501 | margin-left: -20px; 502 | overflow: hidden; 503 | pointer-events: none; 504 | } 505 | .leaflet-popup-tip { 506 | width: 17px; 507 | height: 17px; 508 | padding: 1px; 509 | 510 | margin: -10px auto 0; 511 | 512 | -webkit-transform: rotate(45deg); 513 | -moz-transform: rotate(45deg); 514 | -ms-transform: rotate(45deg); 515 | transform: rotate(45deg); 516 | } 517 | .leaflet-popup-content-wrapper, 518 | .leaflet-popup-tip { 519 | background: white; 520 | color: #333; 521 | box-shadow: 0 3px 14px rgba(0,0,0,0.4); 522 | } 523 | .leaflet-container a.leaflet-popup-close-button { 524 | position: absolute; 525 | top: 0; 526 | right: 0; 527 | padding: 4px 4px 0 0; 528 | border: none; 529 | text-align: center; 530 | width: 18px; 531 | height: 14px; 532 | font: 16px/14px Tahoma, Verdana, sans-serif; 533 | color: #c3c3c3; 534 | text-decoration: none; 535 | font-weight: bold; 536 | background: transparent; 537 | } 538 | .leaflet-container a.leaflet-popup-close-button:hover { 539 | color: #999; 540 | } 541 | .leaflet-popup-scrolled { 542 | overflow: auto; 543 | border-bottom: 1px solid #ddd; 544 | border-top: 1px solid #ddd; 545 | } 546 | 547 | .leaflet-oldie .leaflet-popup-content-wrapper { 548 | -ms-zoom: 1; 549 | } 550 | .leaflet-oldie .leaflet-popup-tip { 551 | width: 24px; 552 | margin: 0 auto; 553 | 554 | -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)"; 555 | filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678); 556 | } 557 | .leaflet-oldie .leaflet-popup-tip-container { 558 | margin-top: -1px; 559 | } 560 | 561 | .leaflet-oldie .leaflet-control-zoom, 562 | .leaflet-oldie .leaflet-control-layers, 563 | .leaflet-oldie .leaflet-popup-content-wrapper, 564 | .leaflet-oldie .leaflet-popup-tip { 565 | border: 1px solid #999; 566 | } 567 | 568 | 569 | /* div icon div icon*/ 570 | 571 | .leaflet-div-icon { 572 | background: #fff; 573 | border: 1px solid #666; 574 | } 575 | 576 | 577 | /* Tooltip */ 578 | /* Base styles for the element that has a tooltip 具有工具提示的元素的基本样式 */ 579 | .leaflet-tooltip { 580 | position: absolute; 581 | padding: 6px; 582 | background-color: #fff; 583 | border: 1px solid #fff; 584 | border-radius: 3px; 585 | color: #222; 586 | white-space: nowrap; 587 | -webkit-user-select: none; 588 | -moz-user-select: none; 589 | -ms-user-select: none; 590 | user-select: none; 591 | pointer-events: none; 592 | box-shadow: 0 1px 3px rgba(0,0,0,0.4); 593 | } 594 | .leaflet-tooltip.leaflet-clickable { 595 | cursor: pointer; 596 | pointer-events: auto; 597 | } 598 | .leaflet-tooltip-top:before, 599 | .leaflet-tooltip-bottom:before, 600 | .leaflet-tooltip-left:before, 601 | .leaflet-tooltip-right:before { 602 | position: absolute; 603 | pointer-events: none; 604 | border: 6px solid transparent; 605 | background: transparent; 606 | content: ""; 607 | } 608 | 609 | /* Directions 方向*/ 610 | 611 | .leaflet-tooltip-bottom { 612 | margin-top: 6px; 613 | } 614 | .leaflet-tooltip-top { 615 | margin-top: -6px; 616 | } 617 | .leaflet-tooltip-bottom:before, 618 | .leaflet-tooltip-top:before { 619 | left: 50%; 620 | margin-left: -6px; 621 | } 622 | .leaflet-tooltip-top:before { 623 | bottom: 0; 624 | margin-bottom: -12px; 625 | border-top-color: #fff; 626 | } 627 | .leaflet-tooltip-bottom:before { 628 | top: 0; 629 | margin-top: -12px; 630 | margin-left: -6px; 631 | border-bottom-color: #fff; 632 | } 633 | .leaflet-tooltip-left { 634 | margin-left: -6px; 635 | } 636 | .leaflet-tooltip-right { 637 | margin-left: 6px; 638 | } 639 | .leaflet-tooltip-left:before, 640 | .leaflet-tooltip-right:before { 641 | top: 50%; 642 | margin-top: -6px; 643 | } 644 | .leaflet-tooltip-left:before { 645 | right: 0; 646 | margin-right: -12px; 647 | border-left-color: #fff; 648 | } 649 | .leaflet-tooltip-right:before { 650 | left: 0; 651 | margin-left: -12px; 652 | border-right-color: #fff; 653 | } 654 | -------------------------------------------------------------------------------- /templates/attr_feature.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | leaflet根据查询条件渲染geojson 6 | 7 | 8 | 9 | 10 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /templates/bbox_feature.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | leaflet点击渲染geojson 6 | 7 | 8 | 9 | 10 | 20 | 21 | 22 |
23 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /templates/draw.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | Leaflet绘制修改页面 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 |
52 |
53 |
54 | 55 |
56 |
57 | 58 | 59 |
60 |
61 | 62 | 63 |
64 |
65 | 66 | 67 |
68 |
69 | 70 | 71 |
72 |
73 | 74 |
75 |
76 | 173 | 174 | -------------------------------------------------------------------------------- /templates/remove.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | leaflet根据name删除数据 6 | 7 | 8 | 9 | 10 | 11 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /templates/tile_wmts.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | leaflet地图瓦片 6 | 7 | 8 | 9 | 19 | 20 | 21 |
22 | 38 | 39 | 40 | --------------------------------------------------------------------------------