diff --git a/doc/api/resources/events.rst b/doc/api/resources/events.rst
index a732ffc056..f4ef10e9a3 100644
--- a/doc/api/resources/events.rst
+++ b/doc/api/resources/events.rst
@@ -1,3 +1,9 @@
+.. spelling::
+
+ geo
+ lat
+ lon
+
Events
======
@@ -25,6 +31,8 @@ is_public boolean If ``true``, th
presale_start datetime The date at which the ticket shop opens (or ``null``)
presale_end datetime The date at which the ticket shop closes (or ``null``)
location multi-lingual string The event location (or ``null``)
+geo_lat float Latitude of the location (or ``null``)
+geo_lon float Longitude of the location (or ``null``)
has_subevents boolean ``true`` if the event series feature is active for this
event. Cannot change after event is created.
meta_data object Values set for organizer-specific meta data parameters.
@@ -62,6 +70,10 @@ seat_category_mapping object An object mappi
The attributes ``seating_plan`` and ``seat_category_mapping`` have been added.
+.. versionchanged:: 3.3
+
+ The attributes ``geo_lat`` and ``geo_lon`` have been added.
+
Endpoints
---------
@@ -105,6 +117,8 @@ Endpoints
"presale_start": null,
"presale_end": null,
"location": null,
+ "geo_lat": null,
+ "geo_lon": null,
"has_subevents": false,
"meta_data": {},
"seating_plan": null,
@@ -169,6 +183,8 @@ Endpoints
"presale_start": null,
"presale_end": null,
"location": null,
+ "geo_lat": null,
+ "geo_lon": null,
"has_subevents": false,
"seating_plan": null,
"seat_category_mapping": {},
@@ -220,6 +236,8 @@ Endpoints
"seating_plan": null,
"seat_category_mapping": {},
"location": null,
+ "geo_lat": null,
+ "geo_lon": null,
"has_subevents": false,
"meta_data": {},
"plugins": [
@@ -249,6 +267,8 @@ Endpoints
"presale_start": null,
"presale_end": null,
"location": null,
+ "geo_lat": null,
+ "geo_lon": null,
"seating_plan": null,
"seat_category_mapping": {},
"has_subevents": false,
@@ -300,6 +320,8 @@ Endpoints
"presale_start": null,
"presale_end": null,
"location": null,
+ "geo_lat": null,
+ "geo_lon": null,
"seating_plan": null,
"seat_category_mapping": {},
"has_subevents": false,
@@ -331,6 +353,8 @@ Endpoints
"presale_start": null,
"presale_end": null,
"location": null,
+ "geo_lat": null,
+ "geo_lon": null,
"has_subevents": false,
"seating_plan": null,
"seat_category_mapping": {},
@@ -394,6 +418,8 @@ Endpoints
"presale_start": null,
"presale_end": null,
"location": null,
+ "geo_lat": null,
+ "geo_lon": null,
"has_subevents": false,
"seating_plan": null,
"seat_category_mapping": {},
diff --git a/doc/api/resources/subevents.rst b/doc/api/resources/subevents.rst
index e531f9b237..ee4d5c9a38 100644
--- a/doc/api/resources/subevents.rst
+++ b/doc/api/resources/subevents.rst
@@ -1,3 +1,9 @@
+.. spelling::
+
+ geo
+ lat
+ lon
+
.. _rest-subevents:
Event series dates / Sub-events
@@ -28,6 +34,8 @@ date_admission datetime The sub-event's
presale_start datetime The sub-date at which the ticket shop opens (or ``null``)
presale_end datetime The sub-date at which the ticket shop closes (or ``null``)
location multi-lingual string The sub-event location (or ``null``)
+geo_lat float Latitude of the location (or ``null``)
+geo_lon float Longitude of the location (or ``null``)
item_price_overrides list of objects List of items for which this sub-event overrides the
default price
├ item integer The internal item ID
@@ -62,6 +70,10 @@ seat_category_mapping object An object mappi
The attributes ``seating_plan`` and ``seat_category_mapping`` have been added.
+.. versionchanged:: 3.3
+
+ The attributes ``geo_lat`` and ``geo_lon`` have been added.
+
Endpoints
---------
@@ -104,6 +116,8 @@ Endpoints
"seating_plan": null,
"seat_category_mapping": {},
"location": null,
+ "geo_lat": null,
+ "geo_lon": null,
"item_price_overrides": [
{
"item": 2,
@@ -152,6 +166,8 @@ Endpoints
"presale_start": null,
"presale_end": null,
"location": null,
+ "geo_lat": null,
+ "geo_lon": null,
"seating_plan": null,
"seat_category_mapping": {},
"item_price_overrides": [
@@ -184,6 +200,8 @@ Endpoints
"presale_start": null,
"presale_end": null,
"location": null,
+ "geo_lat": null,
+ "geo_lon": null,
"seating_plan": null,
"seat_category_mapping": {},
"item_price_overrides": [
@@ -237,6 +255,8 @@ Endpoints
"presale_start": null,
"presale_end": null,
"location": null,
+ "geo_lat": null,
+ "geo_lon": null,
"seating_plan": null,
"seat_category_mapping": {},
"item_price_overrides": [
@@ -303,6 +323,8 @@ Endpoints
"presale_start": null,
"presale_end": null,
"location": null,
+ "geo_lat": null,
+ "geo_lon": null,
"seating_plan": null,
"seat_category_mapping": {},
"item_price_overrides": [
@@ -389,6 +411,8 @@ Endpoints
"presale_start": null,
"presale_end": null,
"location": null,
+ "geo_lat": null,
+ "geo_lon": null,
"seating_plan": null,
"seat_category_mapping": {},
"item_price_overrides": [
diff --git a/src/pretix/api/serializers/event.py b/src/pretix/api/serializers/event.py
index b98cf32119..edddf8e98e 100644
--- a/src/pretix/api/serializers/event.py
+++ b/src/pretix/api/serializers/event.py
@@ -70,7 +70,7 @@ class EventSerializer(I18nAwareModelSerializer):
model = Event
fields = ('name', 'slug', 'live', 'testmode', 'currency', 'date_from',
'date_to', 'date_admission', 'is_public', 'presale_start',
- 'presale_end', 'location', 'has_subevents', 'meta_data', 'seating_plan',
+ 'presale_end', 'location', 'geo_lat', 'geo_lon', 'has_subevents', 'meta_data', 'seating_plan',
'plugins', 'seat_category_mapping')
def validate(self, data):
@@ -277,8 +277,9 @@ class SubEventSerializer(I18nAwareModelSerializer):
class Meta:
model = SubEvent
fields = ('id', 'name', 'date_from', 'date_to', 'active', 'date_admission',
- 'presale_start', 'presale_end', 'location', 'event', 'is_public', 'seating_plan',
- 'item_price_overrides', 'variation_price_overrides', 'meta_data', 'seat_category_mapping')
+ 'presale_start', 'presale_end', 'location', 'geo_lat', 'geo_lon', 'event', 'is_public',
+ 'seating_plan', 'item_price_overrides', 'variation_price_overrides', 'meta_data',
+ 'seat_category_mapping')
def validate(self, data):
data = super().validate(data)
diff --git a/src/pretix/base/middleware.py b/src/pretix/base/middleware.py
index 96225d2bbe..b2706121ea 100644
--- a/src/pretix/base/middleware.py
+++ b/src/pretix/base/middleware.py
@@ -14,6 +14,7 @@ from django.utils.translation.trans_real import (
parse_accept_lang_header,
)
+from pretix.base.settings import GlobalSettingsObject
from pretix.multidomain.urlreverse import get_domain
_supported = None
@@ -187,6 +188,11 @@ class SecurityMiddleware(MiddlewareMixin):
# https://github.com/pretix/pretix/issues/765
resp['P3P'] = 'CP=\"ALL DSP COR CUR ADM TAI OUR IND COM NAV INT\"'
+ img_src = []
+ gs = GlobalSettingsObject()
+ if gs.settings.leaflet_tiles:
+ img_src.append(gs.settings.leaflet_tiles[:gs.settings.leaflet_tiles.index("/", 10)].replace("{s}", "*"))
+
h = {
'default-src': ["{static}"],
'script-src': ['{static}', 'https://checkout.stripe.com', 'https://js.stripe.com'],
@@ -196,7 +202,7 @@ class SecurityMiddleware(MiddlewareMixin):
'child-src': ['{static}', 'https://checkout.stripe.com', 'https://js.stripe.com'],
'style-src': ["{static}", "{media}"],
'connect-src': ["{dynamic}", "{media}", "https://checkout.stripe.com"],
- 'img-src': ["{static}", "{media}", "data:", "https://*.stripe.com"],
+ 'img-src': ["{static}", "{media}", "data:", "https://*.stripe.com"] + img_src,
'font-src': ["{static}"],
'media-src': ["{static}", "data:"],
# form-action is not only used to match on form actions, but also on URLs
diff --git a/src/pretix/base/migrations/0139_auto_20191019_1317.py b/src/pretix/base/migrations/0139_auto_20191019_1317.py
new file mode 100644
index 0000000000..1b4c8bf2a9
--- /dev/null
+++ b/src/pretix/base/migrations/0139_auto_20191019_1317.py
@@ -0,0 +1,33 @@
+# Generated by Django 2.2.1 on 2019-10-19 13:17
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('pretixbase', '0138_auto_20191017_1151'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='event',
+ name='geo_lat',
+ field=models.FloatField(null=True),
+ ),
+ migrations.AddField(
+ model_name='event',
+ name='geo_lon',
+ field=models.FloatField(null=True),
+ ),
+ migrations.AddField(
+ model_name='subevent',
+ name='geo_lat',
+ field=models.FloatField(null=True),
+ ),
+ migrations.AddField(
+ model_name='subevent',
+ name='geo_lon',
+ field=models.FloatField(null=True),
+ ),
+ ]
diff --git a/src/pretix/base/models/event.py b/src/pretix/base/models/event.py
index 55ccff0ecd..6eadc8e5f2 100644
--- a/src/pretix/base/models/event.py
+++ b/src/pretix/base/models/event.py
@@ -324,6 +324,14 @@ class Event(EventMixin, LoggedModel):
max_length=200,
verbose_name=_("Location"),
)
+ geo_lat = models.FloatField(
+ verbose_name=_("Latitude"),
+ null=True, blank=True,
+ )
+ geo_lon = models.FloatField(
+ verbose_name=_("Longitude"),
+ null=True, blank=True,
+ )
plugins = models.TextField(
null=False, blank=True,
verbose_name=_("Plugins"),
@@ -929,6 +937,14 @@ class SubEvent(EventMixin, LoggedModel):
max_length=200,
verbose_name=_("Location"),
)
+ geo_lat = models.FloatField(
+ verbose_name=_("Latitude"),
+ null=True, blank=True,
+ )
+ geo_lon = models.FloatField(
+ verbose_name=_("Longitude"),
+ null=True, blank=True
+ )
frontpage_text = I18nTextField(
null=True, blank=True,
verbose_name=_("Frontpage text")
diff --git a/src/pretix/base/settings.py b/src/pretix/base/settings.py
index 8e0069ff49..1d79c260b8 100644
--- a/src/pretix/base/settings.py
+++ b/src/pretix/base/settings.py
@@ -704,6 +704,18 @@ Your {event} team"""))
'default': '',
'type': LazyI18nString
},
+ 'opencagedata_apikey': {
+ 'default': None,
+ 'type': str
+ },
+ 'leaflet_tiles': {
+ 'default': None,
+ 'type': str
+ },
+ 'leaflet_tiles_attribution': {
+ 'default': None,
+ 'type': str
+ },
'frontpage_subevent_ordering': {
'default': 'date_ascending',
'type': str
diff --git a/src/pretix/control/forms/event.py b/src/pretix/control/forms/event.py
index 090476dd79..e383fbdd18 100644
--- a/src/pretix/control/forms/event.py
+++ b/src/pretix/control/forms/event.py
@@ -112,6 +112,8 @@ class EventWizardBasicsForm(I18nModelForm):
'presale_start',
'presale_end',
'location',
+ 'geo_lat',
+ 'geo_lon',
]
field_classes = {
'date_from': SplitDateTimeField,
@@ -282,6 +284,8 @@ class EventUpdateForm(I18nModelForm):
'presale_start',
'presale_end',
'location',
+ 'geo_lat',
+ 'geo_lon',
]
field_classes = {
'date_from': SplitDateTimeField,
diff --git a/src/pretix/control/forms/global_settings.py b/src/pretix/control/forms/global_settings.py
index 80c551f8bc..7699089713 100644
--- a/src/pretix/control/forms/global_settings.py
+++ b/src/pretix/control/forms/global_settings.py
@@ -37,6 +37,20 @@ class GlobalSettingsForm(SettingsForm):
required=False,
label=_("Global message banner detail text"),
)),
+ ('opencagedata_apikey', forms.CharField(
+ required=False,
+ label=_("OpenCage API key for geocoding"),
+ )),
+ ('leaflet_tiles', forms.CharField(
+ required=False,
+ label=_("Leaflet tiles URL pattern"),
+ help_text=_("e.g. {sample}").format(sample="https://a.tile.openstreetmap.org/{z}/{x}/{y}.png")
+ )),
+ ('leaflet_tiles_attribution', forms.CharField(
+ required=False,
+ label=_("Leaflet tiles attribution"),
+ help_text=_("e.g. {sample}").format(sample='© OpenStreetMap contributors')
+ )),
])
responses = register_global_settings.send(self)
for r, response in sorted(responses, key=lambda r: str(r[0])):
diff --git a/src/pretix/control/forms/subevents.py b/src/pretix/control/forms/subevents.py
index e50cc38d0a..5c35cecf1c 100644
--- a/src/pretix/control/forms/subevents.py
+++ b/src/pretix/control/forms/subevents.py
@@ -37,6 +37,8 @@ class SubEventForm(I18nModelForm):
'presale_end',
'location',
'frontpage_text',
+ 'geo_lat',
+ 'geo_lon',
]
field_classes = {
'date_from': SplitDateTimeField,
diff --git a/src/pretix/control/templates/pretixcontrol/base.html b/src/pretix/control/templates/pretixcontrol/base.html
index d14268218e..131ed4ecab 100644
--- a/src/pretix/control/templates/pretixcontrol/base.html
+++ b/src/pretix/control/templates/pretixcontrol/base.html
@@ -50,6 +50,8 @@
+
+
diff --git a/src/pretix/control/templates/pretixcontrol/event/settings.html b/src/pretix/control/templates/pretixcontrol/event/settings.html
index 683dd4c414..8b17f4db4c 100644
--- a/src/pretix/control/templates/pretixcontrol/event/settings.html
+++ b/src/pretix/control/templates/pretixcontrol/event/settings.html
@@ -1,6 +1,7 @@
{% extends "pretixcontrol/event/settings_base.html" %}
{% load i18n %}
{% load bootstrap3 %}
+{% load static %}
{% load hierarkey_form %}
{% block custom_header %}
{{ block.super }}
@@ -19,7 +20,30 @@
{% bootstrap_field form.slug layout="control" %}
{% bootstrap_field form.date_from layout="control" %}
{% bootstrap_field form.date_to layout="control" %}
- {% bootstrap_field form.location layout="control" %}
+
+ {% bootstrap_field form.location layout="control" %}
+
+
{% bootstrap_field form.date_admission layout="control" %}
{% bootstrap_field form.currency layout="control" %}
{% bootstrap_field sform.contact_mail layout="control" %}
diff --git a/src/pretix/control/templates/pretixcontrol/events/create_basics.html b/src/pretix/control/templates/pretixcontrol/events/create_basics.html
index 0ca973eb88..501c6664f4 100644
--- a/src/pretix/control/templates/pretixcontrol/events/create_basics.html
+++ b/src/pretix/control/templates/pretixcontrol/events/create_basics.html
@@ -1,6 +1,7 @@
{% extends "pretixcontrol/events/create_base.html" %}
{% load i18n %}
{% load bootstrap3 %}
+{% load static %}
{% block form %}
diff --git a/src/pretix/control/templates/pretixcontrol/subevents/bulk.html b/src/pretix/control/templates/pretixcontrol/subevents/bulk.html
index c7c2d91027..c741083b54 100644
--- a/src/pretix/control/templates/pretixcontrol/subevents/bulk.html
+++ b/src/pretix/control/templates/pretixcontrol/subevents/bulk.html
@@ -3,6 +3,7 @@
{% load bootstrap3 %}
{% load formset_tags %}
{% load captureas %}
+{% load static %}
{% load eventsignal %}
{% block title %}{% trans "Date" context "subevent" %}{% endblock %}
{% block content %}
@@ -266,7 +267,30 @@
{% bootstrap_field form.active layout="control" %}
{% bootstrap_field form.time_from layout="control" %}
{% bootstrap_field form.time_to layout="control" %}
- {% bootstrap_field form.location layout="control" %}
+
+ {% bootstrap_field form.location layout="control" %}
+
+
{% bootstrap_field form.time_admission layout="control" %}
{% bootstrap_field form.frontpage_text layout="control" %}
{% bootstrap_field form.is_public layout="control" %}
diff --git a/src/pretix/control/templates/pretixcontrol/subevents/detail.html b/src/pretix/control/templates/pretixcontrol/subevents/detail.html
index ad00a433d2..1b5b124795 100644
--- a/src/pretix/control/templates/pretixcontrol/subevents/detail.html
+++ b/src/pretix/control/templates/pretixcontrol/subevents/detail.html
@@ -3,6 +3,7 @@
{% load bootstrap3 %}
{% load formset_tags %}
{% load eventsignal %}
+{% load static %}
{% block title %}{% trans "Date" context "subevent" %}{% endblock %}
{% block content %}
{% if not subevent.pk %}
@@ -24,7 +25,30 @@
{% bootstrap_field form.active layout="control" %}
{% bootstrap_field form.date_from layout="control" %}
{% bootstrap_field form.date_to layout="control" %}
- {% bootstrap_field form.location layout="control" %}
+
+ {% bootstrap_field form.location layout="control" %}
+
+
{% bootstrap_field form.date_admission layout="control" %}
{% bootstrap_field form.frontpage_text layout="control" %}
{% bootstrap_field form.is_public layout="control" %}
diff --git a/src/pretix/control/urls.py b/src/pretix/control/urls.py
index 0f44387415..2c185bdf40 100644
--- a/src/pretix/control/urls.py
+++ b/src/pretix/control/urls.py
@@ -1,7 +1,7 @@
from django.conf.urls import include, url
from pretix.control.views import (
- auth, checkin, dashboards, event, global_settings, item, main, oauth,
+ auth, checkin, dashboards, event, geo, global_settings, item, main, oauth,
orders, organizer, pdf, search, shredder, subevents, typeahead, user,
users, vouchers, waitinglist,
)
@@ -22,6 +22,7 @@ urlpatterns = [
url(r'^logdetail/$', global_settings.LogDetailView.as_view(), name='global.logdetail'),
url(r'^logdetail/payment/$', global_settings.PaymentDetailView.as_view(), name='global.paymentdetail'),
url(r'^logdetail/refund/$', global_settings.RefundDetailView.as_view(), name='global.refunddetail'),
+ url(r'^geocode/$', geo.GeoCodeView.as_view(), name='global.geocode'),
url(r'^reauth/$', user.ReauthView.as_view(), name='user.reauth'),
url(r'^sudo/$', user.StartStaffSession.as_view(), name='user.sudo'),
url(r'^sudo/stop/$', user.StopStaffSession.as_view(), name='user.sudo.stop'),
diff --git a/src/pretix/control/views/geo.py b/src/pretix/control/views/geo.py
new file mode 100644
index 0000000000..419716af85
--- /dev/null
+++ b/src/pretix/control/views/geo.py
@@ -0,0 +1,59 @@
+import logging
+from urllib.parse import quote
+
+import requests
+from django.contrib.auth.mixins import LoginRequiredMixin
+from django.core.cache import cache
+from django.http import JsonResponse
+from django.views.generic.base import View
+
+from pretix.base.settings import GlobalSettingsObject
+
+logger = logging.getLogger(__name__)
+
+
+class GeoCodeView(LoginRequiredMixin, View):
+ def get(self, request, *args, **kwargs):
+ gs = GlobalSettingsObject()
+ if not gs.settings.opencagedata_apikey:
+ return JsonResponse({
+ 'success': False,
+ 'results': []
+ }, status=200)
+
+ q = request.GET.get('q')
+ cd = cache.get('geocode:{}'.format(q))
+ if cd:
+ return JsonResponse({
+ 'success': True,
+ 'results': cd
+ }, status=200)
+
+ try:
+ r = requests.get(
+ 'https://api.opencagedata.com/geocode/v1/json?q={}&key={}'.format(
+ quote(q), gs.settings.opencagedata_apikey
+ )
+ )
+ r.raise_for_status()
+ except IOError:
+ logger.exception("Geocoding failed")
+ return JsonResponse({
+ 'success': False,
+ 'results': []
+ }, status=200)
+ else:
+ d = r.json()
+ res = [
+ {
+ 'formatted': r['formatted'],
+ 'lat': r['geometry']['lat'],
+ 'lon': r['geometry']['lng'],
+ } for r in d['results']
+ ]
+ cache.set('geocode:{}'.format(q), res, timeout=3600 * 6)
+
+ return JsonResponse({
+ 'success': True,
+ 'results': res
+ }, status=200)
diff --git a/src/pretix/control/views/main.py b/src/pretix/control/views/main.py
index 9032665d5d..fbcb465c56 100644
--- a/src/pretix/control/views/main.py
+++ b/src/pretix/control/views/main.py
@@ -134,6 +134,8 @@ class EventWizard(SafeSessionWizardView):
initial['currency'] = self.clone_from.currency
initial['date_from'] = self.clone_from.date_from
initial['date_to'] = self.clone_from.date_to
+ initial['geo_lat'] = self.clone_from.geo_lat
+ initial['geo_lon'] = self.clone_from.geo_lon
initial['presale_start'] = self.clone_from.presale_start
initial['presale_end'] = self.clone_from.presale_end
initial['location'] = self.clone_from.location
@@ -245,6 +247,8 @@ class EventWizard(SafeSessionWizardView):
presale_start=event.presale_start,
presale_end=event.presale_end,
location=event.location,
+ geo_lat=event.geo_lat,
+ geo_lon=event.geo_lon,
active=True
)
diff --git a/src/pretix/static/leaflet/images/layers-2x.png b/src/pretix/static/leaflet/images/layers-2x.png
new file mode 100644
index 0000000000..200c333dca
Binary files /dev/null and b/src/pretix/static/leaflet/images/layers-2x.png differ
diff --git a/src/pretix/static/leaflet/images/layers.png b/src/pretix/static/leaflet/images/layers.png
new file mode 100644
index 0000000000..1a72e5784b
Binary files /dev/null and b/src/pretix/static/leaflet/images/layers.png differ
diff --git a/src/pretix/static/leaflet/images/marker-icon-2x.png b/src/pretix/static/leaflet/images/marker-icon-2x.png
new file mode 100644
index 0000000000..88f9e50188
Binary files /dev/null and b/src/pretix/static/leaflet/images/marker-icon-2x.png differ
diff --git a/src/pretix/static/leaflet/images/marker-icon.png b/src/pretix/static/leaflet/images/marker-icon.png
new file mode 100644
index 0000000000..7f9f0411fb
Binary files /dev/null and b/src/pretix/static/leaflet/images/marker-icon.png differ
diff --git a/src/pretix/static/leaflet/images/marker-shadow.png b/src/pretix/static/leaflet/images/marker-shadow.png
new file mode 100644
index 0000000000..9fd2979532
Binary files /dev/null and b/src/pretix/static/leaflet/images/marker-shadow.png differ
diff --git a/src/pretix/static/leaflet/leaflet.js b/src/pretix/static/leaflet/leaflet.js
new file mode 100644
index 0000000000..f8a2096ce1
--- /dev/null
+++ b/src/pretix/static/leaflet/leaflet.js
@@ -0,0 +1,5 @@
+/* @preserve
+ * Leaflet 1.5.1+Detached: 2e3e0ffbe87f246eb76d86d2633ddd59b262830b.2e3e0ff, a JS library for interactive maps. http://leafletjs.com
+ * (c) 2010-2018 Vladimir Agafonkin, (c) 2010-2011 CloudMade
+ */
+!function(t,i){"object"==typeof exports&&"undefined"!=typeof module?i(exports):"function"==typeof define&&define.amd?define(["exports"],i):i(t.L={})}(this,function(t){"use strict";var i=Object.freeze;function h(t){var i,e,n,o;for(e=1,n=arguments.length;e=this.min.x&&e.x<=this.max.x&&i.y>=this.min.y&&e.y<=this.max.y},intersects:function(t){t=R(t);var i=this.min,e=this.max,n=t.min,o=t.max,s=o.x>=i.x&&n.x<=e.x,r=o.y>=i.y&&n.y<=e.y;return s&&r},overlaps:function(t){t=R(t);var i=this.min,e=this.max,n=t.min,o=t.max,s=o.x>i.x&&n.xi.y&&n.y=n.lat&&e.lat<=o.lat&&i.lng>=n.lng&&e.lng<=o.lng},intersects:function(t){t=D(t);var i=this._southWest,e=this._northEast,n=t.getSouthWest(),o=t.getNorthEast(),s=o.lat>=i.lat&&n.lat<=e.lat,r=o.lng>=i.lng&&n.lng<=e.lng;return s&&r},overlaps:function(t){t=D(t);var i=this._southWest,e=this._northEast,n=t.getSouthWest(),o=t.getNorthEast(),s=o.lat>i.lat&&n.lati.lng&&n.lng';var i=t.firstChild;return i.style.behavior="url(#default#VML)",i&&"object"==typeof i.adj}catch(t){return!1}}();function kt(t){return 0<=navigator.userAgent.toLowerCase().indexOf(t)}var Bt=(Object.freeze||Object)({ie:it,ielt9:et,edge:nt,webkit:ot,android:st,android23:rt,androidStock:ht,opera:ut,chrome:lt,gecko:ct,safari:_t,phantom:dt,opera12:pt,win:mt,ie3d:ft,webkit3d:gt,gecko3d:vt,any3d:yt,mobile:xt,mobileWebkit:wt,mobileWebkit3d:Pt,msPointer:Lt,pointer:bt,touch:Tt,mobileOpera:zt,mobileGecko:Mt,retina:Ct,canvas:St,svg:Zt,vml:Et}),At=Lt?"MSPointerDown":"pointerdown",It=Lt?"MSPointerMove":"pointermove",Ot=Lt?"MSPointerUp":"pointerup",Rt=Lt?"MSPointerCancel":"pointercancel",Nt=["INPUT","SELECT","OPTION"],Dt={},jt=!1,Wt=0;function Ht(t,i,e,n){return"touchstart"===i?function(t,i,e){var n=a(function(t){if("mouse"!==t.pointerType&&t.MSPOINTER_TYPE_MOUSE&&t.pointerType!==t.MSPOINTER_TYPE_MOUSE){if(!(Nt.indexOf(t.target.tagName)<0))return;Di(t)}qt(t,i)});t["_leaflet_touchstart"+e]=n,t.addEventListener(At,n,!1),jt||(document.documentElement.addEventListener(At,Ft,!0),document.documentElement.addEventListener(It,Ut,!0),document.documentElement.addEventListener(Ot,Vt,!0),document.documentElement.addEventListener(Rt,Vt,!0),jt=!0)}(t,e,n):"touchmove"===i?function(t,i,e){var n=function(t){(t.pointerType!==t.MSPOINTER_TYPE_MOUSE&&"mouse"!==t.pointerType||0!==t.buttons)&&qt(t,i)};t["_leaflet_touchmove"+e]=n,t.addEventListener(It,n,!1)}(t,e,n):"touchend"===i&&function(t,i,e){var n=function(t){qt(t,i)};t["_leaflet_touchend"+e]=n,t.addEventListener(Ot,n,!1),t.addEventListener(Rt,n,!1)}(t,e,n),this}function Ft(t){Dt[t.pointerId]=t,Wt++}function Ut(t){Dt[t.pointerId]&&(Dt[t.pointerId]=t)}function Vt(t){delete Dt[t.pointerId],Wt--}function qt(t,i){for(var e in t.touches=[],Dt)t.touches.push(Dt[e]);t.changedTouches=[t],i(t)}var Gt=Lt?"MSPointerDown":bt?"pointerdown":"touchstart",Kt=Lt?"MSPointerUp":bt?"pointerup":"touchend",Yt="_leaflet_";function Xt(t,o,i){var s,r,a=!1;function e(t){var i;if(bt){if(!nt||"mouse"===t.pointerType)return;i=Wt}else i=t.touches.length;if(!(1this.options.maxZoom)?this.setZoom(t):this},panInsideBounds:function(t,i){this._enforcingBounds=!0;var e=this.getCenter(),n=this._limitCenter(e,this._zoom,D(t));return e.equals(n)||this.panTo(n,i),this._enforcingBounds=!1,this},panInside:function(t,i){var e=I((i=i||{}).paddingTopLeft||i.padding||[0,0]),n=I(i.paddingBottomRight||i.padding||[0,0]),o=this.getCenter(),s=this.project(o),r=this.project(t),a=this.getPixelBounds(),h=a.getSize().divideBy(2),u=R([a.min.add(e),a.max.subtract(n)]);if(!u.contains(r)){this._enforcingBounds=!0;var l=s.subtract(r),c=I(r.x+l.x,r.y+l.y);(r.xu.max.x)&&(c.x=s.x-l.x,0u.max.y)&&(c.y=s.y-l.y,0=this.options.transform3DLimit&&this._resetView(this.getCenter(),this.getZoom())},_findEventTargets:function(t,i){for(var e,n=[],o="mouseout"===i||"mouseover"===i,s=t.target||t.srcElement,r=!1;s;){if((e=this._targets[u(s)])&&("click"===i||"preclick"===i)&&!t._simulated&&this._draggableMoved(e)){r=!0;break}if(e&&e.listens(i,!0)){if(o&&!Ki(s,t))break;if(n.push(e),o)break}if(s===this._container)break;s=s.parentNode}return n.length||r||o||!Ki(s,t)||(n=[this]),n},_handleDOMEvent:function(t){if(this._loaded&&!Gi(t)){var i=t.type;"mousedown"!==i&&"keypress"!==i&&"keyup"!==i&&"keydown"!==i||zi(t.target||t.srcElement),this._fireDOMEvent(t,i)}},_mouseEvents:["click","dblclick","mouseover","mouseout","contextmenu"],_fireDOMEvent:function(t,i,e){if("click"===t.type){var n=h({},t);n.type="preclick",this._fireDOMEvent(n,n.type,e)}if(!t._stopped&&(e=(e||[]).concat(this._findEventTargets(t,i))).length){var o=e[0];"contextmenu"===i&&o.listens(i,!0)&&Di(t);var s={originalEvent:t};if("keypress"!==t.type&&"keydown"!==t.type&&"keyup"!==t.type){var r=o.getLatLng&&(!o._radius||o._radius<=10);s.containerPoint=r?this.latLngToContainerPoint(o.getLatLng()):this.mouseEventToContainerPoint(t),s.layerPoint=this.containerPointToLayerPoint(s.containerPoint),s.latlng=r?o.getLatLng():this.layerPointToLatLng(s.layerPoint)}for(var a=0;athis.options.zoomAnimationThreshold)return!1;var n=this.getZoomScale(i),o=this._getCenterOffset(t)._divideBy(1-1/n);return!(!0!==e.animate&&!this.getSize().contains(o))&&(M(function(){this._moveStart(!0,!1)._animateZoom(t,i,!0)},this),!0)},_animateZoom:function(t,i,e,n){this._mapPane&&(e&&(this._animatingZoom=!0,this._animateToCenter=t,this._animateToZoom=i,pi(this._mapPane,"leaflet-zoom-anim")),this.fire("zoomanim",{center:t,zoom:i,noUpdate:n}),setTimeout(a(this._onZoomTransitionEnd,this),250))},_onZoomTransitionEnd:function(){this._animatingZoom&&(this._mapPane&&mi(this._mapPane,"leaflet-zoom-anim"),this._animatingZoom=!1,this._move(this._animateToCenter,this._animateToZoom),M(function(){this._moveEnd(!0)},this))}});function $i(t){return new Qi(t)}var Qi=Z.extend({options:{position:"topright"},initialize:function(t){p(this,t)},getPosition:function(){return this.options.position},setPosition:function(t){var i=this._map;return i&&i.removeControl(this),this.options.position=t,i&&i.addControl(this),this},getContainer:function(){return this._container},addTo:function(t){this.remove(),this._map=t;var i=this._container=this.onAdd(t),e=this.getPosition(),n=t._controlCorners[e];return pi(i,"leaflet-control"),-1!==e.indexOf("bottom")?n.insertBefore(i,n.firstChild):n.appendChild(i),this._map.on("unload",this.remove,this),this},remove:function(){return this._map&&(ui(this._container),this.onRemove&&this.onRemove(this._map),this._map.off("unload",this.remove,this),this._map=null),this},_refocusOnMap:function(t){this._map&&t&&0",n=document.createElement("div");return n.innerHTML=e,n.firstChild},_addItem:function(t){var i,e=document.createElement("label"),n=this._map.hasLayer(t.layer);t.overlay?((i=document.createElement("input")).type="checkbox",i.className="leaflet-control-layers-selector",i.defaultChecked=n):i=this._createRadioElement("leaflet-base-layers_"+u(this),n),this._layerControlInputs.push(i),i.layerId=u(t.layer),Ei(i,"click",this._onInputClick,this);var o=document.createElement("span");o.innerHTML=" "+t.name;var s=document.createElement("div");return e.appendChild(s),s.appendChild(i),s.appendChild(o),(t.overlay?this._overlaysList:this._baseLayersList).appendChild(e),this._checkDisabledLayers(),e},_onInputClick:function(){var t,i,e=this._layerControlInputs,n=[],o=[];this._handlingClick=!0;for(var s=e.length-1;0<=s;s--)t=e[s],i=this._getLayer(t.layerId).layer,t.checked?n.push(i):t.checked||o.push(i);for(s=0;si.options.maxZoom},_expandIfNotCollapsed:function(){return this._map&&!this.options.collapsed&&this.expand(),this},_expand:function(){return this.expand()},_collapse:function(){return this.collapse()}}),ie=Qi.extend({options:{position:"topleft",zoomInText:"+",zoomInTitle:"Zoom in",zoomOutText:"−",zoomOutTitle:"Zoom out"},onAdd:function(t){var i="leaflet-control-zoom",e=hi("div",i+" leaflet-bar"),n=this.options;return this._zoomInButton=this._createButton(n.zoomInText,n.zoomInTitle,i+"-in",e,this._zoomIn),this._zoomOutButton=this._createButton(n.zoomOutText,n.zoomOutTitle,i+"-out",e,this._zoomOut),this._updateDisabled(),t.on("zoomend zoomlevelschange",this._updateDisabled,this),e},onRemove:function(t){t.off("zoomend zoomlevelschange",this._updateDisabled,this)},disable:function(){return this._disabled=!0,this._updateDisabled(),this},enable:function(){return this._disabled=!1,this._updateDisabled(),this},_zoomIn:function(t){!this._disabled&&this._map._zoomthis._map.getMinZoom()&&this._map.zoomOut(this._map.options.zoomDelta*(t.shiftKey?3:1))},_createButton:function(t,i,e,n,o){var s=hi("a",e,n);return s.innerHTML=t,s.href="#",s.title=i,s.setAttribute("role","button"),s.setAttribute("aria-label",i),Ni(s),Ei(s,"click",ji),Ei(s,"click",o,this),Ei(s,"click",this._refocusOnMap,this),s},_updateDisabled:function(){var t=this._map,i="leaflet-disabled";mi(this._zoomInButton,i),mi(this._zoomOutButton,i),!this._disabled&&t._zoom!==t.getMinZoom()||pi(this._zoomOutButton,i),!this._disabled&&t._zoom!==t.getMaxZoom()||pi(this._zoomInButton,i)}});Ji.mergeOptions({zoomControl:!0}),Ji.addInitHook(function(){this.options.zoomControl&&(this.zoomControl=new ie,this.addControl(this.zoomControl))});var ee=Qi.extend({options:{position:"bottomleft",maxWidth:100,metric:!0,imperial:!0},onAdd:function(t){var i="leaflet-control-scale",e=hi("div",i),n=this.options;return this._addScales(n,i+"-line",e),t.on(n.updateWhenIdle?"moveend":"move",this._update,this),t.whenReady(this._update,this),e},onRemove:function(t){t.off(this.options.updateWhenIdle?"moveend":"move",this._update,this)},_addScales:function(t,i,e){t.metric&&(this._mScale=hi("div",i,e)),t.imperial&&(this._iScale=hi("div",i,e))},_update:function(){var t=this._map,i=t.getSize().y/2,e=t.distance(t.containerPointToLatLng([0,i]),t.containerPointToLatLng([this.options.maxWidth,i]));this._updateScales(e)},_updateScales:function(t){this.options.metric&&t&&this._updateMetric(t),this.options.imperial&&t&&this._updateImperial(t)},_updateMetric:function(t){var i=this._getRoundNum(t),e=i<1e3?i+" m":i/1e3+" km";this._updateScale(this._mScale,e,i/t)},_updateImperial:function(t){var i,e,n,o=3.2808399*t;5280Leaflet'},initialize:function(t){p(this,t),this._attributions={}},onAdd:function(t){for(var i in(t.attributionControl=this)._container=hi("div","leaflet-control-attribution"),Ni(this._container),t._layers)t._layers[i].getAttribution&&this.addAttribution(t._layers[i].getAttribution());return this._update(),this._container},setPrefix:function(t){return this.options.prefix=t,this._update(),this},addAttribution:function(t){return t&&(this._attributions[t]||(this._attributions[t]=0),this._attributions[t]++,this._update()),this},removeAttribution:function(t){return t&&this._attributions[t]&&(this._attributions[t]--,this._update()),this},_update:function(){if(this._map){var t=[];for(var i in this._attributions)this._attributions[i]&&t.push(i);var e=[];this.options.prefix&&e.push(this.options.prefix),t.length&&e.push(t.join(", ")),this._container.innerHTML=e.join(" | ")}}});Ji.mergeOptions({attributionControl:!0}),Ji.addInitHook(function(){this.options.attributionControl&&(new ne).addTo(this)});Qi.Layers=te,Qi.Zoom=ie,Qi.Scale=ee,Qi.Attribution=ne,$i.layers=function(t,i,e){return new te(t,i,e)},$i.zoom=function(t){return new ie(t)},$i.scale=function(t){return new ee(t)},$i.attribution=function(t){return new ne(t)};var oe=Z.extend({initialize:function(t){this._map=t},enable:function(){return this._enabled||(this._enabled=!0,this.addHooks()),this},disable:function(){return this._enabled&&(this._enabled=!1,this.removeHooks()),this},enabled:function(){return!!this._enabled}});oe.addTo=function(t,i){return t.addHandler(i,this),this};var se,re={Events:E},ae=Tt?"touchstart mousedown":"mousedown",he={mousedown:"mouseup",touchstart:"touchend",pointerdown:"touchend",MSPointerDown:"touchend"},ue={mousedown:"mousemove",touchstart:"touchmove",pointerdown:"touchmove",MSPointerDown:"touchmove"},le=k.extend({options:{clickTolerance:3},initialize:function(t,i,e,n){p(this,n),this._element=t,this._dragStartTarget=i||t,this._preventOutline=e},enable:function(){this._enabled||(Ei(this._dragStartTarget,ae,this._onDown,this),this._enabled=!0)},disable:function(){this._enabled&&(le._dragging===this&&this.finishDrag(),Bi(this._dragStartTarget,ae,this._onDown,this),this._enabled=!1,this._moved=!1)},_onDown:function(t){if(!t._simulated&&this._enabled&&(this._moved=!1,!di(this._element,"leaflet-zoom-anim")&&!(le._dragging||t.shiftKey||1!==t.which&&1!==t.button&&!t.touches||((le._dragging=this)._preventOutline&&zi(this._element),bi(),$t(),this._moving)))){this.fire("down");var i=t.touches?t.touches[0]:t,e=Ci(this._element);this._startPoint=new B(i.clientX,i.clientY),this._parentScale=Si(e),Ei(document,ue[t.type],this._onMove,this),Ei(document,he[t.type],this._onUp,this)}},_onMove:function(t){if(!t._simulated&&this._enabled)if(t.touches&&1i.max.x&&(e|=2),t.yi.max.y&&(e|=8),e}function fe(t,i,e,n){var o,s=i.x,r=i.y,a=e.x-s,h=e.y-r,u=a*a+h*h;return 0this._layersMaxZoom&&this.setZoom(this._layersMaxZoom),void 0===this.options.minZoom&&this._layersMinZoom&&this.getZoom()t.y!=n.y>t.y&&t.x<(n.x-e.x)*(t.y-e.y)/(n.y-e.y)+e.x&&(u=!u);return u||De.prototype._containsPoint.call(this,t,!0)}});var We=Ee.extend({initialize:function(t,i){p(this,i),this._layers={},t&&this.addData(t)},addData:function(t){var i,e,n,o=v(t)?t:t.features;if(o){for(i=0,e=o.length;iu.x&&(l=s.x+n-u.x+h.x),s.x-l-a.x<0&&(l=s.x-a.x),s.y+e+h.y>u.y&&(c=s.y+e-u.y+h.y),s.y-c-a.y<0&&(c=s.y-a.y),(l||c)&&t.fire("autopanstart").panBy([l,c])}},_onCloseButtonClick:function(t){this._close(),ji(t)},_getAnchor:function(){return I(this._source&&this._source._getPopupAnchor?this._source._getPopupAnchor():[0,0])}});Ji.mergeOptions({closePopupOnClick:!0}),Ji.include({openPopup:function(t,i,e){return t instanceof nn||(t=new nn(e).setContent(t)),i&&t.setLatLng(i),this.hasLayer(t)?this:(this._popup&&this._popup.options.autoClose&&this.closePopup(),this._popup=t,this.addLayer(t))},closePopup:function(t){return t&&t!==this._popup||(t=this._popup,this._popup=null),t&&this.removeLayer(t),this}}),Se.include({bindPopup:function(t,i){return t instanceof nn?(p(t,i),(this._popup=t)._source=this):(this._popup&&!i||(this._popup=new nn(i,this)),this._popup.setContent(t)),this._popupHandlersAdded||(this.on({click:this._openPopup,keypress:this._onKeyPress,remove:this.closePopup,move:this._movePopup}),this._popupHandlersAdded=!0),this},unbindPopup:function(){return this._popup&&(this.off({click:this._openPopup,keypress:this._onKeyPress,remove:this.closePopup,move:this._movePopup}),this._popupHandlersAdded=!1,this._popup=null),this},openPopup:function(t,i){return this._popup&&this._map&&(i=this._popup._prepareOpen(this,t,i),this._map.openPopup(this._popup,i)),this},closePopup:function(){return this._popup&&this._popup._close(),this},togglePopup:function(t){return this._popup&&(this._popup._map?this.closePopup():this.openPopup(t)),this},isPopupOpen:function(){return!!this._popup&&this._popup.isOpen()},setPopupContent:function(t){return this._popup&&this._popup.setContent(t),this},getPopup:function(){return this._popup},_openPopup:function(t){var i=t.layer||t.target;this._popup&&this._map&&(ji(t),i instanceof Oe?this.openPopup(t.layer||t.target,t.latlng):this._map.hasLayer(this._popup)&&this._popup._source===i?this.closePopup():this.openPopup(i,t.latlng))},_movePopup:function(t){this._popup.setLatLng(t.latlng)},_onKeyPress:function(t){13===t.originalEvent.keyCode&&this._openPopup(t)}});var on=en.extend({options:{pane:"tooltipPane",offset:[0,0],direction:"auto",permanent:!1,sticky:!1,interactive:!1,opacity:.9},onAdd:function(t){en.prototype.onAdd.call(this,t),this.setOpacity(this.options.opacity),t.fire("tooltipopen",{tooltip:this}),this._source&&this._source.fire("tooltipopen",{tooltip:this},!0)},onRemove:function(t){en.prototype.onRemove.call(this,t),t.fire("tooltipclose",{tooltip:this}),this._source&&this._source.fire("tooltipclose",{tooltip:this},!0)},getEvents:function(){var t=en.prototype.getEvents.call(this);return Tt&&!this.options.permanent&&(t.preclick=this._close),t},_close:function(){this._map&&this._map.closeTooltip(this)},_initLayout:function(){var t="leaflet-tooltip "+(this.options.className||"")+" leaflet-zoom-"+(this._zoomAnimated?"animated":"hide");this._contentNode=this._container=hi("div",t)},_updateLayout:function(){},_adjustPan:function(){},_setPosition:function(t){var i=this._map,e=this._container,n=i.latLngToContainerPoint(i.getCenter()),o=i.layerPointToContainerPoint(t),s=this.options.direction,r=e.offsetWidth,a=e.offsetHeight,h=I(this.options.offset),u=this._getAnchor();t="top"===s?t.add(I(-r/2+h.x,-a+h.y+u.y,!0)):"bottom"===s?t.subtract(I(r/2-h.x,-h.y,!0)):"center"===s?t.subtract(I(r/2+h.x,a/2-u.y+h.y,!0)):"right"===s||"auto"===s&&o.xthis.options.maxZoom||ethis.options.maxZoom||void 0!==this.options.minZoom&&oe.max.x)||!i.wrapLat&&(t.ye.max.y))return!1}if(!this.options.bounds)return!0;var n=this._tileCoordsToBounds(t);return D(this.options.bounds).overlaps(n)},_keyToBounds:function(t){return this._tileCoordsToBounds(this._keyToTileCoords(t))},_tileCoordsToNwSe:function(t){var i=this._map,e=this.getTileSize(),n=t.scaleBy(e),o=n.add(e);return[i.unproject(n,t.z),i.unproject(o,t.z)]},_tileCoordsToBounds:function(t){var i=this._tileCoordsToNwSe(t),e=new N(i[0],i[1]);return this.options.noWrap||(e=this._map.wrapLatLngBounds(e)),e},_tileCoordsToKey:function(t){return t.x+":"+t.y+":"+t.z},_keyToTileCoords:function(t){var i=t.split(":"),e=new B(+i[0],+i[1]);return e.z=+i[2],e},_removeTile:function(t){var i=this._tiles[t];i&&(ui(i.el),delete this._tiles[t],this.fire("tileunload",{tile:i.el,coords:this._keyToTileCoords(t)}))},_initTile:function(t){pi(t,"leaflet-tile");var i=this.getTileSize();t.style.width=i.x+"px",t.style.height=i.y+"px",t.onselectstart=l,t.onmousemove=l,et&&this.options.opacity<1&&vi(t,this.options.opacity),st&&!rt&&(t.style.WebkitBackfaceVisibility="hidden")},_addTile:function(t,i){var e=this._getTilePos(t),n=this._tileCoordsToKey(t),o=this.createTile(this._wrapCoords(t),a(this._tileReady,this,t));this._initTile(o),this.createTile.length<2&&M(a(this._tileReady,this,t,null,o)),wi(o,e),this._tiles[n]={el:o,coords:t,current:!0},i.appendChild(o),this.fire("tileloadstart",{tile:o,coords:t})},_tileReady:function(t,i,e){i&&this.fire("tileerror",{error:i,tile:e,coords:t});var n=this._tileCoordsToKey(t);(e=this._tiles[n])&&(e.loaded=+new Date,this._map._fadeAnimated?(vi(e.el,0),C(this._fadeFrame),this._fadeFrame=M(this._updateOpacity,this)):(e.active=!0,this._pruneTiles()),i||(pi(e.el,"leaflet-tile-loaded"),this.fire("tileload",{tile:e.el,coords:t})),this._noTilesToLoad()&&(this._loading=!1,this.fire("load"),et||!this._map._fadeAnimated?M(this._pruneTiles,this):setTimeout(a(this._pruneTiles,this),250)))},_getTilePos:function(t){return t.scaleBy(this.getTileSize()).subtract(this._level.origin)},_wrapCoords:function(t){var i=new B(this._wrapX?r(t.x,this._wrapX):t.x,this._wrapY?r(t.y,this._wrapY):t.y);return i.z=t.z,i},_pxBoundsToTileRange:function(t){var i=this.getTileSize();return new O(t.min.unscaleBy(i).floor(),t.max.unscaleBy(i).ceil().subtract([1,1]))},_noTilesToLoad:function(){for(var t in this._tiles)if(!this._tiles[t].loaded)return!1;return!0}});var an=rn.extend({options:{minZoom:0,maxZoom:18,subdomains:"abc",errorTileUrl:"",zoomOffset:0,tms:!1,zoomReverse:!1,detectRetina:!1,crossOrigin:!1},initialize:function(t,i){this._url=t,(i=p(this,i)).detectRetina&&Ct&&0')}}catch(t){return function(t){return document.createElement("<"+t+' xmlns="urn:schemas-microsoft.com:vml" class="lvml">')}}}(),pn={_initContainer:function(){this._container=hi("div","leaflet-vml-container")},_update:function(){this._map._animatingZoom||(ln.prototype._update.call(this),this.fire("update"))},_initPath:function(t){var i=t._container=dn("shape");pi(i,"leaflet-vml-shape "+(this.options.className||"")),i.coordsize="1 1",t._path=dn("path"),i.appendChild(t._path),this._updateStyle(t),this._layers[u(t)]=t},_addPath:function(t){var i=t._container;this._container.appendChild(i),t.options.interactive&&t.addInteractiveTarget(i)},_removePath:function(t){var i=t._container;ui(i),t.removeInteractiveTarget(i),delete this._layers[u(t)]},_updateStyle:function(t){var i=t._stroke,e=t._fill,n=t.options,o=t._container;o.stroked=!!n.stroke,o.filled=!!n.fill,n.stroke?(i||(i=t._stroke=dn("stroke")),o.appendChild(i),i.weight=n.weight+"px",i.color=n.color,i.opacity=n.opacity,n.dashArray?i.dashStyle=v(n.dashArray)?n.dashArray.join(" "):n.dashArray.replace(/( *, *)/g," "):i.dashStyle="",i.endcap=n.lineCap.replace("butt","flat"),i.joinstyle=n.lineJoin):i&&(o.removeChild(i),t._stroke=null),n.fill?(e||(e=t._fill=dn("fill")),o.appendChild(e),e.color=n.fillColor||n.color,e.opacity=n.fillOpacity):e&&(o.removeChild(e),t._fill=null)},_updateCircle:function(t){var i=t._point.round(),e=Math.round(t._radius),n=Math.round(t._radiusY||e);this._setPath(t,t._empty()?"M0 0":"AL "+i.x+","+i.y+" "+e+","+n+" 0,23592600")},_setPath:function(t,i){t._path.v=i},_bringToFront:function(t){ci(t._container)},_bringToBack:function(t){_i(t._container)}},mn=Et?dn:$,fn=ln.extend({getEvents:function(){var t=ln.prototype.getEvents.call(this);return t.zoomstart=this._onZoomStart,t},_initContainer:function(){this._container=mn("svg"),this._container.setAttribute("pointer-events","none"),this._rootGroup=mn("g"),this._container.appendChild(this._rootGroup)},_destroyContainer:function(){ui(this._container),Bi(this._container),delete this._container,delete this._rootGroup,delete this._svgSize},_onZoomStart:function(){this._update()},_update:function(){if(!this._map._animatingZoom||!this._bounds){ln.prototype._update.call(this);var t=this._bounds,i=t.getSize(),e=this._container;this._svgSize&&this._svgSize.equals(i)||(this._svgSize=i,e.setAttribute("width",i.x),e.setAttribute("height",i.y)),wi(e,t.min),e.setAttribute("viewBox",[t.min.x,t.min.y,i.x,i.y].join(" ")),this.fire("update")}},_initPath:function(t){var i=t._path=mn("path");t.options.className&&pi(i,t.options.className),t.options.interactive&&pi(i,"leaflet-interactive"),this._updateStyle(t),this._layers[u(t)]=t},_addPath:function(t){this._rootGroup||this._initContainer(),this._rootGroup.appendChild(t._path),t.addInteractiveTarget(t._path)},_removePath:function(t){ui(t._path),t.removeInteractiveTarget(t._path),delete this._layers[u(t)]},_updatePath:function(t){t._project(),t._update()},_updateStyle:function(t){var i=t._path,e=t.options;i&&(e.stroke?(i.setAttribute("stroke",e.color),i.setAttribute("stroke-opacity",e.opacity),i.setAttribute("stroke-width",e.weight),i.setAttribute("stroke-linecap",e.lineCap),i.setAttribute("stroke-linejoin",e.lineJoin),e.dashArray?i.setAttribute("stroke-dasharray",e.dashArray):i.removeAttribute("stroke-dasharray"),e.dashOffset?i.setAttribute("stroke-dashoffset",e.dashOffset):i.removeAttribute("stroke-dashoffset")):i.setAttribute("stroke","none"),e.fill?(i.setAttribute("fill",e.fillColor||e.color),i.setAttribute("fill-opacity",e.fillOpacity),i.setAttribute("fill-rule",e.fillRule||"evenodd")):i.setAttribute("fill","none"))},_updatePoly:function(t,i){this._setPath(t,Q(t._parts,i))},_updateCircle:function(t){var i=t._point,e=Math.max(Math.round(t._radius),1),n="a"+e+","+(Math.max(Math.round(t._radiusY),1)||e)+" 0 1,0 ",o=t._empty()?"M0 0":"M"+(i.x-e)+","+i.y+n+2*e+",0 "+n+2*-e+",0 ";this._setPath(t,o)},_setPath:function(t,i){t._path.setAttribute("d",i)},_bringToFront:function(t){ci(t._path)},_bringToBack:function(t){_i(t._path)}});function gn(t){return Zt||Et?new fn(t):null}Et&&fn.include(pn),Ji.include({getRenderer:function(t){var i=t.options.renderer||this._getPaneRenderer(t.options.pane)||this.options.renderer||this._renderer;return i||(i=this._renderer=this._createRenderer()),this.hasLayer(i)||this.addLayer(i),i},_getPaneRenderer:function(t){if("overlayPane"===t||void 0===t)return!1;var i=this._paneRenderers[t];return void 0===i&&(i=this._createRenderer({pane:t}),this._paneRenderers[t]=i),i},_createRenderer:function(t){return this.options.preferCanvas&&_n(t)||gn(t)}});var vn=je.extend({initialize:function(t,i){je.prototype.initialize.call(this,this._boundsToLatLngs(t),i)},setBounds:function(t){return this.setLatLngs(this._boundsToLatLngs(t))},_boundsToLatLngs:function(t){return[(t=D(t)).getSouthWest(),t.getNorthWest(),t.getNorthEast(),t.getSouthEast()]}});fn.create=mn,fn.pointsToPath=Q,We.geometryToLayer=He,We.coordsToLatLng=Fe,We.coordsToLatLngs=Ue,We.latLngToCoords=Ve,We.latLngsToCoords=qe,We.getFeature=Ge,We.asFeature=Ke,Ji.mergeOptions({boxZoom:!0});var yn=oe.extend({initialize:function(t){this._map=t,this._container=t._container,this._pane=t._panes.overlayPane,this._resetStateTimeout=0,t.on("unload",this._destroy,this)},addHooks:function(){Ei(this._container,"mousedown",this._onMouseDown,this)},removeHooks:function(){Bi(this._container,"mousedown",this._onMouseDown,this)},moved:function(){return this._moved},_destroy:function(){ui(this._pane),delete this._pane},_resetState:function(){this._resetStateTimeout=0,this._moved=!1},_clearDeferredResetState:function(){0!==this._resetStateTimeout&&(clearTimeout(this._resetStateTimeout),this._resetStateTimeout=0)},_onMouseDown:function(t){if(!t.shiftKey||1!==t.which&&1!==t.button)return!1;this._clearDeferredResetState(),this._resetState(),$t(),bi(),this._startPoint=this._map.mouseEventToContainerPoint(t),Ei(document,{contextmenu:ji,mousemove:this._onMouseMove,mouseup:this._onMouseUp,keydown:this._onKeyDown},this)},_onMouseMove:function(t){this._moved||(this._moved=!0,this._box=hi("div","leaflet-zoom-box",this._container),pi(this._container,"leaflet-crosshair"),this._map.fire("boxzoomstart")),this._point=this._map.mouseEventToContainerPoint(t);var i=new O(this._point,this._startPoint),e=i.getSize();wi(this._box,i.min),this._box.style.width=e.x+"px",this._box.style.height=e.y+"px"},_finish:function(){this._moved&&(ui(this._box),mi(this._container,"leaflet-crosshair")),Qt(),Ti(),Bi(document,{contextmenu:ji,mousemove:this._onMouseMove,mouseup:this._onMouseUp,keydown:this._onKeyDown},this)},_onMouseUp:function(t){if((1===t.which||1===t.button)&&(this._finish(),this._moved)){this._clearDeferredResetState(),this._resetStateTimeout=setTimeout(a(this._resetState,this),0);var i=new N(this._map.containerPointToLatLng(this._startPoint),this._map.containerPointToLatLng(this._point));this._map.fitBounds(i).fire("boxzoomend",{boxZoomBounds:i})}},_onKeyDown:function(t){27===t.keyCode&&this._finish()}});Ji.addInitHook("addHandler","boxZoom",yn),Ji.mergeOptions({doubleClickZoom:!0});var xn=oe.extend({addHooks:function(){this._map.on("dblclick",this._onDoubleClick,this)},removeHooks:function(){this._map.off("dblclick",this._onDoubleClick,this)},_onDoubleClick:function(t){var i=this._map,e=i.getZoom(),n=i.options.zoomDelta,o=t.originalEvent.shiftKey?e-n:e+n;"center"===i.options.doubleClickZoom?i.setZoom(o):i.setZoomAround(t.containerPoint,o)}});Ji.addInitHook("addHandler","doubleClickZoom",xn),Ji.mergeOptions({dragging:!0,inertia:!rt,inertiaDeceleration:3400,inertiaMaxSpeed:1/0,easeLinearity:.2,worldCopyJump:!1,maxBoundsViscosity:0});var wn=oe.extend({addHooks:function(){if(!this._draggable){var t=this._map;this._draggable=new le(t._mapPane,t._container),this._draggable.on({dragstart:this._onDragStart,drag:this._onDrag,dragend:this._onDragEnd},this),this._draggable.on("predrag",this._onPreDragLimit,this),t.options.worldCopyJump&&(this._draggable.on("predrag",this._onPreDragWrap,this),t.on("zoomend",this._onZoomEnd,this),t.whenReady(this._onZoomEnd,this))}pi(this._map._container,"leaflet-grab leaflet-touch-drag"),this._draggable.enable(),this._positions=[],this._times=[]},removeHooks:function(){mi(this._map._container,"leaflet-grab"),mi(this._map._container,"leaflet-touch-drag"),this._draggable.disable()},moved:function(){return this._draggable&&this._draggable._moved},moving:function(){return this._draggable&&this._draggable._moving},_onDragStart:function(){var t=this._map;if(t._stop(),this._map.options.maxBounds&&this._map.options.maxBoundsViscosity){var i=D(this._map.options.maxBounds);this._offsetLimit=R(this._map.latLngToContainerPoint(i.getNorthWest()).multiplyBy(-1),this._map.latLngToContainerPoint(i.getSouthEast()).multiplyBy(-1).add(this._map.getSize())),this._viscosity=Math.min(1,Math.max(0,this._map.options.maxBoundsViscosity))}else this._offsetLimit=null;t.fire("movestart").fire("dragstart"),t.options.inertia&&(this._positions=[],this._times=[])},_onDrag:function(t){if(this._map.options.inertia){var i=this._lastTime=+new Date,e=this._lastPos=this._draggable._absPos||this._draggable._newPos;this._positions.push(e),this._times.push(i),this._prunePositions(i)}this._map.fire("move",t).fire("drag",t)},_prunePositions:function(t){for(;1i.max.x&&(t.x=this._viscousLimit(t.x,i.max.x)),t.y>i.max.y&&(t.y=this._viscousLimit(t.y,i.max.y)),this._draggable._newPos=this._draggable._startPos.add(t)}},_onPreDragWrap:function(){var t=this._worldWidth,i=Math.round(t/2),e=this._initialWorldOffset,n=this._draggable._newPos.x,o=(n-i+e)%t+i-e,s=(n+i+e)%t-i-e,r=Math.abs(o+e)i.getMaxZoom()&&1 svg,
+.leaflet-pane > canvas,
+.leaflet-zoom-box,
+.leaflet-image-layer,
+.leaflet-layer {
+ position: absolute;
+ left: 0;
+ top: 0;
+ }
+.leaflet-container {
+ overflow: hidden;
+ }
+.leaflet-tile,
+.leaflet-marker-icon,
+.leaflet-marker-shadow {
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ user-select: none;
+ -webkit-user-drag: none;
+ }
+/* Prevents IE11 from highlighting tiles in blue */
+.leaflet-tile::selection {
+ background: transparent;
+}
+/* Safari renders non-retina tile on retina better with this, but Chrome is worse */
+.leaflet-safari .leaflet-tile {
+ image-rendering: -webkit-optimize-contrast;
+ }
+/* hack that prevents hw layers "stretching" when loading new tiles */
+.leaflet-safari .leaflet-tile-container {
+ width: 1600px;
+ height: 1600px;
+ -webkit-transform-origin: 0 0;
+ }
+.leaflet-marker-icon,
+.leaflet-marker-shadow {
+ display: block;
+ }
+/* .leaflet-container svg: reset svg max-width decleration shipped in Joomla! (joomla.org) 3.x */
+/* .leaflet-container img: map is broken in FF if you have max-width: 100% on tiles */
+.leaflet-container .leaflet-overlay-pane svg,
+.leaflet-container .leaflet-marker-pane img,
+.leaflet-container .leaflet-shadow-pane img,
+.leaflet-container .leaflet-tile-pane img,
+.leaflet-container img.leaflet-image-layer,
+.leaflet-container .leaflet-tile {
+ max-width: none !important;
+ max-height: none !important;
+ }
+
+.leaflet-container.leaflet-touch-zoom {
+ -ms-touch-action: pan-x pan-y;
+ touch-action: pan-x pan-y;
+ }
+.leaflet-container.leaflet-touch-drag {
+ -ms-touch-action: pinch-zoom;
+ /* Fallback for FF which doesn't support pinch-zoom */
+ touch-action: none;
+ touch-action: pinch-zoom;
+}
+.leaflet-container.leaflet-touch-drag.leaflet-touch-zoom {
+ -ms-touch-action: none;
+ touch-action: none;
+}
+.leaflet-container {
+ -webkit-tap-highlight-color: transparent;
+}
+.leaflet-container a {
+ -webkit-tap-highlight-color: rgba(51, 181, 229, 0.4);
+}
+.leaflet-tile {
+ filter: inherit;
+ visibility: hidden;
+ }
+.leaflet-tile-loaded {
+ visibility: inherit;
+ }
+.leaflet-zoom-box {
+ width: 0;
+ height: 0;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ z-index: 800;
+ }
+/* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */
+.leaflet-overlay-pane svg {
+ -moz-user-select: none;
+ }
+
+.leaflet-pane { z-index: 400; }
+
+.leaflet-tile-pane { z-index: 200; }
+.leaflet-overlay-pane { z-index: 400; }
+.leaflet-shadow-pane { z-index: 500; }
+.leaflet-marker-pane { z-index: 600; }
+.leaflet-tooltip-pane { z-index: 650; }
+.leaflet-popup-pane { z-index: 700; }
+
+.leaflet-map-pane canvas { z-index: 100; }
+.leaflet-map-pane svg { z-index: 200; }
+
+.leaflet-vml-shape {
+ width: 1px;
+ height: 1px;
+ }
+.lvml {
+ behavior: url(#default#VML);
+ display: inline-block;
+ position: absolute;
+ }
+
+
+/* control positioning */
+
+.leaflet-control {
+ position: relative;
+ z-index: 800;
+ pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
+ pointer-events: auto;
+ }
+.leaflet-top,
+.leaflet-bottom {
+ position: absolute;
+ z-index: 1000;
+ pointer-events: none;
+ }
+.leaflet-top {
+ top: 0;
+ }
+.leaflet-right {
+ right: 0;
+ }
+.leaflet-bottom {
+ bottom: 0;
+ }
+.leaflet-left {
+ left: 0;
+ }
+.leaflet-control {
+ float: left;
+ clear: both;
+ }
+.leaflet-right .leaflet-control {
+ float: right;
+ }
+.leaflet-top .leaflet-control {
+ margin-top: 10px;
+ }
+.leaflet-bottom .leaflet-control {
+ margin-bottom: 10px;
+ }
+.leaflet-left .leaflet-control {
+ margin-left: 10px;
+ }
+.leaflet-right .leaflet-control {
+ margin-right: 10px;
+ }
+
+
+/* zoom and fade animations */
+
+.leaflet-fade-anim .leaflet-tile {
+ will-change: opacity;
+ }
+.leaflet-fade-anim .leaflet-popup {
+ opacity: 0;
+ -webkit-transition: opacity 0.2s linear;
+ -moz-transition: opacity 0.2s linear;
+ transition: opacity 0.2s linear;
+ }
+.leaflet-fade-anim .leaflet-map-pane .leaflet-popup {
+ opacity: 1;
+ }
+.leaflet-zoom-animated {
+ -webkit-transform-origin: 0 0;
+ -ms-transform-origin: 0 0;
+ transform-origin: 0 0;
+ }
+.leaflet-zoom-anim .leaflet-zoom-animated {
+ will-change: transform;
+ }
+.leaflet-zoom-anim .leaflet-zoom-animated {
+ -webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1);
+ -moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1);
+ transition: transform 0.25s cubic-bezier(0,0,0.25,1);
+ }
+.leaflet-zoom-anim .leaflet-tile,
+.leaflet-pan-anim .leaflet-tile {
+ -webkit-transition: none;
+ -moz-transition: none;
+ transition: none;
+ }
+
+.leaflet-zoom-anim .leaflet-zoom-hide {
+ visibility: hidden;
+ }
+
+
+/* cursors */
+
+.leaflet-interactive {
+ cursor: pointer;
+ }
+.leaflet-grab {
+ cursor: -webkit-grab;
+ cursor: -moz-grab;
+ cursor: grab;
+ }
+.leaflet-crosshair,
+.leaflet-crosshair .leaflet-interactive {
+ cursor: crosshair;
+ }
+.leaflet-popup-pane,
+.leaflet-control {
+ cursor: auto;
+ }
+.leaflet-dragging .leaflet-grab,
+.leaflet-dragging .leaflet-grab .leaflet-interactive,
+.leaflet-dragging .leaflet-marker-draggable {
+ cursor: move;
+ cursor: -webkit-grabbing;
+ cursor: -moz-grabbing;
+ cursor: grabbing;
+ }
+
+/* marker & overlays interactivity */
+.leaflet-marker-icon,
+.leaflet-marker-shadow,
+.leaflet-image-layer,
+.leaflet-pane > svg path,
+.leaflet-tile-container {
+ pointer-events: none;
+ }
+
+.leaflet-marker-icon.leaflet-interactive,
+.leaflet-image-layer.leaflet-interactive,
+.leaflet-pane > svg path.leaflet-interactive,
+svg.leaflet-image-layer.leaflet-interactive path {
+ pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
+ pointer-events: auto;
+ }
+
+/* visual tweaks */
+
+.leaflet-container {
+ background: #ddd;
+ outline: 0;
+ }
+.leaflet-container a {
+ color: #0078A8;
+ }
+.leaflet-container a.leaflet-active {
+ outline: 2px solid orange;
+ }
+.leaflet-zoom-box {
+ border: 2px dotted #38f;
+ background: rgba(255,255,255,0.5);
+ }
+
+
+/* general typography */
+.leaflet-container {
+ font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif;
+ }
+
+
+/* general toolbar styles */
+
+.leaflet-bar {
+ box-shadow: 0 1px 5px rgba(0,0,0,0.65);
+ border-radius: 4px;
+ }
+.leaflet-bar a,
+.leaflet-bar a:hover {
+ background-color: #fff;
+ border-bottom: 1px solid #ccc;
+ width: 26px;
+ height: 26px;
+ line-height: 26px;
+ display: block;
+ text-align: center;
+ text-decoration: none;
+ color: black;
+ }
+.leaflet-bar a,
+.leaflet-control-layers-toggle {
+ background-position: 50% 50%;
+ background-repeat: no-repeat;
+ display: block;
+ }
+.leaflet-bar a:hover {
+ background-color: #f4f4f4;
+ }
+.leaflet-bar a:first-child {
+ border-top-left-radius: 4px;
+ border-top-right-radius: 4px;
+ }
+.leaflet-bar a:last-child {
+ border-bottom-left-radius: 4px;
+ border-bottom-right-radius: 4px;
+ border-bottom: none;
+ }
+.leaflet-bar a.leaflet-disabled {
+ cursor: default;
+ background-color: #f4f4f4;
+ color: #bbb;
+ }
+
+.leaflet-touch .leaflet-bar a {
+ width: 30px;
+ height: 30px;
+ line-height: 30px;
+ }
+.leaflet-touch .leaflet-bar a:first-child {
+ border-top-left-radius: 2px;
+ border-top-right-radius: 2px;
+ }
+.leaflet-touch .leaflet-bar a:last-child {
+ border-bottom-left-radius: 2px;
+ border-bottom-right-radius: 2px;
+ }
+
+/* zoom control */
+
+.leaflet-control-zoom-in,
+.leaflet-control-zoom-out {
+ font: bold 18px 'Lucida Console', Monaco, monospace;
+ text-indent: 1px;
+ }
+
+.leaflet-touch .leaflet-control-zoom-in, .leaflet-touch .leaflet-control-zoom-out {
+ font-size: 22px;
+ }
+
+
+/* layers control */
+
+.leaflet-control-layers {
+ box-shadow: 0 1px 5px rgba(0,0,0,0.4);
+ background: #fff;
+ border-radius: 5px;
+ }
+.leaflet-control-layers-toggle {
+ background-image: url(images/layers.png);
+ width: 36px;
+ height: 36px;
+ }
+.leaflet-retina .leaflet-control-layers-toggle {
+ background-image: url(images/layers-2x.png);
+ background-size: 26px 26px;
+ }
+.leaflet-touch .leaflet-control-layers-toggle {
+ width: 44px;
+ height: 44px;
+ }
+.leaflet-control-layers .leaflet-control-layers-list,
+.leaflet-control-layers-expanded .leaflet-control-layers-toggle {
+ display: none;
+ }
+.leaflet-control-layers-expanded .leaflet-control-layers-list {
+ display: block;
+ position: relative;
+ }
+.leaflet-control-layers-expanded {
+ padding: 6px 10px 6px 6px;
+ color: #333;
+ background: #fff;
+ }
+.leaflet-control-layers-scrollbar {
+ overflow-y: scroll;
+ overflow-x: hidden;
+ padding-right: 5px;
+ }
+.leaflet-control-layers-selector {
+ margin-top: 2px;
+ position: relative;
+ top: 1px;
+ }
+.leaflet-control-layers label {
+ display: block;
+ }
+.leaflet-control-layers-separator {
+ height: 0;
+ border-top: 1px solid #ddd;
+ margin: 5px -10px 5px -6px;
+ }
+
+/* Default icon URLs */
+.leaflet-default-icon-path {
+ background-image: url(images/marker-icon.png);
+ }
+
+
+/* attribution and scale controls */
+
+.leaflet-container .leaflet-control-attribution {
+ background: #fff;
+ background: rgba(255, 255, 255, 0.7);
+ margin: 0;
+ }
+.leaflet-control-attribution,
+.leaflet-control-scale-line {
+ padding: 0 5px;
+ color: #333;
+ }
+.leaflet-control-attribution a {
+ text-decoration: none;
+ }
+.leaflet-control-attribution a:hover {
+ text-decoration: underline;
+ }
+.leaflet-container .leaflet-control-attribution,
+.leaflet-container .leaflet-control-scale {
+ font-size: 11px;
+ }
+.leaflet-left .leaflet-control-scale {
+ margin-left: 5px;
+ }
+.leaflet-bottom .leaflet-control-scale {
+ margin-bottom: 5px;
+ }
+.leaflet-control-scale-line {
+ border: 2px solid #777;
+ border-top: none;
+ line-height: 1.1;
+ padding: 2px 5px 1px;
+ font-size: 11px;
+ white-space: nowrap;
+ overflow: hidden;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+
+ background: #fff;
+ background: rgba(255, 255, 255, 0.5);
+ }
+.leaflet-control-scale-line:not(:first-child) {
+ border-top: 2px solid #777;
+ border-bottom: none;
+ margin-top: -2px;
+ }
+.leaflet-control-scale-line:not(:first-child):not(:last-child) {
+ border-bottom: 2px solid #777;
+ }
+
+.leaflet-touch .leaflet-control-attribution,
+.leaflet-touch .leaflet-control-layers,
+.leaflet-touch .leaflet-bar {
+ box-shadow: none;
+ }
+.leaflet-touch .leaflet-control-layers,
+.leaflet-touch .leaflet-bar {
+ border: 2px solid rgba(0,0,0,0.2);
+ background-clip: padding-box;
+ }
+
+
+/* popup */
+
+.leaflet-popup {
+ position: absolute;
+ text-align: center;
+ margin-bottom: 20px;
+ }
+.leaflet-popup-content-wrapper {
+ padding: 1px;
+ text-align: left;
+ border-radius: 12px;
+ }
+.leaflet-popup-content {
+ margin: 13px 19px;
+ line-height: 1.4;
+ }
+.leaflet-popup-content p {
+ margin: 18px 0;
+ }
+.leaflet-popup-tip-container {
+ width: 40px;
+ height: 20px;
+ position: absolute;
+ left: 50%;
+ margin-left: -20px;
+ overflow: hidden;
+ pointer-events: none;
+ }
+.leaflet-popup-tip {
+ width: 17px;
+ height: 17px;
+ padding: 1px;
+
+ margin: -10px auto 0;
+
+ -webkit-transform: rotate(45deg);
+ -moz-transform: rotate(45deg);
+ -ms-transform: rotate(45deg);
+ transform: rotate(45deg);
+ }
+.leaflet-popup-content-wrapper,
+.leaflet-popup-tip {
+ background: white;
+ color: #333;
+ box-shadow: 0 3px 14px rgba(0,0,0,0.4);
+ }
+.leaflet-container a.leaflet-popup-close-button {
+ position: absolute;
+ top: 0;
+ right: 0;
+ padding: 4px 4px 0 0;
+ border: none;
+ text-align: center;
+ width: 18px;
+ height: 14px;
+ font: 16px/14px Tahoma, Verdana, sans-serif;
+ color: #c3c3c3;
+ text-decoration: none;
+ font-weight: bold;
+ background: transparent;
+ }
+.leaflet-container a.leaflet-popup-close-button:hover {
+ color: #999;
+ }
+.leaflet-popup-scrolled {
+ overflow: auto;
+ border-bottom: 1px solid #ddd;
+ border-top: 1px solid #ddd;
+ }
+
+.leaflet-oldie .leaflet-popup-content-wrapper {
+ zoom: 1;
+ }
+.leaflet-oldie .leaflet-popup-tip {
+ width: 24px;
+ margin: 0 auto;
+
+ -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)";
+ filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678);
+ }
+.leaflet-oldie .leaflet-popup-tip-container {
+ margin-top: -1px;
+ }
+
+.leaflet-oldie .leaflet-control-zoom,
+.leaflet-oldie .leaflet-control-layers,
+.leaflet-oldie .leaflet-popup-content-wrapper,
+.leaflet-oldie .leaflet-popup-tip {
+ border: 1px solid #999;
+ }
+
+
+/* div icon */
+
+.leaflet-div-icon {
+ background: #fff;
+ border: 1px solid #666;
+ }
+
+
+/* Tooltip */
+/* Base styles for the element that has a tooltip */
+.leaflet-tooltip {
+ position: absolute;
+ padding: 6px;
+ background-color: #fff;
+ border: 1px solid #fff;
+ border-radius: 3px;
+ color: #222;
+ white-space: nowrap;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ pointer-events: none;
+ box-shadow: 0 1px 3px rgba(0,0,0,0.4);
+ }
+.leaflet-tooltip.leaflet-clickable {
+ cursor: pointer;
+ pointer-events: auto;
+ }
+.leaflet-tooltip-top:before,
+.leaflet-tooltip-bottom:before,
+.leaflet-tooltip-left:before,
+.leaflet-tooltip-right:before {
+ position: absolute;
+ pointer-events: none;
+ border: 6px solid transparent;
+ background: transparent;
+ content: "";
+ }
+
+/* Directions */
+
+.leaflet-tooltip-bottom {
+ margin-top: 6px;
+}
+.leaflet-tooltip-top {
+ margin-top: -6px;
+}
+.leaflet-tooltip-bottom:before,
+.leaflet-tooltip-top:before {
+ left: 50%;
+ margin-left: -6px;
+ }
+.leaflet-tooltip-top:before {
+ bottom: 0;
+ margin-bottom: -12px;
+ border-top-color: #fff;
+ }
+.leaflet-tooltip-bottom:before {
+ top: 0;
+ margin-top: -12px;
+ margin-left: -6px;
+ border-bottom-color: #fff;
+ }
+.leaflet-tooltip-left {
+ margin-left: -6px;
+}
+.leaflet-tooltip-right {
+ margin-left: 6px;
+}
+.leaflet-tooltip-left:before,
+.leaflet-tooltip-right:before {
+ top: 50%;
+ margin-top: -6px;
+ }
+.leaflet-tooltip-left:before {
+ right: 0;
+ margin-right: -12px;
+ border-left-color: #fff;
+ }
+.leaflet-tooltip-right:before {
+ left: 0;
+ margin-left: -12px;
+ border-right-color: #fff;
+ }
diff --git a/src/pretix/static/pretixcontrol/js/ui/geo.js b/src/pretix/static/pretixcontrol/js/ui/geo.js
new file mode 100644
index 0000000000..49f9d31d45
--- /dev/null
+++ b/src/pretix/static/pretixcontrol/js/ui/geo.js
@@ -0,0 +1,122 @@
+/*globals $*/
+
+$(function () {
+ var j = 0;
+ $(".geodata-section").each(function () {
+ // Geocoding
+ var $sec = $(this);
+ var $inp = $(this).find("textarea[lang=en], input[lang=en]").first();
+ if ($inp.length === 0) {
+ $inp = $(this).find("textarea, input").first();
+ }
+ var timer, timer2;
+ var touched = $sec.find("input[name$=geo_lat]").val() !== "";
+
+ function load() {
+ window.clearTimeout(timer);
+ var q = $.trim($inp.val().replace(/\n/g, ", "));
+ if ((touched && $sec.find("input[name$=geo_lat]").val() !== "") || $.trim(q) === "") {
+ return;
+ }
+ $sec.find(".col-md-1").html("");
+ $.getJSON('/control/geocode/?q=' + escape(q), function (res) {
+ var q2 = $.trim($inp.val().replace(/\n/g, ", "));
+ if (q2 !== q) {
+ return; // lost race
+ }
+ if (res.results) {
+ $sec.find("input[name$=geo_lat]").val(res.results[0].lat);
+ $sec.find("input[name$=geo_lon]").val(res.results[0].lon);
+ center(13);
+ }
+ $sec.find(".col-md-1").html("");
+ })
+ }
+
+ $sec.find("input[name$=geo_lat], input[name$=geo_lon]").change(function () {
+ touched = $sec.find("input[name$=geo_lat]").val() !== "";
+ center(13);
+ }).keyup(function () {
+ if (timer2) {
+ window.clearTimeout(timer2);
+ }
+ timer2 = window.setTimeout(center, 300);
+ });
+
+ $inp.change(load);
+ $inp.keyup(function () {
+ if (timer) {
+ window.clearTimeout(timer);
+ }
+ timer = window.setTimeout(load, 1000);
+ });
+
+ // Map
+ var $grp = $sec.find(".geodata-group");
+ var tiles = $grp.attr("data-tiles");
+ var attrib = $grp.attr("data-attrib");
+ if (tiles) {
+ var $map = $("");
+ $grp.append($("
").addClass("col-md-9 col-md-offset-3").append($map));
+ var map = L.map($map.get(0));
+ L.tileLayer(tiles, {
+ attribution: attrib,
+ maxZoom: 18,
+ }).addTo(map);
+ var $lat = $sec.find("input[name$=geo_lat]");
+ var $lon = $sec.find("input[name$=geo_lon]");
+
+ function getpoint() {
+ if ($lat.val() !== "" && $lon.val() !== "") {
+ return [parseFloat($lat.val().replace(",", ".")), parseFloat($lon.val().replace(",", "."))];
+ } else {
+ return [0.0, 0.0];
+ }
+ }
+
+ var marker = L.marker(getpoint(), {
+ draggable: 'true',
+ icon: L.icon({
+ iconUrl: $grp.attr("data-icon"),
+ shadowUrl: $grp.attr("data-shadow"),
+ iconSize: [25, 41],
+ iconAnchor: [12, 41],
+ popupAnchor: [1, -34],
+ tooltipAnchor: [16, -28],
+ shadowSize: [41, 41]
+ })
+ });
+ marker.addTo(map);
+ marker.on("dragend", function (event) {
+ var position = marker.getLatLng();
+ marker.setLatLng(position, {
+ draggable: 'true'
+ }).bindPopup(position).update();
+ $lat.val(position.lat.toFixed(7));
+ $lon.val(position.lng.toFixed(7));
+ center(null);
+ });
+
+ function center(zoom) {
+ if ($lat.val() !== "" && $lon.val() !== "") {
+ if (zoom) {
+ map.setView(getpoint(), zoom);
+ } else {
+ map.panTo(getpoint());
+ }
+ marker.setLatLng(getpoint(), {
+ draggable: 'true'
+ }).bindPopup(getpoint()).update();
+ } else {
+ map.fitWorld();
+ }
+ }
+
+ center(13);
+ } else {
+ function center(zoom) {
+ }
+ }
+
+ });
+});
diff --git a/src/pretix/static/pretixcontrol/scss/_forms.scss b/src/pretix/static/pretixcontrol/scss/_forms.scss
index debbf666da..06351ebe75 100644
--- a/src/pretix/static/pretixcontrol/scss/_forms.scss
+++ b/src/pretix/static/pretixcontrol/scss/_forms.scss
@@ -224,6 +224,23 @@ div.mail-preview {
input[type=number].short {
width: 80px !important;
}
+.geodata-group {
+ .form-group {
+ margin: 0 0 10px 0;
+ }
+ .attrib {
+ padding: 0;
+ margin: 0;
+ font-size: $font-size-small;
+ a {
+ color: $text-muted;
+ }
+ }
+ .leaflet-container {
+ height: 200px;
+ margin: 0;
+ }
+}
.metadata-group {
.form-group {
margin: 0 0 10px 0;
diff --git a/src/pretix/static/pretixcontrol/scss/main.scss b/src/pretix/static/pretixcontrol/scss/main.scss
index e7488f19e3..368e79d019 100644
--- a/src/pretix/static/pretixcontrol/scss/main.scss
+++ b/src/pretix/static/pretixcontrol/scss/main.scss
@@ -12,6 +12,7 @@
@import "_dashboard.scss";
@import "../../pretixbase/scss/webfont.scss";
@import "../../fileupload/jquery.fileupload.scss";
+@import "../../leaflet/leaflet.scss";
@import "../../select2/select2.scss";
@import "../../select2/select2_bootstrap.scss";
@import "../../colorpicker/bootstrap-colorpicker.scss";
diff --git a/src/tests/api/test_events.py b/src/tests/api/test_events.py
index 07e02cf352..7362b2fdf4 100644
--- a/src/tests/api/test_events.py
+++ b/src/tests/api/test_events.py
@@ -69,6 +69,8 @@ TEST_EVENT_RES = {
"presale_start": None,
"presale_end": None,
"location": None,
+ "geo_lat": None,
+ "geo_lon": None,
"slug": "dummy",
"has_subevents": False,
"seating_plan": None,
diff --git a/src/tests/api/test_subevents.py b/src/tests/api/test_subevents.py
index aecef4f1e7..101e2e49a1 100644
--- a/src/tests/api/test_subevents.py
+++ b/src/tests/api/test_subevents.py
@@ -79,6 +79,8 @@ TEST_SUBEVENT_RES = {
'id': 1,
'variation_price_overrides': [],
'location': None,
+ "geo_lat": None,
+ "geo_lon": None,
'is_public': True,
'item_price_overrides': [],
'meta_data': {'type': 'Workshop'}